LoginSignup
7
8

More than 5 years have passed since last update.

node.jsでjQueryを使ってスクレイピングする

Last updated at Posted at 2018-10-28

要約

  • 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年の遅れをキャッチアップせねば。
なんとか動作はしたが、もっといいやり方とかがあるかもしれない。

7
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
8