node.jsでデータ収集のためのWebスクレイピングを行う。
Webスクレイピングの流れというのはだいたい決まっていて、
- WebページにアクセスしてHTMLを取得する
- 取得したHTMLの中から必要なデータを抽出する
- 抽出したデータを保存する
の3段階となる。
通常、Webスクレイピングが必要となるのは
- データ取得用のAPIが提供されていない
- 必要なデータが1ページに収まらず多くのページにまたがっており、手作業でコピペしていくのが難しい
場合で、そのうちWebスクレイピングが可能なのは
- 対象の各ページが同じフォーマットになっており、パターン化された処理で必要なデータを取得できる
場合となる。
例えば「ページングされた検索結果画面」とか
「同じフォーマットでHTMLが書かれたたくさんの商品詳細画面」
なんかがスクレイピングでデータ取得しやすい。
前提として、
Webスクレイピングは手作業 でWebページにアクセスしてコピペする、
という作業をプログラムが自動で行うようなもので、手動か自動かに関わらず
対象のデータを取得したり保存したりする権利なり許可なりが必要となる。
また、プログラムを使用する場合は
手動とちがってWebページに一度に大量のリクエストを行なうことが可能なので、
サーバに負荷をかけないように注意する必要がある。
node.jsでWebスクレイピングする場合、いくつか使えるライブラリがあるけど
「cheerio-httpcli」がつまづきにくくて良いと思う。
puppeteerの方が人気ありそうだけど、自分のWindows環境ではうまく動かなかった。
取得したデータをデータベースに保存する場合、自分は「knex」を使ってる。
Knex.js - A SQL Query Builder for Javascript
あとは日付処理に便利な「moment」と
リストやマップの処理に便利な「lodash」を必要に応じてインポートしておく。
だいたい下記のような形でスクリプトを始める:
// cheerio-httpcliでhttpsアクセスするための設定 process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // 必要なライブラリをインポート const httpClient = require('cheerio-httpcli'); const knexLib = require('knex'); const _ = require('lodash'); const moment = require('moment'); // knexで生成するDB情報保存用ファイルのインポート const knexfile = require('./knexfile')['development']; // リクエストごとに一定時間空けるためのsleep関数 async function sleep(waitMillSeconds) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, waitMillSeconds); }); }
メイン処理は下記のような感じ。 例としてYahoo!天気の地震情報を数ページ分取得してみる。
async function main() { // ベースURL。リクエストごとに変わらない部分 const baseUrl = 'https://typhoon.yahoo.co.jp/weather/jp/earthquake/list/'; // DBアクセス用のknexオブジェクトを生成 const knex = knexLib({ client: knexfile.client, connection: knexfile.connection }); // リクエストごとに3秒待つための設定 const waitMillSeconds = 3000; // 取得件数が500件を超えたら終了する設定 const maxB = 500; // 1ページあたりの件数設定。 const intervalB = 100; // 1ページ目から開始 let currentB = 1; while (currentB < maxB) { console.log('b = ', currentB); // HTMLデータを取得 const result = await httpClient.fetch(baseUrl, {sort: 1, key: 1, b: currentB}); const $ = result.$; const logList = []; // HTML内の表の行データを取得して変換してリストに入れる $('tr', '#eqhist').filter(function () { return $(this).attr('bgcolor') === '#ffffff'; }).each(function () { const row = $(this); const cellList = $('td', row).map(function () { return $(this).text(); }).get(); const rowId = $('a', $('td', row).first()).attr('href'); const cellMap = _.zipObject(['happened', 'announced', 'place', 'magnitude', 'intensity'], cellList); cellMap.happened = moment(cellMap.happened, 'YYYY年M月D日 H時m分ごろ').toDate(); cellMap.id = rowId; if (cellMap.magnitude !== '---') { logList.push(cellMap); } }); // リストに入れたデータをデータベースに登録する for (const log of logList) { await knex('test_logs').insert(log); } currentB += intervalB; // 指定ミリ秒数待機 await sleep(waitMillSeconds); } await knex.destroy(); } main();
このスクリプトを流すと、取得したデータがデータベースに入るので、
あとはSQLで集計してみたりグラフ表示に使ってみたりすることができる。
上記スクリプトはinsertにしか対応してないので、2回実行すると落ちる。
ちゃんと作るなら既存データは無視する処理を入れたりして、
新しいデータが増えても新しいデータの分だけ取得するようにする。
JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック
- 作者: クジラ飛行机
- 出版社/メーカー: ソシム
- 発売日: 2015/08/31
- メディア: 単行本
- この商品を含むブログ (2件) を見る