JavaScript でスクレイピング

インターネッツが普及していない実家の父親から、最近ちょいちょい某サイトのアクセス状況を教えてって頼まれます。PDF で公開されている自著論文のアクセス数・ダウンロード数を知りたいそうです。まあ一種のエゴサーチですね。父親はガラケーしか持ってないので、サイトの HTML から抜き出した情報をメールに貼って送るのですが、定期的になってきたので、自動化したいなあと思いました。

JavaScript で使えるようなスクレイピングのライブラリないかなとぐぐったら cheerio-httpcli というのを見つけました。

qiita.com

これ使うと jQuery っぽく DOM 取得できて楽チンです。Promise とかも使えてコードがシンプルになります。

目的のサイトでは、dl 要素 (Definition List) 使ってペーパーのタイトルを dt 要素で、著者やアクセス情報を dd 要素 で取得できます。なので、タイトルとアクセス情報を dl 毎に取得して、zip しちゃえば OK.

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

var p = client.fetch('http://target/url');

p.then(function (result) {

    var titles = [];
    result.$('dt').each(function () {
        // タイトル部分取得
        titles.push(result.$(this).text().replace(/(^\s+)|(\s+$)/g, ''));
    });

    var accesses = [];
    var downloads = [];
    result.$('dd').each(function () {
        // アクセス情報取得
        var dd = result.$(this).text();
        var start1 = dd.lastIndexOf('アクセス件数 : ');
        var start2 = dd.lastIndexOf('ダウンロード件数 : ');
        var end = dd.lastIndexOf(' 件') + 2;
        if (start1 != -1 && start2 != -1) {
            accesses.push('アクセス数: ' +  dd.substring(start1 + 9, start2 - 2));
            downloads.push('ダウンロード数: ' + dd.substring(start2 + 11, end));
        }
    });

    _.chain(_.zip(titles, accesses, downloads))
    .each(function(elm) {
        console.log(elm[0]);
        console.log(elm[1]);
        console.log(elm[2]);
        console.log();
    });
});

p.catch(function(err) {
    console.log(err);
});

p.finally(function() {
// console.log('done');
});

dl 要素なんて普段使わないんで知りませんでした。構造化されててスバラシイ。さすがに学術系サイトです。

スクレイピングって大昔に HTML パース処理を C++ で書いて以来だけど、随分と楽になりました。Ruby とか Python とかあまり手に馴染んでないのでライトウェイト力が低いのですが、node.js 周りのエコシステムが発展しているおかげで JavaScript を汎用目的に使えるようになってます(スクレイピングは JavaScript にマッチした題材ではありますが)。

Nodemailer とか使うとメール送信も自動化できそうです。

github.com