はじめに
先日、PhantomJS でログインが必要なページでも自由自在にスクレイピング - 凹みTips という記事を書きました。
前回は PhantomJS のみを用いてスクレイピングを行なっていましたが、スクレイピングした結果を使って色々やりたい!となると、Node.js の力を借りたくなってきます。単純に取得した HTML を利用するだけなら、PhantomJS の fs モジュールを使って HTML をファイルに書き出し、これを Node.js で読み取って…、なんてことをすれば可能ですが、何かしらの入力を受けて動的にページを遷移したい、となると厳しくなってきます。
そこで、本エントリでは PhantomJS を Node.js から使って色々出来るよ!ということを解説したいと思います。
利用するモジュール
PhantomJS をラップした Node モジュールは沢山あります。試しに、
$ npm search phantomjs
すると、大量に出てくると思います。色々試してみましたが、「phantomjs-node」というモジュールが上手く動いたので今回はコレを使ってみます。またパースには cheerio を使用しました。
$ npm install phantom cheerio
コード
例として、はてなにログインし、自分のユーザ名とはてなポイントの残りを取得してみます。基本的な流れは前回と同じです。
コード
var phantom = require('phantom'); var cheerio = require('cheerio'); phantom.create(function(ph) { ph.createPage(function(page) { // ページが読み込まれたら page.onCallback を呼ぶ page.set('onInitialized', function() { page.evaluate(function() { document.addEventListener('DOMContentLoaded', function() { window.callPhantom('DOMContentLoaded'); }, false); }); }); // ページが読み込まれたら登録した関数の配列を順次実行してくれるクラス var funcs = function(funcs) { this.funcs = funcs; this.init(); }; funcs.prototype = { // ページが読み込まれたら next() を呼ぶ init: function() { var self = this; page.set('onCallback', function(data) { if (data === 'DOMContentLoaded') self.next(); }); }, // 登録した関数の配列から1個取り出して実行 next: function() { var func = this.funcs.shift(); if (func !== undefined) { func(); } else { page.set('onCallback', undefined); } } }; // 順次実行する関数 new funcs([ function() { console.log('ログイン処理'); page.open('https://www.hatena.ne.jp/login'); // 次ページヘ }, function() { console.log('ログイン画面'); page.evaluate(function() { document.getElementById('login-name').value = 'はてなの ID'; document.querySelector('.password').value = 'パスワード'; document.querySelector('form').submit(); // 次ページヘ }); }, function() { console.log('ログイン後画面'); setTimeout(function() { page.open('http://www.hatena.ne.jp/my'); }, 2000); }, function() { console.log('iframe 内'); // iframe 内の HTML を取得 page.evaluate(function() { return document.getElementsByTagName('html')[0].innerHTML; }, function(html) { // cheerio でパースしてユーザ名とポイントを取得 var $ = cheerio.load(html); var point = $('.hatena-module').eq(0).find('.count').text(); console.log('ポイントは後', point, 'point だよ!'); // お忘れなきよう (-人-) ph.exit(); }); } ]).next(); }); });
結果
純粋な PhantomJS と違って evaluate した結果はコールバックで受け取ったり、page のプロパティには set/get でアクセスするといった差異があるのでご注意を。
あと純粋な PhantomJS の時と違ってログイン中画面の遷移が上手く行かなかったので泣く泣く setTimeout で待機する処理使ってます( ;∀;)。
ちなみに phantom-sync なんていう phantom をラップしたモジュールもあるので、こちらを用いたコードは以下の gist にアップしました。