要約
-
Node.js
でスクレイピングする -
jQuery
を利用する -
Promise
,async/await
で同期処理にする。なお同期処理に悪戦苦闘した。難しい・・・
動機
- 自然言語の機械学習用データ集めのため、スクレイピングする必要があった。
- Pythonでばかりコーディングするのも芸がない。DOMを操作するんだから、jQueryを使ってやろう。
- Node.jsは前から興味があったけど、初めて触る。JSのお勉強がてらやってみるか。(※ワタクシのJavaScript知識は10年以上前で止まっていましたので。プロミス?なにそれ、消費者金融?って状態でした)
やりたいこと
- とある、Webサイトの記事のURLを取得したい。
- Webサイトには一覧ページがあり、1ページに50件程度の記事一覧がある。ページはパラメータで変えられる(
https://hoge?page=2
のような形で) - 記事一覧の各ページから、記事本文のURLと投稿日、記事タイトル等を取得する
やったこと
必要なライブラリのインストール
$ node -v # => v8.11.1
$ npm init
$ npm install jquery --save
$ npm install jsdom --asve
jqueryは趣旨のとおり。jsdomはjqueryをブラウザなしで利用するのに必要だった。
コーディング
ポイントは
- https.getは非同期で動作するので、(同期させたいなら)
Promise
オブジェクトを返す関数でラップしてやる(get_entry_titles
) - GETで取得したHTMLをそのままjqueryへ突っ込むと windowがない といってエラーになるで、jsdomを利用する
- 同期したいところを
async/await
を使う。
const https = require("https"); //GETメソッドでリクエストするために
const util = require("util"); //format関数を利用するために
const jsdom = require("jsdom"); //jqueryをつかうために
const jquery = require("jquery"); //趣旨のとおり、DOM走査するために
// 記事一覧のページ
const URL = "https://hoge?page=%s";
// スクレイピングする処理(プロミスオブジェクトを返す)
// 最近はfunctionで定義しないで、無名関数を変数に代入にするのがモダンなの?
function get_entry_titles(page){
// プロミスをnewする
return new Promise((resolve) => {
// httpsリクエスト
const req = https.get(util.format(URL,page), (res) =>{
// このあたりは公式のドキュメント参考に
var html = "";
res.setEncoding('utf8');
res.on('data',(chunk) => html += chunk);
res.on('end',() => {
// windowがないとjQueryが動かないそうで、以下の様にする模様
const dom = new jsdom.JSDOM(html);
const $ = (jquery)(dom.window);
//スクレイプしたい処理をjQueryで記述
$(".foo").each((index,element) =>{
// スクレイプ結果を出力
console.log(util.format(
"%s\t%s\t%s"),
page, //ページNo.
$(element).find(".bar").attr("href"), //リンクURL
$(element).find(".hoge").text()); //タイトル
});
//ここで処理完了なのでresolve。これにたどり着くのに悪戦苦闘した
resolve();
});
});
req.end();
});
}
// メインのループ処理(ページ数文繰り返し) async関数にする
async function main(){
// 1〜9ページ繰り返す
for (var page=1; page < 10 ; page++){
// awaitを付けて、1ページづづ同期する。じゃないとページ数だけが先にインクリメントされてしまう。
await get_entry_titles(page);
}
}
// 処理エントリ
main();
最後に
うむ、JavaScript、もう一度勉強しないと。10年の遅れをキャッチアップせねば。
なんとか動作はしたが、もっといいやり方とかがあるかもしれない。