JavaScript
casperJs
自動化
ハマり

casperJsでブラウザ作業を自動化するまでの5つのステップ


はじめに

前回書いた内容を知り合いに見せたら、「ピンポイントすぎてわからん」、「それ以前にハマる」、という声があったので、自分が踏んだステップとハマったポイントを再度まとめました。

で、それをソースで説明できるようにサンプルを作ったので、これをすればブラウザ作業の自動化のベースは作れると思います。(作れるはず)

それなりに細かく書いたので思ったよりボリューミーです


step1:phantomJs, capserJsのインストール

sudo yum install nodes npm

sudo npm install -g phantomjs
sudo npm install -g casperjs

これだけ

あとはバージョンを確認できればOK (失敗例とその対策は後に書いてます)

phantomjs -v

2.1.1
casperjs --version
1.1.4

ハマりポイント① npm installで失敗

npm install -g phantomjs

npm http GET https://registry.npmjs.org/phantomjs
npm http GET https://registry.npmjs.org/phantomjs
npm http GET https://registry.npmjs.org/phantomjs
npm ERR! Error: CERT_UNTRUSTED

こんな感じでインストールできなかったら、npm config set strict-ssl falseを実行

※install完了後にtrueに戻すことを忘れずに

ハマりポイント② phantomjsが実行できない

phantomjs -v

/usr/lib/node_modules/phantomjs/lib/phantom/bin/phantomjs: error while loading shared libraries: libfontconfig.so.1: cannot open shared object file: No such file or directory

こんなエラーがでたら、sudo yum -y install fontconfig-develを実行

CentOSでPhantomJSの起動ができない を参考にしました


step2:とりあえず簡単なものを実行してみる

サンプルとして「googleで検索して、その検索結果をキャプチャする」まで実施してみる

※ここまではCasperJSでブラウザ操作を自動化しようを参考にしました


sample.js

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

casper.start("https://www.google.co.jp/");
casper.then(function() {
this.capture('google.png');
});
casper.run();

こんな感じでサンプルを作成

start()capture()は直感的にわかると思うので割愛

then()はその処理が完了するまで待ってから引数に設定した関数を実行する

今回であれば、google.co.jpが表示されてからthis.capture('google.png')を実行する

run()は処理の実行(これがないと、何もおきなくなります。)

モジュールができたのでこのコマンド(casperjs sample.js)を実行

すると、「google.png」ができていて、ちゃんとキャプチャされているはず。

次に、サンプルを少し変更して「casperjs」で検索かけて、キャプチャをとるように修正


sample.js

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

casper.start("https://www.google.co.jp/");
casper.then(function() {
this.capture('google.png');
}).then(function() {
this.fill('form', {'q': 'casperjs'}, true);
});
casper.then(function() {
this.capture('search_casperjs.png');
});
casper.run();

追加点としては、capture()でキャプチャとるのを待ってから、fill()を実行している部分

fill()は'form'に引数のjsonで指定したパラメータを設定しサブミットする

今回の処理では、formタグ内に、<input name='q' ・・・のvalueにcasperjsを設定している感じ

それで、最後にtrueを設定すればサブミットする。(falseなら値を入れるだけで終わる)

ハマりポイント③

ちなみに、capture()fill()の間にthen()を挟まないと正しい挙動にならない。

どうやら、capture()で画面をキャプチャしている途中だから、fill()による画面遷移が効いていない模様。

※調べたが具体的な原因はわからず。(知っている人がいたら教えてください)


step3:submit以外の画面遷移(リンク押下)を対応する

サブミットだけじゃリンクが対応できん。ということでクリック操作のサンプルを作成

「qiitaで自分の記事を検索かけて、キャプチャする」ってものを作る

※ついでに、キャプチャを綺麗(サイズがおかしいのを対応)


sample2.js

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

casper.start("https://qiita.com/");
casper.viewport(1280, 768); // これで1280×768でキャプチャとる

casper.then(function() {
this.fill('form', {'q': '"casperJsを使ってポップアップ画面でファイルアップロードするまで"'}, true);
});

casper.then(function() {
this.capture('search.png');
}).then(function() {
// 完全一致で検索かけているから1つのみだろうけど、念のためURLで自分の記事チェックを挟む
this.click(".searchResult_main .searchResult_itemTitle a[href^='/192_60_33_2']");
});

