読者です 読者をやめる 読者になる 読者になる

プログラマでありたい

おっさんになっても、プログラマでありつづけたい

Node.jsのWebスクレイピングモジュール 『cheerio-httpcli』のサンプルソースを読み解く

 不定期連載、Node.jsのcheerio-httpcliによるWebスクレイピング講座です。今回は具体的なcheerio-httpcliの使い方をみていきましょう。cheerio-httpcliが公開されてるGithubリポジトリには、豊富なサンプルがあります。サンプル例としては下記の通りで2016年5月現在で11本もあり、これを読んでると一通りの使い方が解ってきます。ということで、サンプル読みながら紹介してみます。

2ちゃんねる検索サンプル



 1つ目のサンプルは、2ちゃんねる検索のサンプルです。特定のワードを入れて、その検索結果を表示するという形です。

まず2ちゃんねるの検索部分がどうなっているかです。サイト上は、下記のとおりです。シンプルですね。
f:id:dkfj:20160514164719p:plain

 HTMLのソースは次のような形になっています。

<div style="display: inline; margin-left: 2.0em;">
	<form method="get" action="http://find.2ch.net/" style="display: inline;">
		<input type="text" name="q" value="" style="width: 16em; height: 1.00em;" id="fulltext-keyword">
		<input type="hidden" name="ita" value="">
		<button type="submit" value="">【新】全文検索</button>
	</form>
	<form method="get" action="http://dig.2ch.net/" accept-charset="UTF-8" style="display: inline;">
		<input type="text" name="keywords" value="" style="width: 16em; height: 1.00em; display: none;" id="thread-keyword">
		<input type="hidden" name="Bbs" value="">
		<button type="submit" value="">スレタイ検索</button>
	</form>
</div>

※見辛いので、適当にインデントをつけました。

 それでは、cheerio-httpcliのソースを見てみましょう。まず検索部分です。

var word = 'ぬるぽ';


var client = require('../index');

client.fetch('http://www.2ch.net/')
.then(function (result) {
  return result.$('form').eq(0).submit({
    q: word
  });
})

http://www.2ch.net/のフォームの一つ目に、『ぬるぽ』という単語を設定してサブミットボタンを押しています。HTMLのソースをみると、【新】全文検索とスレタイ検索があるので、ここで利用しているのは【新】全文検索の方ですね。検索ワードを入れるinput type="text"の名前が、qなのでそこにwordを入れています。なお、requireの部分は、Git等でソースとサンプルをまとめてダウンロードして展開した以外は、下記のように指定します。

var client = require('cheerio-httpcli');

次に取得した結果に対する処理です。先に実行結果をみてみましょう。検索結果の中から、title,url,timestampをJSON形式で出力しています。

[ { title: '【DMM/角川】艦隊これくしょん〜艦これ〜6208隻目 [無断転載禁止]©2ch.net',
    url: 'http://wc2014.2ch.net/test/read.cgi/gameswf/1463111858/294',
    timestamp: '2016-05-13 21:36:28.08' },

〜中略

  { title: '【テレ玉】TVSアニメ専門スレ45【テレビ埼玉】 [転載禁止]©2ch.net',
    url: 'http://yomogi.2ch.net/test/read.cgi/asaloon/1443865196/142',
    timestamp: '2016-05-06 21:01:38.4' },
  { title: '歴史秘話ヒストリア「愛と悲しみの大奥物語」★2©2ch.net',
    url: 'http://nhk.2ch.net/test/read.cgi/livenhk/1462533366/167',
    timestamp: '2016-05-06 20:35:22.01' } ]
終了します

 これを出力しているコードは下記のとおりです。
htmlのソースと比べると、classがboxのdivタグの中から、aタグを抽出しているようです。

f:id:dkfj:20160514215641p:plain

.then(function (result) {
  var $ = result.$;
  var results = [];
  $('.box').each(function () {
    var $a = $(this).find('a').eq(0);
    results.push({
      title: $a.text().trim(),
      url: $a.attr('href'),
      timestamp: $(this).find('.timestamp').text().trim()
    });
  });
  console.info(results);
})

 aタグの抽出の部分が、黒魔術に見えます。

var $a = $(this).find('a').eq(0);

findでaタグを指定して、その後のeqで絞り込んでいます。cheerioのメソッド説明によると、eqは下記のとおりです。

Reduce the set of matched elements to the one at the specified index. Use .eq(-i) to count backwards from the last selected element.

マッチした一つ目の要素のみ取得しているということのようです。使いこなすと便利そうですね。

 2ちゃんねる検索のサンプルで、検索ワードワードを入れて結果一覧から取得するというスクレイピングの基本的な動作が解りました。特に検索結果からの抽出の部分は参考になります。

要素の多段絞込



 後はサンプルの中から、使いそうな記述の仕方をピックアップします。まず、要素の絞込の仕方です。これは、例えば、特定のタグの下のものを選んで、再度絞り込むというやり方です。一度で抽出するという方法もありますが、2回に別けた方がよい場合もあります。ということで、sync.jsの例を見ていきましょう。sync.jsは同期リクエストのサンプルですが、要素の絞込があったのでピックアップします。

$('.b_algo').each(function () {
    // 各検索結果のタイトル部分とURL、概要を取得
    var $h2 = $(this).find('h2');
    var url = $h2.find('a').attr('href');
〜省略〜

 ソースを見ると解るのですが、まずh2タグを抽出し変数に代入しています。次に、代入した変数から再度絞り込んでいます。こう書くと解りやすいですね。この辺りの書き方が、スクレイピングしていて個々人の癖が出る部分だと思います。

不要な情報の削除



 次はタグの中から不要な情報の削除です。私は、あまりこういった方法を使わないのですが、読んでて成る程なぁと思いました。上手く使えば、分岐処理とかが不要になるので効率的になります。この例は、hatena-keyword.jsというはてなのキーワード抽出のサンプルにあります。

var $ = result.$;
  $('#updatekeywords ul li .name').each(function () {
    // NEW!は邪魔なので削除
    $('.new', $(this)).remove();
    console.info('* ' + $(this).text());
  });

Cookie情報の取得



 ログイン後のセッションを維持するために、Cookieを使う場合が多いです。スクレイピングで、ログイン後のページを参照する場合は、ログインしてCookieを取得するといったことが必要です。そのサンプルも、facebook.jsにあります。

client.fetch('http://www.facebook.com/')
.then(function (result) {
  console.info('ログインフォームを送信します');
  return result.$('#login_form').submit({
    email: username,
    pass: password
  });
})
.then(function (result) {
  console.info('クッキー', result.response.cookies);
  console.info('ユーザー名を取得します');
  console.info(result.$('._2dpb').text());
})

感想



 cheerio-httpcliは、サンプルが充実しています。どのサンプルもシンプルでありながら、スクレイピングに必要な要素が散りばめられています。今回じっくりと読むことで、だいたいの使い方が理解できました。ということで、ぜひ読んで動かしてみてください。

シリーズ目次:
Node.jsでスクレイピングするならば
AWS Lambdaでcheerio-httpcliを実行する
Node.jsのWebスクレイピングモジュール 『cheerio-httpcli』の使い方その1 cheerioでhtmlの要素指定

See Also:
『Rubyによるクローラー開発技法』を書きました
アプリケーションエンジニア向けのAWS本を書きました
『Amazon Web Services パターン別構築・運用ガイド』を書きました