poyopoyo0のブログ

poyopoyoのブログ

Web 関連のメモ書きブログです。

【Node.js】UTF8以外のページをlibxmljsでスクレイピングするには

【追記】metaタグのcharsetの置換について、当初はreplaceメソッドを使用していましたが、replaceは最初に一致した文字列だけを置換するので、metaタグのcharsetの置換が失敗することがあることが分かりました。そのため、置換の方法をsplitとjoinメソッドを用いる全置換に変更しました。

libxmljsスクレイピングする際、対象ページの文字コードがUTF8以外だとエラーが発生してしまう。そこで、取得したページをUTF8に変換するために

trycatchand.blogspot.com

を参考に

const request = require('request-promise');
const encoding = require('encoding-japanese');
const libxmljs = require('libxmljs');

const body = await (request.defaults({
  encoding: null
})).get(url);

const unicodeArr = encoding.convert(body, {
  to: 'UNICODE'
});

const htmlDoc = libxmljs.parseHtml(body);

としてみた。ところが、このコードの直後に

console.log(htmlDoc);

とすると

Error: encoding not supported EUC-JP

というエラーが発生してしまう。どうやらlibxmljsでは、実際の文字コードではなく、metaタグのcharsetを読み取って文字コードを判断してしまっているみたい。そこで encoding-japanese (=encoding.js)

github.com

文字コード判別機能 encoding.detect() を用いて

const request = require('request-promise');
const encoding = require('encoding-japanese');
const libxmljs = require('libxmljs');

const body = await (request.defaults({
  encoding: null
})).get(url);

const detected = encoding.detect(body);
const unicodeArr = encoding.convert(body, {
  to: 'UNICODE'
});

let strToUtf8 = '';
if (detected === 'UTF8') {
  strToUtf8 = encoding.codeToString(unicodeArr);
} else if (detected === 'EUCJP') {
  strToUtf8 = (encoding.codeToString(unicodeArr)).split('EUC-JP').join('UTF-8');
} else if (detected === 'SJIS') {
  strToUtf8 = (encoding.codeToString(unicodeArr)).split('Shift_JIS').join('UTF-8');
};

const htmlDoc = libxmljs.parseHtml(strToUtf8);

としたところ、 htmlDoc.get('/html/head/title') のようにXPathで要素を取得できるようになった。

本当はmetaタグのcharsetに絞って置換してやらないと、charset以外の文字列を置換してしまう可能性があるのだけど、手軽にcharsetのみを置換する方法が分からないので、取り敢えず全置換してしまってます。どなたか、charsetをピンポイントで置換する方法をご存知でしたらお教え下さい💦

ちなみに

const unicodeArr = encoding.convert(body, {
  to: 'UNICODE'
});

のところは

const unicodeArr = encoding.convert(body, {
  to: 'UTF8'
});

としてしまうと文字化けしてしまい、スクレイピングができなくなるので注意が必要。

encoding-japanese (=encoding.js) は自動で文字コードを判断したり、目的の文字コードに変換してくれるので、ほんと手軽で便利。贅沢を言うと、日本語以外の文字コードにも対応してくれるライブラリが存在すると嬉しいのだけど、今どきUTF8以外のWebページは滅多に存在しないから、あんまり需要が無いかなあ…。