CasperJSで動的なjsページを操作しキャプチャを撮る

CasperJSで動的なjsページを操作してキャプチャを撮りました。

以前CasperJSでクロールしてキャプチャ撮る記事をあげました。
pppurple.hatenablog.com

普通のクロールではjsで動的にページが生成されるようなページは
クロールできません。

CasperJSを使うとページを操作することができるので、そういうページも
ダウンロードしたりキャプチャを撮ることが出来ます。

クリック操作

例えばこんなjsパーツがあったとします。
これはクリックすると、一覧が表示されます。
f:id:pppurple:20160222182440j:plain:w500

クリック時
f:id:pppurple:20160222182954j:plain:w500

これをCasperJSで操作する場合、click()で対象のcssセレクタを指定します。

casper.start('http://xxxxxxxxxxxx/');

casper.then(function() {
    this.click('#search_area > a');
});
evaluateで処理

クリックした後の下記の各エリアをすべて取得する場合、evaluate()で取得します。

f:id:pppurple:20160222182954j:plain:w500

こんな感じでdocument.querySelectorAll()で各エリアを取得するgetAreaList()を用意し、
evaluate()で呼び出します。

var getAreaList = function () {
  var ids = [];
  var arealist = document.querySelectorAll('#area > div > ul > li > a');
  for (var i = 0; i < area.length; i++) {
    var id = area[i].id;
    ids.push(id);
  }
  return ids;
};
var arealist = this.evaluate(getAreaList);

evaluate()は対象ページ内で指定したfunctionを実行するようなイメージです。
f:id:pppurple:20160222211351j:plain:w500

繰り返し処理

上記で取得したエリアリストをそれぞれ処理したい場合は、each()を使用します。

casper.each(arealist, function(self, id){
  :
  :
});
描画待機

例えばエリアで北海道を選択し、探すボタンを押すと、
f:id:pppurple:20160222214051j:plain:w500

下記のような待機画面が表示され、数秒後にjsで動的に検索結果が表示されます。
f:id:pppurple:20160222214108j:plain:w500

そのため、すぐに検索結果をオープンしてキャプチャを撮ると、
上記のような待機画面のキャプチャが撮れてしまいます。

そういう場合はwait()でjsによる画面描画を待機します。

var url = 'http://xxxxxxxxxxxxxxxxxxxxx/';
self.thenOpen(url, function(){
  self.wait(5000, function() {
       :
       :
  });
});
画面スクロール

画面描画を待機すると下記のような検索結果が表示されます。
が、検索結果が14件となっていますが、実際は10件しか表示されません。
f:id:pppurple:20160222220140j:plain:w500

これは、検索結果が初期表示では10件までしか表示されず、
ページを下までスクロールするとjsで残りが表示されるようになっているためです。
twitterで下までスクロールすると過去のツイートが表示されるような感じです。

そんな場合はscrollToBottom()を使用すると画面下までスクロールしてくれます。
スクロールした場合は新しいデータをjsで読み込んでいるので少し待機する必要があります。
なのでスクロールした後少し待機するようにwait()と組み合わせます。

this.scrollToBottom();
self.wait(5000, function() {
   :
   :
});
ページ取得と画面キャプチャ

その後、ページ取得と画面キャプチャを撮ります。
画面キャプチャは普通にcapture()で取得します。

this.capture(id + '.png');

ページの取得はdownload()で取得できるのですが、
download()ではurlを指定して取得するので、urlの初期表示のhtmlが取得され、
jsで動的に描画されたページのHTMLが取得できません。

// これだと動的に読み込んだページは取得できない
this.download(url, id + '.html');

なので、jsで動的に描画している状態のhtmlを取得するgetHTML()を使用します。

fs.write(id + '.html', this.getHTML(), 'w');
取得スクリプト

これらを組み合わせて、jsで動的に生成される某ページのhtmlと画面キャプチャを取得しました。
ソースはこんな感じ。

crawl_dynamic_html.js

var casper = require('casper').create();
var fs = require('fs');

casper.start('http://xxxxxxxxxxxxxxxxxx/');

casper.then(function() {
  this.click('#search_area > a');
});

casper.wait(1000, function() {
  var getAreaList = function () {
    var ids = [];
    var arealist = document.querySelectorAll('#area > div > ul > li > a');
    for (var i = 0; i < area.length; i++) {
      var id = area[i].id;
      ids.push(id);
    }
    return ids;
  };
  var arealist = this.evaluate(getAreaList);

  casper.each(arealist, function(self, id){
    var url = 'http://xxxxxxxxxxxxxxxxxx/search/index.html#' + id;
    self.thenOpen(url, function(){
      self.wait(5000, function() {
        this.scrollToBottom();
        self.wait(5000, function() {
          this.capture(id + '.png');
          fs.write(id + '.html', this.getHTML(), 'w');
        });
      });
    });
  });
});

casper.run();


一応GitHubにソースあげました。
github.com

【参考】

JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック