Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

要約

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした