PhantomJSを使って色々試してみる

#PhantomJSとは

HP: [PhantomJS](http://phantomjs.org/)
参考までに: [GoogleとAppleが争う「WebKit」ってどうして重要？](http://ascii.jp/elem/000/000/778/778742/)

一言で言うと、WebkitベースのHeadlessブラウザ。Webkitとは主にWebブラウザーで用いられているレンダリングエンジンのこと。PhantomJSはwebkitにbuilt-inされているJavaScriptCore (safariとかもこれを使っている) というjavascriptエンジンを採用している。スクレイピングやスクリーンキャプチャなどにも活用可能。

#PhantomJSに出会うまでの経緯
Python3.4でスクレイピングを試していたらJavaScriptのレンダリングがかかっているサイトにぶつかった。[PythonでさくっとWebスクレイピングする (JavaScript読み込みにも対応しつつ)](http://qiita.com/beatinaniwa/items/72b777e23ef2390e13f8#comment-8ec96aa8e93ceb9cf3ea)というサイトを参考に辿って行くことに。。

#Installation

ということで実際にインストール開始。
`$ brew install phantomjs`

#Experiment #0

ネット上に公開されている[Quick Start](http://phantomjs.org/quick-start.html)を参考に色々試してみることにする。基本的にはJavascript言語を使っていく。

#Experiment #1 Hello, World!
terminal上でプロジェクトを行うディレクトリへ移動（もしくはmkdir ファイル名）。プロジェクトディレクトリ内で`touch hello.js`でファイルを作成し`open hello.js`で肉付け開始。

```js:hello.js
console.log('Hello, World!');
phantom.exit();
```
terminal上で`phantomjs hello.js`を実行すると`Hello, World!`と返ってくる。最初のラインである`console.log`という命令により`Hello, world!`というストリングをターミナルに投げ込む。投げ込んだ情報を`phantom.exit()`というコマンドによって実際に実行される。ちなみに`phantom.exit()`コマンドを書かないとコードが処理されない。

# Experiment #2 Page Loading
PhantomJSを用いて任意のウェブページをオブジェクトを作成し、そのウェブページの読み込み、解析、生成を行うことが可能。

```js:page_loading.js
//headlessブラウザを作る
var page = require('webpage').create();

//指定したURLを開く
page.open('https://google.com', function(status) {
  console.log("Status: " + status);
  if(status === "success") {
  	//screen capture
    page.render('google.png');
  }
  phantom.exit();
});
```
成功すると`Status: success`と表示され表示され、作業ディレクトリ内にgoogleのスクリーンキャプチャがあるはず。
他にもページを読み込む速度などを測定できたりするらしい。例えば`http://www.google.com`へのページの読み込み速度を知りたい場合、

```js:loadspeed.js
var page = require('webpage').create(),
  system = require('system'),
  t, address;

//<>内に指定のURLを入れる
if (system.args.length === 1) {
  console.log('Usage: loadspeed.js <http://www.google.com>');
  phantom.exit();
}

t = Date.now();
address = system.args[1];
page.open(address, function(status) {
  if (status !== 'success') {
    console.log('FAIL to load the address');
  } else {
    t = Date.now() - t;
    console.log('Loading ' + system.args[1]);
    console.log('Loading time ' + t + ' msec');
  }
  phantom.exit();
});
```
`phantomjs loadspeed.js http://www.google.com`で実行すると、`Loading http://www.google.com
Loading time 698 msec`といった実行結果が表示される。

#Experiment #3 Code Evaluation
基本的には`evaluate()`関数を使ってウェブ上のJavaScriptのデータを取得することができる。例えばwebページでのタイトルを取得したいなんて時は、

```js
// headlessブラウザを作成
var page = require('webpage').create();

//URLを開く
page.open('http://www.google.com', function(status) {
  //ブラウザ内でJSを介してデータを取得	
  var title = page.evaluate(function() {
    return document.title;
  });
  console.log('Page title is ' + title);
  phantom.exit();
});
```

terminal上で実行すると`Page title is Google`と表示される。

#Supplemental tryout
PhantomJsを使ってgoogle mapをベースとした運転案内までできちゃう。 

```js:direction.js
// Get driving direction using Google Directions API.

var page = require('webpage').create(),
    system = require('system'),
    origin, dest, steps;

if (system.args.length < 3) {
    console.log('Usage: direction.js origin destination');
    console.log('Example: direction.js "San Diego" "Palo Alto"');
    phantom.exit(1);
} else {
    origin = system.args[1];
    dest = system.args[2];
    page.open(encodeURI('http://maps.googleapis.com/maps/api/directions/xml?origin=' + origin +
                '&destination=' + dest + '&units=imperial&mode=driving&sensor=false'), function (status) {
        if (status !== 'success') {
            console.log('Unable to access network');
        } else {
            steps = page.content.match(/<html_instructions>(.*)<\/html_instructions>/ig);
            if (steps == null) {
                console.log('No data available for ' + origin + ' to ' + dest);
            } else {
                steps.forEach(function (ins) {
                    ins = ins.replace(/\&lt;/ig, '<').replace(/\&gt;/ig, '>');
                    ins = ins.replace(/\<div/ig, '\n<div');
                    ins = ins.replace(/<.*?>/g, '');
                    console.log(ins);
                });
                console.log('');
                console.log(page.content.match(/<copyrights>.*<\/copyrights>/ig).join('').replace(/<.*?>/g, ''));
            }
        }
        phantom.exit();
    });
}
```
を適当なディレクトリに投げ込んでおき、terminal上で`phantomjs direction.js 出発地 到着地`で実行。試しに`phantomjs direction.js Tokyo Osaka`で実行したところ、

```
Head south
Turn right at 都庁南（交差点） toward 都道431号線
At 角筈区民センター前（交差点）, continue onto 都道431号線
Turn left at 西新宿四丁目（交差点） onto 山手通り/都道317号線
Continue straight to stay on 山手通り/都道317号線
Take the ramp on the right to 首都高速中央環状線
Toll road
Continue onto Exit 初台南料金所
Toll road
Merge onto 首都高速中央環状線
Toll road
Take exit 大橋ＪＣＴ on the right toward 東名・都心環状
Toll road
Keep left at the fork, follow signs for 東名 and merge onto 首都高速3号渋谷線
Toll road
Keep right to continue on 東名高速道路
Toll road
Keep right at the fork to stay on 東名高速道路, follow signs for 右ルート・静岡・御殿場
Toll road
Take exit 御殿場ＪＣＴ toward 新東名・静岡・名古屋
Toll road
Continue onto 新東名高速道路
Toll road
Continue onto Exit 浜松いなさＪＣＴ
Toll road
Keep right at the fork, follow signs for 東名・東京・名古屋 and merge onto 新東名高速道路
Toll road
Take exit 三ヶ日ＪＣＴ on the right toward 東名・名古屋
Toll road
Merge onto 東名高速道路
Toll road
Take exit 豊田ＪＣＴ toward 東海環状・伊勢湾岸道・豊田東出口・土岐ＪＣＴ・四日市・新名神
Toll road
Keep right at the fork, follow signs for 伊勢湾岸道・四日市・新名神 and merge onto 伊勢湾岸自動車道
Toll road
Take exit 高速飛島ＩＣ on the right toward 伊勢湾岸自動車道
Toll road
Continue onto 伊勢湾岸自動車道
Toll road
Take exit 四日市ＪＣＴ on the right toward 東名阪道・大阪・伊勢道
Toll road
Merge onto 東名阪自動車道
Toll road
Take exit 亀山ＪＣＴ toward 新名神・京都・大阪
Toll road
Continue onto 新名神高速道路
Toll road
Take exit 草津ＪＣＴ toward 草津ＰＡ・名神・京滋・京都・大阪
Toll road
Keep right at the fork to continue on Exit 草津ＰＡ, follow signs for 名神 and merge onto 名神高速道路
Toll road
Keep right at the fork to stay on 名神高速道路, follow signs for 右ルート
Toll road
Take exit 豊中ＩＣ toward 阪神高速・豊中出口・大阪市内
Toll road
Keep left at the fork, follow signs for 大阪市内・一般道出口・阪神高速
Toll road
Keep right at the fork to continue on Exit 高速豊中ＩＣ, follow signs for 阪神高速
Toll road
Continue onto Exit 名神阪神料金同時徴収料金所
Toll road
Merge onto 阪神高速11号池田線
Toll road
Merge onto 阪神高速1号環状線
Toll road
Take exit 都市高北浜 toward 北浜出口
Partial toll road
Turn right at 菅原町西（交差点） onto 堺筋
Slight right to stay on 堺筋
Slight right onto 天神橋筋
Turn right at 天神橋（交差点） onto 土佐堀通/府道168号線
Turn right at 北浜２（交差点） toward 中之島通
Turn is not allowed 8:00 AM – 8:00 PM
Turn left toward 中之島通
Turn left onto 中之島通
Destination will be on the left
```
正確さは別としてとりあえず動く模様。PhantomJSだけでも色んなことが試せることが判明。
参考: [direction.js](https://github.com/ariya/phantomjs/blob/master/examples/direction.js)