読者です 読者をやめる 読者になる 読者になる

Itsukaraの日記

最新IT技術を勉強・実践中。最近はDeep Learningに注力。

初心者としてCasperJSで迷ったこと、困ったこと

ITのお勉強 スクレイピング

クジラ飛行机さんの本JS+Node.jsによるWebクローラー/ネットエージェント開発テクニックを参考に、CasperJSでプログラムを試作してます。これに際し、迷ったこと、困ったことをメモしました。

CasperJSのfsはnodeのfsと異なる

CasperJSのプログラムで、htmlのファイル出力のため、次のように書いたのですが、「fs.writeFile("test.txt", html);」でファイルに何も出力されません。また、特にエラーも出ません。

var casper = require("casper").create();
casper.start("https://en.wikipedia.org/wiki/Test", function() {
  var html = casper.getHTML();
  casper.echo(html)

  var fs = require("fs");
  fs.writeFile("test.txt", html);
});
casper.run();

いろいろ調べたところ、CasperJSのrequire("fs")は、nodeのrequire("fs")とは別のものであることが分かりました。これが原因でした。

CasperJSでは、writeFileでなくwriteとなり、下記が正しい書き方でした。

var casper = require("casper").create();
casper.start("https://en.wikipedia.org/wiki/Test", function() {
  var html = casper.getHTML();
  casper.echo(html)

  var fs = require("fs");
  fs.write("test.txt", html);
});
casper.run();

それにしても、エラーメッセージも何も出ないので、とても困りました。

evaluateする関数内でのsetTimeoutの使用は要注意

元々、chromeでDeveloper toolsのConsoleから実行していたスクリプトを、CasperJSのevaluateを使ってPhantomJSで実行しようと試行していました。具体的には次のような感じです。

casper.then(function() {
  casper.evaluate(function() {
    // Developer toolsのConsoleから実行していたスクリプト
  });
});

しかし、Consoleから実行していたスクリプトが、途中までしか実行されていないように見えます。何が原因か、いろいろ悩んだのですが、スクリプト内で、setTimeoutを使っていることが原因であることが分かりました。

スクリプトの内容は、次のような形式になっていました。考えてみれば当たり前なのですが、setTimeoutを実行した直後に、f1の処理は完了し、evaluateも完了します。そのため、setTimeoutでのf1の遅延実行の完了を待たずに処理が完了します。

var i = 0;
f1();

function f1() {
  // 処理1
  if (++i < 6) {
    setTimeout(f1, 1000);
  }
}

本当は、PhantomJS側で、一定時間間隔でf1を実行し、結果をファイルに出力したかったのですが...

PhantomJS側の動きをもう少し調べたところ、setTimeoutで設定した関数の実行自体は行われているようです。具体的には、次のようなプログラムをCasperJSで実行してみました。

var casper = require("casper").create();

casper.on('remote.message', function(msg) {
  this.echo("REMOTE-MESSAGE: " + msg);
});

casper.start("https://en.wikipedia.org/wiki/Test", function() {
  casper.echo("POINT 1");
  casper.evaluate(function() {
    var i = 0;
    f1();
    
    function f1() {
      console.log("f1(): i = " + i);
      if (++i < 6) {
        setTimeout(f1, 1000);
      } else {
        console.log("f1(): FINISHED");
      }
    }
  });
  casper.echo("POINT 2");
  casper.wait(3000);
  casper.echo("POINT 3");
});

casper.then(function() {
  casper.echo("POINT 4");
  casper.wait(6000);
  casper.echo("POINT 5");
});

casper.run();

上記の出力結果は以下のようになりました。"POINT 3"、"POINT 5"は、casper.waitでの待ち時間が完了する前に出力されているようです。casper.waitでは、次のステップまでのwaitはしますが、ステップ内での次の分までのwaitはしないようです。

POINT 1
REMOTE-MESSAGE: f1(): i = 0
POINT 2
POINT 3
REMOTE-MESSAGE: f1(): i = 1
REMOTE-MESSAGE: f1(): i = 2
REMOTE-MESSAGE: f1(): i = 3
POINT 4
POINT 5
REMOTE-MESSAGE: f1(): i = 4
REMOTE-MESSAGE: f1(): i = 5
REMOTE-MESSAGE: f1(): FINISHED