【連載】CasperJS でスクレイピング (3) - 実践テクニック
今回は CasperJS を使い実際のサイトをスクレイピングしてみる。
北陸鉄道の時刻表が2014年4月の改正で路線時刻表の permalink がなくなって不便なため、今回はこれを取得してミラーサイトを作成することを考える。
Referer 制限や POST メソッドを通過する
まず路線別時刻表検索 (busline.php) から路線の一覧を取得する。単にこの URL へアクセスするだけでは次のエラーが出てしまう。
原因はサーバ側のプログラムが POST メソッドしか受け付けないため。このような場合は何も考えず、人が操作したのと同じことをスクリプトで指示してやればよい。リファラ制限がかけられている場合も同じ方法でよい。よほど大規模なサイトで、厳しいチェックがかけられている場合を除き、この方法で通過できるはずだ。
人が操作する場合は時刻表メニュー (menu.php) において「路線別時刻検索 (busline.php)」をクリックするので、 click() メソッドを使って次のように書ける。
casper.start("http://arj.hokutetsu.co.jp/timetable/menu.php"); casper.then(function() { this.click("a[href$='busline.php']"); });
引数として CSS セレクタを使用する。今回の場合は a 要素の、 href 属性が “busline.php” で終わるものを選びたいのでこのような書き方になる。
非同期読み込みを待つ
時刻表検索を使っていて気付くのが、ページを読み込んだあとにくるくる回る「ローダ」が表示されること。これが消えてからでないと操作ができないようだ(確認はしていない)。
この場合は waitFor メソッドを使い、ローダが消えたかどうかを判断してから処理を実行するようにする。ブラウザの開発者ツールで確認したところ、読み込み完了時に div#loader にスタイル display: none が設定されていたのでここで判断した。
casper.waitFor(isLoaderHidden, function() { // 処理... }); function isLoaderHidden() { return this.evaluate(function() { return document.querySelector("#loader").style.display == "none"; }); }
ここで登場する evaluate メソッドは、Web ページ内の変数やメソッドにアクセスし、値を取得するためのものである。引数として渡した関数内では Web ページで使う JavaScript と同じスコープを持っており、値の書き換えも行える。
Web ページから値を得る
ここまで来ればあとは値を抽出してくるだけ。 evaluate() を使い、あとは DOM 操作を駆使してやればよい。
var rosenList = this.evaluate(function() { var options = document.querySelectorAll("#ros_list > option"); return Array.prototype.map.call(options, function(e) { return { name: e.innerText }; }); });
querySelectorAll() の返す値は単なる配列ではなく NodeList という型なので Array.prototype.〜.call() という形をよく使う。 map 以外にも forEach, slice などが使える。
WebKit にはもともと Internet Explorer の独自拡張だった innerText プロパティが実装されているため、要素内のテキストの抽出が簡単にできて便利だ。textContent と違い、見えていない文字は返さないので、ほぼ見た通りのテキストが得られるのが利点だ。
(次回へ続く)
- 第1回 CasperJS の基本
- 第2回 役に立つオプション
- 第3回 実践テクニック