HTMLパーサーが欲しい
GASでスクレイピングすると誰もが思うこと、「HTMLパーサーが…欲しい…!!」
正規表現でもなんとかなるといえばなるけど柔軟性に欠けるし保守性も低い。
よく紹介されるのはParserだけど個人的には「コレ使うなら正規表現でも変わらなくね?」と思ってしまうし、同一タグのネストを解決するのが大変。
ならnodejsから移植すれば良いじゃない。
というわけでライブラリを作りました。下記IDをライブラリ検索で利用できます。
スクリプトID: 1JTLPVXGW6Pq4zSAV5ED9XrWOPydVywumQZSOWN7l6KJ0UtWxtX3RbYO6
GitHub: https://github.com/kairi003/gas-html-parser
使い方
本ライブラリはほぼnode-html-parserそのままです。作ったとか言えないレベル。
細かい利用方法は本家のドキュメントを確認していただくとして、以下のように使えます。
const html = `<body>
<ul id="fruits">
<li class="apple">Apple</li>
<li class="orange">Orange</li>
<li class="pear">Pear</li>
</ul>
</body>`;
const dom = HtmlParser.parse(html);
console.log(dom.querySelector('#id .pear').textContent);
ちなみになんでnode-html-parserなのかというとAPIがブラウザDOMに近い(同じではない)のと軽いからです。jqueryが好きならcheerioのほうがいいかも。jsdomは無理そうでした。
作り方
GitHub: https://github.com/kairi003/gas-html-parser
node-html-parserをimportするだけのindex.jsをwebpackでバンドルしてます。
またGASライブラリとして使う都合上、ライブラリのグローバルとnode-html-parserのグローバルを一致させたかったのでglobalThis
にObject.assign
してます。
Object.assign(globalThis, require('node-html-parser'));
普通に使う分には以下のようにexportしてwebpackの設定でoutput.library: "HtmlParser"
などを指定したほうが良いでしょう。
module.exports(require('node-html-parser'));
ちなみにwebpackでちょっと困ったこととして、developmentモードでは動くのにproductionでは動かないという自体に遭遇しました。
これはminimize時にオブジェクトキーに非ASCII文字がクォーテーション無しに生成されて一部がSyntaxErrorになっていたこと ({ℵ: "aleph",...}
←こんなの) が原因で、次のようにterserのオプションを指定することで解決しました。
module.exports = {
...,
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: { output: { ascii_only: true } }
})
],
}
}
感想
割と手軽にできて驚きでした。なんで普及してないんだろう。