LoginSignup
23
18

More than 5 years have passed since last update.

SpookyJSでJavaScript有効状態のページをスクレイピング

Last updated at Posted at 2016-05-31

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点かと思いました。

  1. spooky.then内はCasperJSが作っている独自のJavaScript環境なので外部の変数を参照できないし、Node.js特有の機能は動作しない
  2. 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

23
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
18