WEBページをクローリングしている時に、非同期で構成されているWEBページは、非同期処理でスクレイピングしなければいけませんね。当たり前です・・・
spookyjsを使ってスクレイピングしていた時に、非同期処理を判定するのが非常に難しく、これまでは、waitで任意秒数待ってから、値を取りに行っていたんですが、さすがにこれではまずいと思って、casperjsのドキュメントをあさっていたら、いい機能があったので、非同期対応することができました。
ちなみに、casperjsの参考にしたページは以下のリンクです。
http://docs.casperjs.org/en/latest/modules/casper.html#waitfor
そしてこの記事は、Spookyjsでプログラムを実行するため、Spookyjsとはなんぞや?という方は以下の記事を読んでから閲覧ください。
[Nodejs] クローリングシステム構築 #1.Spookyjsの環境設定
非同期ページのサンプル
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>ajax</title>
</head>
<body>
<h1>1秒毎に項目が増えます</h1>
<div id="items"></div>
</body>
</html>
<script>
var num = 1;
function addItem(){
if(num >= 20){clearTimeout(term);}
var root = document.getElementById("items");
var item = document.createElement("div");
item.textContent = num + " : " + (+new Date());
root.appendChild(item);
num++;
}
var term = setInterval(addItem,1000);
</script>
これまでのスクレイピング・スクリプト
var Spooky = require('spooky');
var spooky = new Spooky({
child: {
command : '/usr/local/bin/casperjs',
transport : 'stdio'
},
casper: {
logLevel : 'info',
verbose : true,
waitTimeout : 3000
}} , function(err){
spooky.start();
spooky.thenOpen("index.html");
spooky.then(function(){this.wait(10000, function(){});});
spooky.then(function(){
var data = this.evaluate(function(){
return document.getElementById("items").childNodes.length;
});
this.emit('message' , data);
});
spooky.run();
spooky.then(function(){this.exit();});
});
spooky.on('message', function (res){
console.log("{'message' : '"+res+"'}");
});
簡単に解説すると、"index.html"を表示してから、"wait"で10秒待ってから、ページ内のid="items"の内包する追加されたエレメント数を返すプログラムですが、1秒毎に1つずつ増える要素の10秒後なので、アイテム数は"10"が返ってきます。
$ node scraping.js
{'message' : '10'}
全部で20個まで表示するように設定していますが、もちろん、wait値を"20秒後"にセットすれば、全てのアイテムを読み込んだ状態になりますが、インターネットサービスでは、大体がサーバーからデータの読み込みをするため、間隔が一定という保証はありません。
そうした時に、読み込みの想定の最大時間をセットするという手段もいいかもしれませんが、最大時間をどのくらいにすればいいかという問題に直面します。
この辺が不明確なインターネットツールにおいて、読み込み完了を取得できてその後コールバック関数を埋め込める処理は必要不可欠なんですね。
対応完了後にスクリプト
var Spooky = require('spooky');
var spooky = new Spooky({
child: {
command : '/usr/local/bin/casperjs',
transport : 'stdio'
},
casper: {
logLevel : 'info',
verbose : true,
waitTimeout : 30000
}} , function(err){
spooky.start();
spooky.thenOpen("index.html");
spooky.waitFor(function check(){
return this.evaluate(function(){
return document.getElementById("items").childNodes.length >= 20;
});
} , function then(){
var res = this.evaluate(function(){
return document.getElementById("items").childNodes.length;
});
this.emit('message' , "success : "+res);
} , function timeout(){
this.emit('message' , "timeout");
});
spooky.run();
spooky.then(function(){this.exit();});
});
spooky.on('message', function (res){
console.log("{'message' : '"+res+"'}");
});
そして、このようにプログラムを変えてみました。
まず、waitを廃止して、waitForという新たな命令を取り入れました。
このwaitForは、check()という中で、サイト内の任意条件を監視して、返り値が"true"になった時に、そのあとのthen()関数が実行されるという遅延待機状態を維持できます。
注意点としては、spookyのoptionでセットした11行目の"waitTimeout : ***"の部分を超える時間になるとtimeoutエラーが実行されてしまいます。
この値を最大値にセットしてあげましょう。
※この処理の時は、timeoutを無視できる様な特別仕様があればいいんですが・・・
そして実行した結果は以下の通りです。
time node waitfor.js
{'message' : 'success : 20'}
real 0m20.747s
user 0m0.700s
sys 0m0.600s
今回はわかりやすい様に!"time"コマンドを先頭につけて実行時間を計測しています。
しっかりドキュメントは読まねば!
この処理でのメリットは、最大時間のwaitで無駄な待ち時間を消費しなくても、必要最低限で処理が完了するエコなシステムが構築できるでしょう。
膨大なクローリング量などになる場合は、とても有効な機能になることが容易に想像できますね。
そんな時の為に、ちゃんとドキュメントを読んで、効率のいいシステム構築をしなくては!!!
関連リンク
クローリングシステム構築
0 件のコメント:
コメントを投稿