なんか大げさなタイトルですが要するにバージョンアップのお知らせだったります。
「cheerio-httpcliって何よ」という場合は紹介記事をご覧になると、こちらの記事もより分かりやすくなると思います。
簡単に説明するとこんな感じのことができるモジュールです。
var client = require('cheerio-httpcli');
// Googleで「node.js」について検索する。
client.fetch('http://www.google.com/search', { q: 'node.js' }, function (err, $, res) {
// レスポンスヘッダを参照
console.log(res.headers);
// HTMLタイトルを表示
console.log($('title').text());
// リンク一覧を表示
$('a').each(function (idx) {
console.log($(this).attr('href'));
});
});
さて、今回のバージョンアップで結構機能を追加しました。npmにも登録済みなので
$ npm install cheerio-httpcli
でインストールできます(バージョン0.3.0)。
ソースやサンプルはこちら。
では新機能の紹介です。
目玉機能1: コールバック形式とプロミス形式のハイブリッド化
今まではNode.jsお馴染みのコールバック形式での実装だったので、順を追ってWEBサイトの中に潜っていく場合はいわゆるコールバック地獄が発生してしまう状態でした。
var client = require('cheerio-httpcli');
client.fetch('http://hogehoge/', function (err, $, res) {
if (err) {
return console.log(err);
}
client.fetch('http://hogehoge/page-1.html', function (err, $, res) {
if (err) {
return console.log(err);
}
client.fetch('http://hogehoge/page-1-1.html', function (err, $, res) {
if (err) {
return console.log(err);
}
client.fetch('http://hogehoge/page-1-2.html', function (err, $, res) {
if (err) {
return console.log(err);
}
// リンク一覧を表示
$('a').each(function (idx) {
console.log($(this).attr('href'));
});
});
});
});
});
今回のバージョンアップにより今流行のプロミス形式で呼び出すこともできるようになりました。
上記コールバック地獄もプロミス形式のメソッドチェーンを使用すればこんな感じにスッキリさせることができます。エラーの捕捉も下の方にあるcatch
でまとめて面倒見てくれます。
client.fetch('http://hogehoge/')
.then(function (result) {
return client.fetch('http://hogehoge/page-1.html');
})
.then(function (result) {
return client.fetch('http://hogehoge/page-1-1.html');
})
.then(function (result) {
return client.fetch('http://hogehoge/page-1-2.html');
})
.then(function (result) {
var $ = result.$;
// リンク一覧を表示
$('a').each(function (idx) {
console.log($(this).attr('href'));
});
})
.catch(function (err) {
console.log(err);
})
.finally(function () {
// 処理完了でもエラーでも最終的に必ず実行される
});
それぞれの形式の使い分け方
fetch()
でWEBページを取得する際の引数にコールバック関数を指定した場合は処理後にそのコールバック関数が呼ばれ、コールバック関数を省略した場合は戻り値にPromiseオブジェクトが返る仕様となっています。
コールバック関数を指定した場合はPromiseオブジェクトは返しませんので、コールバック関数で処理しつつ同時進行でプロミス形式でなんちゃらするといったカオスなことはできません。
引数の受け取り方の違い
従来のコールバック形式の場合は
client.fetch('http://hogehoge/', { param1: 'fuga' }, function (err, $, res, body) {
console.log(err);
console.log($);
console.log(res);
console.log(body);
});
というようにコールバック関数に引数が4つ入る形で受け取りますが、プロミス形式では
client.fetch('http://hogehoge/', { param1: 'fuga' })
.then(function (result) {
console.log(result.error);
console.log(result.$);
console.log(result.response);
console.log(result.body);
});
というように1つの引数内にコールバック形式の時と同じ情報がまとめて入ります。
目玉機能2: cheerioオブジェクトのプロトタイプを拡張してフォームの送信やリンクのクリックをエミュレート
fetch
後のコールバックやプロミスのthen
で取得できるcheerioオブジェクトに以下のメソッドを独自拡張として実装しました。
$.documentInfo()
取得したWEBページに関する情報(URLとエンコーディング)を取得できます。
client.fetch('http://hogehoge/', function (err, $, res, body) {
var docInfo = $.documentInfo();
console.log(docInfo.url); // http://hogehoge/
console.log(docInfo.encoding); // 'utf-8'
});
$(link-element).click([ callback ])
a
タグでのみ使用できます。
href
属性に指定されているURLと取得したページのURLを組み合わせて移動先のURLを作成し、fetch()
を実行します。fetch()
と同様に引数のcallback
関数の有無でコールバック形式とプロミス形式の指定を切り替えられます。
client.fetch('http://hogehoge/')
.then(function (result) {
// id="login"の子のリンクをクリック(プロミス形式)
return result.$('#login a').click();
})
.then(function (result) {
// クリックした先のURL取得後の処理
});
javascriptリンクや
onclick="..."
などの動的処理には対応していません。あくまでもhref
のURLに簡単にアクセスできるための機能です。
$(form-element).submit([ param, callback ])
form
タグでのみ使用できます。
指定したフォーム内に配置されているinput
やcheckbox
などのフォーム部品から送信パラメータを自動作成し、action
属性のURLにmethod
属性でフォームを送信します。こちらもcallback
関数の有無でコールバック形式とプロミス形式の指定を切り替えられます。
また、フォーム送信パラメータはparam
引数で指定した連想配列の内容で上書きできるので、利用する側ではパラメータを変更したい項目だけ指定するだけで済みます。
もちろん、cheerio-httpcliの売りである文字コードの自動判別にも対応しているので、フォームの設置されているページの文字コードに合わせたURLエンコードをして送信してくれます。
client.fetch('http://hogehoge/')
.then(function (result) {
// ユーザー名とパスワードだけ入力して、あとはフォームのデフォルト値で送信する
var loginInfo = {
user: 'guest',
pass: '12345678'
};
// name="login"フォームを送信(コールバック形式)
result.$('form[name=login]').submit(loginInfo, function (err, $, res, body) {
// フォーム送信後に移動したページ取得後の処理
});
})
cheerio-httpcliは内部でクッキーも保持するので、ログインが必要なページの取得などもsubmit()
でログイン後に巡回できるようになります。
フォームの送信サンプルはGitHubリポジトリにもいくつか用意していあるので参考にしてみてください。
ちなみにExcite翻訳の自動実行はこんな感じです。
var client = require('cheerio-httpcli');
client.fetch('http://www.excite.co.jp/world/')
.then(function (result) {
return result.$('#formTrans').submit({
auto_detect: 'on',
before: '翻訳したい文字列をうんたらかんたら'
});
})
.then(function (result) {
console.log('翻訳結果: ' + result.$('#after').val());
});
click()
やsubmit()
はスクレイピングモジュールの老舗であるPerlのWWW::Mechanize にインスパイアされています。
目玉機能3: ブラウザごとのUser-Agentの簡易指定機能実装
今まで特定のブラウザになりきろうとする場合は手動でheaders
にUser-Agentを指定する必要がありましたが、今バージョンから以下のようにブラウザごとのUser-Agentをセットするメソッドを実装しました。
var client = require('cheerio-httpcli');
client.setBrowser('chrome'); // GoogleChromeのUser-Agentに変更
client.setBrowser('android'); // AndroidのUser-Agentに変更
client.setBrowser('googlebot'); // GooglebotのUser-Agentに変更
ブラウザの細かいバージョンまでは指定できないので、そういった場合は今までどおり手動でheader
に指定する必要があります。
その他のバージョンアップ内容
上記に挙げた大規模な目玉機能の他にも
- クッキーの内容を簡単に確認できるようになった
- cheerioオブジェクトにパースする前の生HTMLも取得できるようになった
- リクエスト時にRefererを自動設定するオプション追加
など、色々といい感じに取り揃えていますので、Node.jsでスクレイピングという機会があった場合にはこの記事のことも思い出してもらえればうれしいのです。
それでは素敵なスクレイピングライフを