初心者としてCasperJSで迷ったこと、困ったこと
クジラ飛行机さんの本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でのrequire('fs'):http://phantomjs.org/api/fs/
- nodeのrequire('fs'):http://nodejs.jp/nodejs.org_ja/api/fs.html
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