Murayama blog.

プログラミング教育なブログ

PhantomJS入門 CasperJSを使う

ここんとこのブログの題材として2週間ほどPhantomJSを触ってみましたが、これけっこう難しいです。JavaScript独特のイベント処理や、WebKitの仕組みうんぬんで、リンクをクリックするのにもハマったりします。

何かいい方法あるはず、、と調べてたら、やっぱりあるんですね。

CasperJS

CasperJS is a navigation scripting & testing utility for PhantomJS, written in Javascript

http://casperjs.org/


CasperJSは、JavaScriptで実装されたPhantomJSのユーティリティです。ざっくり以下のことができます。

  • ブラウザのナビゲーションステップを順に定義できる
  • フォーム入力
  • リンクのクリック
  • ページのスクリーンショット(画面の一部分だけもOK)
  • リモートDOMのテスト
  • イベントのロギング
  • バイナリを含んだリソースのダウンロード
  • テストスイート(結果をJUnitXMLで出力)
  • Webコンテンツをスクレイピング

Installation

Macユーザーはbrewコマンドでインストールできます。

% brew install casperjs

詳しくは公式サイトで。
http://casperjs.org/installation.html

Quickstart

http://casperjs.org/quickstart.html

本家サイトのQuickstartを見てみると大体のイメージがつかめます。
サンプルはGoogleの検索結果を出力するものです。*1

var casper = require('casper').create();

casper.start('http://google.fr/', function() {
    // search for 'casperjs' from google form
    this.fill('form[action="/search"]', { q: 'casperjs' }, true);
});

たとえば、上記の場合だと、まずcasperオブジェクトを作っています。このcasperオブジェクトを操作していくことが処理の中心になります。

casper.startメソッドにURLとコールバック処理を定義しています。コールバックの中では、フォームの入力処理(fill)を実行しています。第3引数にtrueを指定しているので、フォームの入力と共にsubmitします。

ナビゲーションステップ

サンプルの説明

勉強も兼ねてサンプルを作ってみました。
Webサーバー上に3つのページがあります。

  • /index
  • /page1
  • /page2

/indexには/page1、/page2へのリンクがあります。
これを以下の手順でスクリーンショットを撮ってみたいと思います。

  1. /indexのスクリーンショットを撮る
  2. page1リンクをクリックし/page1"へ移動する
  3. /page1のスクリーンショットを撮る
  4. "ブラウザの戻る"を使用して/indexへ戻る
  5. page2リンクをクリックし/page2"へ移動する
  6. /page2のスクリーンショットを撮る

このサンプルを実行すると次の3枚のスクリーンショットを撮ります。*2


CasperJSプログラムを作成する

casper = require('casper').create()

casper.start 'http://localhost:4567',  ->
    @capture('index.png')
    @click("#link1")

casper.then ->
    @capture('page1.png')
    @back()

casper.then ->
    @click("#link2")

casper.then ->
    @capture('page2.png')

casper.run()

上記のプログラムを実行するとindex、page1、page2のスクリーンショットを取得できます。
サンプルはCoffeeScriptで書いています。そのため、コード量も少なくなり、より直感的になっています。*3

コードの解説も加えておくと、casperオブジェクトに対して、まずstartメソッドを呼び出しています。その後、thenメソッドで処理を順に定義していきます。実際に実行するためにはrunメソッドを呼び出す必要があります。

CasperクラスのAPI

CasperクラスのAPIを見れば他にどんなことができるのか参考になります。たとえば、echoでコンソール出力したり、evaluateでDOMを解析したりというのがわかります。
http://casperjs.org/api.html#casper

背景色について

PhantomJSでスクリーンショットを撮ると背景色が透過されてしまいます。
公式のFAQによると、PhantomJSプログラム側で細工してあげる必要があるようです。
http://phantomjs.org/faq.html

page.evaluate(function() {
    document.body.bgColor = 'white';
});

おまけサーバーサイドプログラム

本題と関係ないですが、サンプルで使用したサーバーサイド処理です。静的な処理だけで済んだから、Webサーバーで良かったんですけど、Sinatra使ってみたかったので。

require 'sinatra'
require 'sinatra/reloader'
require 'haml'

get "/index" do
  haml :index
end

get "/page1" do
  haml :page1
end

get "/page2" do
  haml :page2
end

__END__

@@index
%html
  %body(style="background-color: white")
    %h1 index
    %ul
      %li
        %a(href="/page1" id="link1") page1
      %li
        %a(href="/page2" id="link2") page2

@@page1
%html
  %body(style="background-color: white")
    %h1 page1

@@page2
%html
  %body(style="background-color: white")
    %h1 page2

サーバーサイド処理の依存ライブラリ(Gemfile)はこちら。

# A sample Gemfile
source "https://rubygems.org"

gem "thin"
gem "sinatra"
gem "sinatra-contrib"
gem "haml"

*1:残念ながら私のローカルでは上手く動きませんでした。

*2:スクリーンショット地味すぎた

*3:PhantomJS単体で同じことをやるとなると、イベント制御用のユーティリティを準備するのが大変です。