[SpookyJS] 同一ページで複数ダウンロードする時は”eachThen”を使うと便利

Pocket
LINEで送る
GREE にシェア
LinkedIn にシェア

クローリングをやっていると色々なホームページがある事に気がつきます。
 

昔ながらのtableタグを連発しているサイトだったり、bootstrapを使ったdivネスト連発しているサイト、
属性をほとんど付与していないネイティブタグなサイト。
 

スクレイピング操作をしていると、どのサイトでも鉄板的にデータ取得できるという統一ルールなどない事がよくわかります。
 

そして最ももどかしいのが、同じサイトの別ページでルールが全く違う(おそらく全く別の制作者)場合にクローリングの手間が数倍かかる事です。
 

そして今回は、1ページ内にダウンロードボタンが沢山あるページで、データダウンロードを一気に行う方法です。
 

サンプルページとして、東京都教育員会の資料ダウンロード一覧ページのPDFデータを一括ダウンロードしてみたいと思います。
 

http://www.kyoiku.metro.tokyo.jp/consulting/procedure_and_form/format.html
 

ダメパターン

通常考えると、ダウンロードボタンを”querySelectorAll”で一括取得して、”for”で回しながらelement.click()を実行していけばいいと思うのですが、少数であれば、この処理でうまくいく事もあるかもしれませんが、僕が試した結果、これではどうもうまくダウンロードされないという事がわかりました。
 

 

このプログラムを実行しても、click()をしているにもかかわらずpdfファイルへのリクエストも実行されません。
 

casperjsとspookyjs(中身は同じです)では、こうした場合、thenやevaluateを一旦終了してもう一度実行する事でアクションが次に進むという特性があるので、”for”文で一気に.click()処理を行なっても空振りするだけという事がわかりました。

eachThenを使ったダウンロード

まずダウンロードする要素一覧を取得してから、それをeachThenで繰り返し処理を行う事で、複数のevaluateに切り分けたダウンロードを行う事ができ、全てのダウンロードが正常に行われるようになりました。
 

ポイントは、要素をユニーク文字列にする事ですが、これは以前に記事で書いた「ライブラリ「DOMコード」」をそのまま使う事で対応できます。
 

[JavaScript] ライブラリ「DOMコード」


 

 

 

 


 

見事に、PDFファイルが大量に一括でダウンロードすることができました。

ちょこっと解説

1. uniqueDomライブラリを使ってDOM位置を文字列化する。

まず、spooky実行時のoptionに、uniqueDomライブラリのjsファイルを読み込んで起きます。

 

これによりevaluete内でエレメントをユニーク文字列に変換できます。
 

“var string = $$uniqueDom.encode(element);”
 

xpathみたいなものだと考えてください。
 

この文字列をまたエレメントに戻す時は以下の処理です。
 

“var element = $$uniqueDom.decode(string);”
 

2. requestとfsを使ってデータダウンロード

 

URLにダイレクトアクセスでファイルダウンロードができる場合は、上記のソースでほぼダウンロードが可能になります。
 

パスワードなどの認証を求められる場合などは、これにさらに処理を加えないといけないのですが、今回はここまでにしておきます。
 

eachThenでダウンロード実行

evaluateの返り値は、配列を取得ことはできますが、element情報を取得する事ができないため、今回はuniqueDomという関数を使って文字列一覧を取得しましたが、それを今度は”eachThen”を使って配列全てを別々のthen処理で復元します。
 

コマンドを実行すると、elementのユニーク文字と、ダウンロードするURLをダウンプしておいたので、内容が確認できるかと思います。

かなり便利なRPA

ダウンロードを手作業で行なって仕事をしている人などは、こうしたスクリプトを作っておくと、毎日自動的にファイルをダウンロードしてくれる簡単ツールを作る事が可能になります。
 

RPA(Robotic Process Automation)として最近業務系で流行りのやり方らしいので、興味のある方はサンプルソースを元に独自ソースを作ってみてはいかがですか?

Leave a Reply

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です