歩いたら休め

なんでこんな模様をしているのですか?

【node.js】文字コードを推定してurlからタイトルを取得する

最近、以前Pythonで実装したクローラーをnode.jsに移行しようとしています。将来的にpuppeteerを使ったスクレイピングが必要になったときにnode.jsで詰まりたくないのと、Promiseasync/awaitを使ったプログラミングのサンプルとして遊んでみたかったのがその理由です。

ただ、文字コードの判定と変換の実装で多少詰まってしまったのでメモ書きしておきます。

const rp = require('request-promise');
const cheerio = require('cheerio');
const jschardet = require('jschardet');
const iconv = require('iconv-lite');


const sampleFetch = async (url) => {
    return await rp.get({
        "uri": url,
        "followAllRedirects": true,
        // encodingにnullを指定することで文字列ではなくBufferとして読み込む
        "encoding": null,
        "transform": (body, response, _) => {
            // jschardetで文字コードを推定し、iconvで変換する
            const encoding = jschardet.detect(body).encoding;
            const decoded = iconv.decode(body, enc);

            // cheerioでタイトルを取得する
            const $ = cheerio.load(decoded);
            return $('title').text().trim();
        },
        "headers": {
            'User-Agent': 'Sample-Crawler'
        }
    });
}

これらのページを参考にしました。

qiita.com

qiita.com

テストしてみましょう。

また、個人的に使い慣れているRubyRSpecに近い使い勝手の、jasmineというライブラリを利用しています。itのコールバックにdoneが入り、それを非同期処理のテストも簡単に書けるようです。

rewireというライブラリを使うことで、exportしていないメソッドもテストできます。

const rewire = require('rewire');
const samplecode = rewire('./samplecode');


describe("sampleFetchのテスト", () => {
    const sampleFetch = samplecode.__get__("sampleFetch");

    it("UTF-8のページのとき", (done) => {
        sampleFetch("https://kiito.hatenablog.com/").then(response => {
            expect(response).toEqual("歩いたら休め");
            done();
        });
    });

    it("SHIFT_JISのページの時", (done) => {
        sampleFetch("https://monoist.atmarkit.co.jp/mn/articles/1806/12/news057.html").then(response => {
            expect(response).toEqual("日立は「カンパニー」から「ビジネスユニット」へ、成長のエンジンは「Lumada」 - MONOist(モノイスト)");
            done();
        });
    });
});