ヘッドレスブラウザ
GUI(注2)を持たずプログラムから操作できるブラウザをヘッドレスブラウザと呼びます。通常のブラウザと違って表示ウインドウを持たないためにヘッドレス(頭部を持たない)と呼ばれます。
PhantomJS(BSDライセンス)はWebKit(注3)をベースとしたヘッドレスブラウザで、JavaScriptのAPIを持ちます。通常のブラウザのようなユーザ操作を再現することができ、Webサイトにアクセスしてフォームを送信したり、ページのキャプチャを取ったりすることができます。またページ内のJavaScriptやCSS、Cookieなども通常のブラウザと同じように扱えます。
ヘッドレスブラウザは実際のブラウザとほぼ同等に動作するうえ、LinuxサーバなどでGUIのない環境でも実行できます。Webサイトのコードを修正した時にUIや遷移が崩れていないかどうかテストするプログラムを作ったり、スクレイピング(注4)をする際などに便利です。
ターミナルではなくマウスで操作できるウインドウなどのインタフェースのこと。Graphical User Interfaceの略。
SafariやChromeなどのブラウザで使われているレイアウトエンジン。
Webページから一部の必要なデータだけを取得してプログラムで利用すること。
PhantomJSのインストール
まず、ヘッドレスブラウザ本体であるPhantomJSをインストールします。
MacまたはLinuxの場合
PhantomJSのWebサイトからファイルをダウンロードします。
ダウンロードしたファイル名にstaticと付いている場合は、解凍すると現れるbin/phantomjsを/usr/local/bin/などのPATHが通ったディレクトリにコピーしてインストールは完了です。
一方、ファイル名にdynamicと付いている場合は、解凍したディレクトリをそのまま任意の場所に移動し、PATHの通ったディレクトリからbin/phantomjsに対してシンボリックリンクを作成します。例えば解凍したディレクトリを/usr/local/phantomjsに置いた場合、
ln -s /usr/local/phantomjs/bin/phantomjs /usr/local/bin/
と実行すると、/usr/local/bin/phantomjsにシンボリックリンクが作成され、phantomjsコマンドが使えるようになります。
Windowsの場合
PhantomJSのWebサイトからファイルをダウンロードします。解凍すると現れるphantomjs.exeがPhantomJSのプログラム本体なので、任意の場所に移動してPathを通します。
CasperJSのインストール
PhantomJS自体はJavaScriptのAPIを持っており、phantomjsコマンドで利用することができます。しかしPhantomJSは基本的なAPIしか持たないため、フォーム送信などの複雑な操作をするのは少し大変な作業となってしまいます。
そこで、PhantomJSの扱いを簡単にするためCasperJS(MITライセンス)というツールをインストールします。CasperJSはPhantomJSの上で動くツールで、これを使うと複雑な操作を簡単なAPIで実現できます。
MacまたはLinuxの場合
CasperJSのWebサイトからファイルをダウンロードします。解凍すると現れるディレクトリを任意の場所に配置し、PATHが通ったディレクトリからシンボリックリンクを張ります。例えば解凍したディレクトリを/usr/local/casperjsに置いた場合、
ln -s /usr/local/casperjs/bin/casperjs /usr/local/bin/
と実行すると/usr/local/bin/casperjsにシンボリックリンクが作成されます。実行するときはコマンドラインから
casperjs ファイル名
のように入力します。
Windowsの場合
CasperJSのWebサイトからファイルをダウンロードします。解凍すると現れるディレクトリ内のbin/casperjsがプログラム本体です。中身はPythonスクリプトなので、コマンドプロンプトから
python bin/casperjs ファイル名
のように実行できます。Cygwinなどを使えば、casperjs ファイル名
で起動できるように環境を設定することも可能です。
Node.jsでは実行不可
PhantomJSやCasperJSは独自のJavaScriptエンジンを使うため、nodeやcoffeeコマンドで実行することはできません。その代わり、CasperJSやPhantomJSはJavaScriptとCoffeeScriptのいずれのソースファイルも直接実行できます。拡張子が.jsか.coffeeかによって自動的に判断されます。
CasperJSの使い方
CasperJSのプログラムはリスト17のように記述します。jQueryをscripts/jquery-latest.min.jsに設置してこのプログラムを実行すると、Webページにアクセスしてフォームを送信し、リンクをたどり、CSSを変更してキャプチャを取ります。
# casperインスタンスを作成する casper = require('casper').create() # URLを開く casper.start 'http://www.yahoo.co.jp/', -> # 画面サイズを幅1024px、高さ768pxにセットする @viewport 1024, 768 # ページタイトルが一致することを確認する @test.assertTitle 'Yahoo! JAPAN', 'タイトルがYahoo! JAPAN' # フォームに値を入力して送信する @fill 'form[name="sf1"]', p: '東京 wikipedia', true # ページがロードされるまで待つ casper.then -> # ページ内にjQueryを読み込む @page.injectJs 'scripts/jquery-latest.min.js' # ページ内でJavaScriptを実行し、その値を受け取る links = @evaluate -> # この部分のコードがページ内で実行される # <h3>以下のテキストを収集して配列にして返す links = $('h3') $(link).text() for link in links # 結果を表示する for link, i in links @echo "#{i+1} - #{link}" # URLに特定の文字列が含まれることを確認する @test.assertUrlMatch /\?p=東京\+wikipedia/, 'URLに検索文字列が含まれる' # DOM要素が存在することを確認する @test.assertExists 'div#contents', 'div#contentsが存在' # 文字列が存在することを確認する @test.assertTextExists '検索結果', '「検索結果」という文字列が存在' # CSSセレクタに該当するリンクをクリックする @click '#WS2m > ul > li:first-of-type h3 a' # ページがロードされるまで待つ casper.then -> # URLが一致することを確認する @test.assertEquals @getCurrentUrl(), 'http://ja.wikipedia.org/wiki/東京', 'URLが一致' # ページ内でJavaScriptを実行する @evaluate -> # 配色を変える h1 = document.getElementById('firstHeading') h1.style.backgroundColor = '#333' h1.style.color = '#fff' # ページのキャプチャを取りファイルに保存する @capture 'wikipedia.png' # 全体を実行する casper.run -> # すべて完了したらPhantomJSを終了する @exit()
このプログラムはcoffeeコマンドを使わずにcasperjs ファイル名
のように実行します。実行すると図1のように結果が表示されます。
CasperJSの主なAPI
CasperJSの主なAPIを紹介します。
基本的な操作
以下の関数の引数callback
の中では、this
でcasperオブジェクトを参照できます。[ ] で囲まれた引数の指定は任意です。
casper.start(url, [callback])
Casperをスタートし、url
を開きます。callback
で関数が指定されると、ページがロードされた後で実行されます。casper.start()メソッドの引数を1つも指定しない場合は、Casperのスタートだけが行われます。
casper.open(url, [settings])
url
を開きます。事前にcasper.start()メソッドが呼ばれている必要があります。POSTリクエストを送る際は、次のようにsettings
引数を指定します。dataプロパティには文字列またはオブジェクトを指定できます。オブジェクトを指定すると、=
と&
で組み合わせた文字列となって送信されます。
casper.open 'http://localhost/post', method: 'post' data: query: 'tea' count: 100
casper.then(callback)
ページがロードされた後でcallback
の関数を実行します。
casper.run(callback)
ステップ全体を実行します。完了後にcallback
の関数が呼ばれます。
casper.exit([status])
PhantomJSを終了します。status
を指定した場合、その値がプロセスの終了ステータスとなります。
casper.wait(timeout, [callback])
timeout
で指定されたミリ秒の間一時停止します。callback
を指定すると、再開時に実行されます。
ページ内の操作
casper.viewport(width, height)
画面の表示サイズを幅width
ピクセル、高さheight
ピクセルにセットします。
casper.getCurrentUrl()
現在のページのURLを返します。URLデコードされた文字列が戻り値となります。
casper.click(selector)
CSS3セレクタselector
に該当する要素をクリックします。
casper.fill(selector, values, [submit])
CSS3セレクタselector
に該当するフォームについて、values
オブジェクトに指定された値をそれぞれセットします。submit
にtrue
を指定するとフォームの送信も行います。
例えば次のようなHTMLに対して操作する場合を考えます。
<form id="search_form" action="/search" method="get"> <input type="text" name="query"> <input type="text" name="count"> <input type="submit" value="送信"> </form>
この場合、フォームに値を入力して送信するには次のように記述します。
casper.fill 'form#search', query: 'tea' count: 100 , true
casper.back()
1つ前のページに戻ります。
casper.evaluate(fn)
fn
に指定された関数をページ内で実行し、値を返します。
casper.echo(message, [style])
標準出力にmessage
を出力します。style
を指定すると色付きで表示されます。style
には次のうちの一つを指定できます。ERROR, INFO, TRACE, PARAMETER, COMMENT, WARNING, GREEN_BAR, RED_BAR, INFO_BAR。
casper.debugHTML()
デバッグ用に現在のページのHTMLを出力します。
casper.capture(filename, [clipRect])
ページをレンダリングしてfilename
のファイルに保存します。.pngや.jpg、.pdfなど拡張子によって出力フォーマットが変わります。clipRect
にオブジェクトを指定すると、部分的に切り取って保存できます。例えば次のように指定すると、上0px、左0pxの位置から幅1024px、高さ768pxの領域が保存されます。
@capture 'page.png', top : 0 left : 0 width : 1024 height: 768
ページ内で背景色が決められていない部分の背景は透明になります。デフォルトの背景色を白に設定するにはcasper.evaluate()
メソッドを使って次のようにCSSを変更してからキャプチャを取ります。
# 背景色を変更する casper.evaluate -> document.body.style.backgroundColor = 'white' # キャプチャを取る casper.capture 'page.png'
casper.page.injectJs(path)
path
のファイルを外部JavaScriptとしてページ内に読み込みます。ローカルのファイルのみ指定可能で、URLは指定できません。
テスト用メソッド
以下の各メソッドで最後の引数message
を指定すると、そのメッセージがテスト実行時に表示されます。message
の指定は任意ですが、ここにテストの概要を書いておくとわかりやすくなります。
casper.test.assert(condition, [message])
condition
がtrue
であることを確認します。「trueとして解釈できる値」ではなく厳密にtrue
である必要があります。
casper.test.assertEquals(testValue, expected, [message])
testValue
とexpected
を厳密に比較して等しいことを確認します。
casper.test.assertExists(selector, [message])
CSS3セレクタselector
に該当する要素が存在することを確認します。
casper.test.assertTextExists(expected, [message])
ページ内に文字列expected
が出現していることを確認します。
casper.test.assertTitle(expected, [message])
ページのタイトルがexpected
と一致することを確認します。
casper.test.assertUrlMatch(pattern, [message])
ページのURLが正規表現pattern
にマッチすることを確認します。
その他のメソッド
その他のメソッドはCasperJSのWebサイトを参照してください。