【Node.js】UTF8以外のページをlibxmljsでスクレイピングするには
【追記】metaタグのcharsetの置換について、当初はreplaceメソッドを使用していましたが、replaceは最初に一致した文字列だけを置換するので、metaタグのcharsetの置換が失敗することがあることが分かりました。そのため、置換の方法をsplitとjoinメソッドを用いる全置換に変更しました。
libxmljs でスクレイピングする際、対象ページの文字コードがUTF8以外だとエラーが発生してしまう。そこで、取得したページをUTF8に変換するために
を参考に
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)
の文字コード判別機能 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ページは滅多に存在しないから、あんまり需要が無いかなあ…。