casper.then(function() {
this.capture('item.png');
});
casper.run();


前回から変わったことと言えば、

1. viewport()でサイズ指定

2. click()でクリック。クリックしたいseletorを指定してあげればOK

ただ、思った通りの挙動にならず。

どうやら、完全に描画される前にキャプチャを取ってたり、クリックイベントが動いている模様

ハマりポイント④ サーバからのレスポンスをちゃんと待つ!

ということで、描画されていることを待つためにwaitForSelector()を利用

これで対象のselectorが見つかるまで待てる。ということでサンプルに反映


sample2.js

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

casper.start("https://qiita.com/");
casper.viewport(1280, 768);

casper.then(function() {
this.fill('form', {'q': '"casperJsを使ってポップアップ画面でファイルアップロードするまで"'}, true);
});

casper.waitForSelector(".searchResult_main", function() {
this.echo("complate search...");
}, function() {
this.echo("timeout... wait 10s");
this.exit();
}, 10000).then(function() {
this.capture('search.png');
}).then(function() {
this.click(".searchResult_main .searchResult_itemTitle a[href^='/192_60_33_2']");
});
casper.then(function() {
this.capture('item.png');
});
casper.run();


waitForSelector(待つ対象selector, Function 実行処理, [Function タイムアウト時の実行処理, タイムアウト時間(s)])

となっているので、タイムアウトしなければメッセージ出して後続処理を実行、タイムアウトならexit()で終了とする感じ(ちなみにデフォルトは5秒らしいです。)


step4:form以外でパラメータを渡す

ここでやりたいのは、submitする必要がないけど、データ渡したいとき、ってありません?

例えば、「selectboxを選んだら、それに合わせてリンクが切り替わる。」的なものです。


sample3

casper.then(function() {

this.sendKeys('.selector', 'value', {reset: ture});
}).then(function() {
this.click('.links a');
});

すみません、ちゃんとしたサンプルないのでイメージです。(いい例がなかったので)

今回やったことは、sendKeys()でデータを設定すること、対象のselectorに'value'を設定する感じ

resetがtrueなら上書き。falseなら追記

ハマりポイント⑤ ajaxもサーバからのレスポンスをちゃんと待つ!

ajax処理後のDOM操作内容によっては前のwaitForSelector()では対応できないケースがある

例えば、$('.selector').text('XXXX')のような処理

こんな感じで遷移先だけ変えられた場合にselectorが増えないので対応しきれない(そんな無理やりやっているサイトがあるのです)

その対応として、waitForSelectorTextChange()を利用する

これにより、対象のselectorの中身が変わったら後続が動くようになる。


sample3

casper.then(function() {

this.sendKeys('.selector', 'value', {reset: ture});
}).waitForSelectorTextChange('.selector', function() {
this.echo('.selector changed...');
}, function() {
// タイムアウトは終了...
this.echo("timeout... wait 10s");
this.exit();
}, 10000).then(function() {
this.click('.links a');
});

使い方は、waitForSelector()と変わらないので説明は割愛する


step5:その他諸々もあるよね。(今後もふくめて)


ブラウザの「戻る」「進む」も対応したい!

戻るはブラウザバックですけど、進むはなんて言うんですかね?ブラウザフォワード?

こちらを見れば一発かと。


ポップアップ、iframeとかに対応したい!

ここでようやく前回の内容が登場

casperJsを使ってポップアップ画面でファイルアップロードするまで


ドラッグ&ドロップも対応したい!

mouseEvent()で対応できそう

けど、試していないからまたの機会に。


おわりに(感想)

これで、ブラウザ作業は一通り実施できると思います。

ここまで見てくれてありがとうございます。

正直、「まとめてると結構な量になってしまった、全部見てもらえるだろうか」という感想

初心者はこのぐらいあると嬉しいと思うが、この情報量がノイズになる人もいるのだろうな、と思うと難しい・・・

細かくアウトプットすれば、1つ1つはシンプルだけど、全体で流れができるからきっとそれがいいんだろうな、と思いました。

個人的な今回の学びは、技術的なことではなくて、アウトプットの仕方的な話でした