CasperJSでクローリングしながらキャプチャを撮る
CasperJSを使ってクローリングしながらキャプチャを撮りました。
node.jsでクローラを作成していたのですが、
画面キャプチャも撮りたかったので、CasperJSを使うことにしました。
CasperJSはPhantomJSを利用するためのライブラリです。
PhantomJSとはコマンドラインのWebブラウザで、画面表示のないブラウザです。
PhantomJSを使うと、コマンドラインからブラウザ操作をしたり、
画面キャプチャを撮ることができます。
PhantomJSはレンダリングエンジンとしてWebkitを使用しています。
インストール
CentOS7に必要な依存関係をインストールしておきます。
# yum install freetype # yum install fontconfig
次にPhantomJSとCasperJSをグローバルインストールします。
# npm install -g phantomjs # npm install -g casperjs # phantomjs -v 2.1.1
画面キャプチャの取得
node.jsの日本語サイトを画面キャプチャしてみます。
screenshot.js
var casper = require('casper').create(); casper.start(); casper.open('http://nodejs.jp/'); casper.then(function () { casper.capture("nodejs.png"); }); casper.run();
処理の概要です。
1行目でCasperJSを読み込み、オブジェクトを生成。
var casper = require('casper').create();
open()で対象URLを指定し、then()にその後の処理を記述します。
capture()で画面キャプチャを取得して、引数の名前で保存します。
casper.start(); casper.open('http://nodejs.jp/'); casper.then(function () { casper.capture("nodejs.png"); });
最後のrun()で実際に実行されます。
casper.run();
実行してみます。
# casperjs screenshot.js
画面キャプチャの結果がこちら。
なんかめっちゃ長くなりました。
nodejs.png
原因は画面サイズの指定がデフォルトでは400x300であること。
これでは困るので、viewport()でブラウザと同じくらいの大きさに設定します。
screenshot2.js
var casper = require('casper').create(); casper.start(); casper.open('http://nodejs.jp/'); casper.viewport(1500, 1000); casper.then(function () { casper.capture("nodejs2.png"); }); casper.run();
再実行。
# casperjs screenshot2.js
撮り直した結果がこちら。
いい感じに撮れました。
nodejs2.png
クローリング
クローリングしながらキャプチャを撮ってみました。
藤子F不二雄大全集のサイトをクローリングしてみます。
ソースはこんな感じになりました。
crawl_capture.js
var casper = require('casper').create(); var LINK_LEVEL = 4; var TARGET_URL = "http://www.shogakukan.co.jp/fzenshu/4thseason"; var list = {}; casper.start(); casper.viewport(1500, 1000); function getLinks() { var links = document.querySelectorAll('a'); return Array.prototype.map.call(links, function(e) { return e.getAttribute('href'); }); } casper.then(function() { casper.emit('crawl', TARGET_URL, 0); }); casper.run(); casper.on('crawl', function (url, level){ var links = []; if (level >= LINK_LEVEL) return; if (list[url]) return; list[url] = true; var us = TARGET_URL.split("/"); us.pop(); var base = us.join("/"); if (url.indexOf(base) < 0) return; casper.open(url).then(function () { links = casper.evaluate(getLinks); Array.prototype.map.call(links, function (href) { var absolutePath = casper.evaluate(function (path) { var e = document.createElement('span'); e.innerHTML = '<a href="' + path + '" />'; return e.firstChild.href; }, href); if (!absolutePath) return; absolutePath = absolutePath.replace(/\#.+$/, ""); casper.emit('crawl', absolutePath, level + 1); }); casper.open(url).then(function () { var savepath = url.split("/").slice(2).join("/"); if (savepath.substr(savepath.length - 1, 1) == '/') { savepath += "index.html"; } casper.download(url, savepath); var capturePath = savepath.split(".").slice(0, -1).join(".") + ".png"; casper.capture(capturePath); }); }); });
処理の概要です。
クロールする階層の深さと、対象のURLを指定。
var LINK_LEVEL = 4; var TARGET_URL = "http://www.shogakukan.co.jp/fzenshu/4thseason";
getLinks()で対象ページからaタグのhref要素を取り出します。
function getLinks() { var links = document.querySelectorAll('a'); return Array.prototype.map.call(links, function(e) { return e.getAttribute('href'); }); }
クロールで再帰処理をするので、メイン処理はcrawlというカスタムイベントとして定義します。
emit()で対象URLと階層の深さを指定して初期呼び出します。
casper.then(function() { casper.emit('crawl', TARGET_URL, 0); });
カスタムイベントを定義
casper.on('crawl', function (url, level){ : : }
階層の深さのチェックと一度クロールしたURLの場合はスキップ。
if (level >= LINK_LEVEL) return; if (list[url]) return;
リンク先のページに対して再帰的に処理を実行。
Array.prototype.map.call(links, function (href) { var absolutePath = casper.evaluate(function (path) { var e = document.createElement('span'); e.innerHTML = '<a href="' + path + '" />'; return e.firstChild.href; }, href); if (!absolutePath) return; absolutePath = absolutePath.replace(/\#.+$/, ""); casper.emit('crawl', absolutePath, level + 1); });
htmlページの取得と画面キャプチャの取得。
casper.open(url).then(function () { var savepath = url.split("/").slice(2).join("/"); if (savepath.substr(savepath.length - 1, 1) == '/') { savepath += "index.html"; } casper.download(url, savepath); var capturePath = savepath.split(".").slice(0, -1).join(".") + ".png"; casper.capture(capturePath); });
実行してみます。
# casperjs crawl_capture.js
確認するとうまく取れているようです。
# tree --charset=x www.shogakukan.co.jp/ www.shogakukan.co.jp/ `-- fzenshu |-- 4thseason | |-- index.html | |-- index.png | |-- lineup.html | |-- lineup.png | |-- sakuhin.html | |-- sakuhin.png | |-- tokuchou.html | `-- tokuchou.png |-- fgoods | |-- index.html | `-- index.png |-- lassie | |-- index.html | `-- index.png `-- lineup |-- 21emon01.html |-- 21emon01.png |-- 21emon02.html |-- 21emon02.png |-- bakeru.html |-- bakeru.png |-- bekkan.html |-- bekkan.png |-- berabo.html |-- berabo.png |-- boysf01.html : : : |-- umino02.html |-- umino02.png |-- umino03.html |-- umino03.png |-- utopia.html |-- utopia.png |-- yamabiko.html `-- yamabiko.png 5 directories, 246 files
トップページの画面キャプチャを確認。
www.shogakukan.co.jp/fzenshu/4thseason/index.png
うまく撮れているみたいです。
各書籍の詳細ページを確認してみます。
www.shogakukan.co.jp/fzenshu/lineup/pokonyan.png
きちんとポコニャンのキャプチャが撮れています。
次触る時はページの操作をやってみたいと思います。
ソースは一応あげときました。
github.com
【参考】