SpookyJSは、PhantomJSというヘッドレスブラウザを使って色々と便利なことをするラッパーであるCasperJSを、Node.jsスクリプトから操作するためのNodeモジュールです (ちょっとややこしい)。
CasperJSはシングルページアプリの自動テストや、JSでレンダリングされるページのスクレイピングをするのに便利ですが、Node.js特有の機能を有するモジュールは動作しないようです。
SpookyJSからCasperJSを使うことで、Cheerio等の外部のNodeモジュールと連携できますし、また逆にCasperJSによってスクレイピングした結果をexportし、他のNodeスクリプトからrequireすることもできたりと、何かと便利そうなので今回試してみることにしました。
インストール
SpookyJSを使うにはPhantomJSとCasperJSが必要です。全てnpmでインストールしてもいいですが、僕が使っているArch Linuxの公式リポジトリにPhantomJSが置いてあったので、なんとなくPhantomはArchのパッケージマネージャでインストールし、他はnpmでローカルに入れてみました。
$ sudo pacman -S phantomjs
$ cd (プロジェクトルート)
$ npm i casperjs spooky
あと何故かnpmでphantomjsをインストールしている環境だとtiny-jsonrpc
が無いと怒られたので、その場合は $ npm i tiny-jsonrpc
してください。
サンプル
Qiitaのトップページのタイトルを取得するサンプルです。これが最小構成だと思います。
var Spooky = require('spooky');
var options = {
child: {transport: 'http'},
};
var spooky = new Spooky(options, function(err) {
spooky.start('https://qiita.com/');
spooky.then(function() {
this.emit('p', this.getTitle());
});
spooky.run();
});
spooky.on('p', function(msg) {
console.log(msg);
});
ポイントは下記の2点かと思いました。
-
spooky.then
内はCasperJSが作っている独自のJavaScript環境なので外部の変数を参照できないし、Node.js特有の機能は動作しない -
spooky.then
内で得たデータはthis.emit
でイベントをトリガーして外部に渡す。
試しにspooky.then
の部分を以下のように書き換えてみます。
var hello = 'hello?';
spooky.then(function() {
// this.emit('p', this.getTitle());
this.emit('p', hello);
});
するとReferenceError: Can't find variable: hello
と怒られます (エラー出力するにはそのためのイベントリスナーが必要)。
一応spooky.then
内でconsole.log
したものをコンソールに表示させる方法もありますが、読み込んだサイト内のスクリプトによってconsole.log
で出力されたものまで表示してしまうため、スクレイピングが目的であればイベントリスナーで取る方がよいかと思います。
Ajaxで表示しているコンテンツの取得
私事ですが、現在転職活動中ということで、お世話になっているGreenという転職サイトのトップページに表示される掲載求人数を取得してみます。求人数はAjaxで読み込んでいるのでJSオフだと表示されませんがSpooky (CasperJS) なら問題ありません。
var Spooky = require('spooky');
var options = {
child: {transport: 'http'},
};
var spooky = new Spooky(options, function (err) {
spooky.start('https://www.green-japan.com/');
spooky.then(function() {
var client_count = this.evaluate(function() {
return document.querySelector('#job_count').innerText;
});
this.emit('p', client_count);
});
spooky.run();
});
spooky.on('p', function (msg) {
console.log(msg);
});
evaluate
はCasperJS固有のAPIで、ブラウザでページを開いてデベロッパーツール上でJSをインスタント実行していることと同等になるようです。
Nodeモジュールの利用
良い例が思い浮かばないのでかなり強引ですが、例えば特定の要素の取得をquerySelector
では無く、Cheerio等の外部のパーサーで行うとします。
その場合は下記のようにthis.emit
でCasperJS領域の外側に飛ばしてパースできます。
var Spooky = require('spooky');
var cheerio = require('cheerio');
var options = {
child: {transport: 'http'},
};
var spooky = new Spooky(options, function (err) {
spooky.start('http://qiita.com/');
spooky.then(function() {
this.emit('body', this.getHTML());
});
spooky.run();
});
spooky.on('body', function (body) {
var $ = cheerio.load(body);
console.log($('title').text());
});
getHTML
はページのHTML全体を返すCasperJSのAPIです。他にも特定のDOM要素をクリックさせるclick
や、ページ全体のキャプチャをpngで出力するcapture
等、CasperJSには便利なAPIがたくさんあります。詳しくは下記のドキュメントページをご参照ください。
以上、ほぼCasperJSの話になってしまいましたが、Node.jsからCasperJSを簡単に扱う場合のサンプルとしてここに記録しておきます。
参考ページ
API Documentation — CasperJS 1.1.0-DEV documentation
http://docs.casperjs.org/en/latest/modules/
SpookyJS/SpookyJS: Drive CasperJS from Node.js
https://github.com/SpookyJS/SpookyJS