CasperJSのwaitForやwaitは、thenで連結してるなら要らないケースが多いという話
2018-09-07
CasperJSを使う際に「waitちゃんとかけとかないと、ローディング中にデータ取りに行く可能性がある」と思い込んでいたのですが、どうやらそんなことは無さそうだという話です。
ネット上にあるサンプルコードには、私のコードも含めwaitやwaitForが多用されています。「公開する以上は安全なものを…」という気持ちが働いているのだと思いますが、waitForならともかく、waitだと無駄に処理が重くなってしまい、実用に足らないレベルになってしまうことも多々あるのではないかと思います。
というわけで、私もCasperJSのwaitまわり、特にwaitForの挙動について疑問を感じていたので、色々実験を行ってみました。
結論から言うと、thenで連結してるなら、wait系が必要なのはonloadやready以外でjsの処理を走らせた場合のみなんじゃないかということが分かったので、共有したいと思います。
thenはonloadやreadyが終わるまで待ってくれる
言葉で説明しにくいので、サンプルコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var casper = require("casper").create({ // 表示サイズを一応設定 viewportSize: { width: 1920, height: 1080, } }); casper.start(); casper.open("http://www.hatena.ne.jp/"); casper.then(function() { var text = this.evaluate(function() { return document.querySelector('#s-question > ul > li:nth-child(6) > span > a > span').textContent; }); require('utils').dump(text); }); casper.run(); |
はてなのトップページにある、人力検索はてなの上から5番目にある質問のタイトルを取得して出力しています。はてなのトップページにアクセスしてみればわかりますが、人力検索はてなの質問一覧は後からjsで読み込まれています。ということで、このプログラムでは「タイミング次第でタイトルが取れない」のではないかと思っていたのですが、そんなことはありませんでした。
実行に時間がかかることはあるものの、必ずタイトルが取得出来ていました。これは、thenの挙動が「jsも含め、全てのローディングが完了してから実行」されるという仕様になっているからなのではないかと思います。ちゃんとドキュメント読めば書いてあるのかもしれませんが、英語なのでちゃんと読んでません。
ということで、何らかのクリックとかスクロールで後からjsによる処理を走らせない限り、thenの中に更にwaitをかける必要は無いようです。
waitForの使い方
この記事で書きたい内容はここまでで終わりなのですが、ネット上に公開されているwaitForを絡めたコードには、どうも公式ドキュメントと異なる記述になっているコードが多く(古いバージョン?)、「これ動かないだろう」と思って実行してみたら、やっぱり動かないということがあったので、正しいwaitForの使い方を書いておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
var casper = require("casper").create({ // 表示サイズを一応設定 viewportSize: { width: 1920, height: 1080, } }); casper.start(); casper.open("http://www.hatena.ne.jp/"); casper.then(function() { casper.waitFor(function check() { // ここに判定用の処理を書く return this.evaluate(function() { // evaluteで画面情報を取得 return document.querySelector('#s-question > ul > li:nth-child(6) > span > a > span').textContent; }); }, function then() { // 判定が通った際に実行したい処理を書く var text = this.evaluate(function() { return document.querySelector('#s-question > ul > li:nth-child(6) > span > a > span').textContent; }); require('utils').dump(text); }); }); casper.run(); |
前述したように、このコードのwaitForは多分無意味です。openの読み込み完了を待ってから処理が走るため、waitForによる待機は意味が無いということです。クリックやスクロールで後からjsの処理が走るページがパッと浮かばなかったので無意味なコードを書きましたが、スクロール→waitForで要素が表示されるまで待機→表示されたらクリック、みたいな用途であれば意味はあると思います。
また、bot対策に色々やっていそうなページでCasperJSを動かしたいと考えるのであれば、念のために各所にwaitやwaitForを入れておくことで、想定外の遅延ロード処理による妨害を回避できる可能性もあると思います。