🤖
本文抽出ライブラリの readability.js を DOM 非依存に書き直す(WIP)
現状、最低限動いてはいるけどガバガバ
Readability.js とは
HTML から本文を抽出するライブラリ。
元々はArc90によって開発され、現在はMozillaのFirefox Reader View に使われている。
AIモデルではなくルールベースだが、かなり精度がでる。
自分はこれを AI に食わせるサマリを作るのに愛用していたのだが...
DOM API 依存と Cloudflare でJSDOMが動かない問題
DOM API に依存しているので、 jsdom に依存してしまう。
import { JSDOM } from "jsdom";
import { Readability } from "@mozilla/readability";
import html2md from "html-to-md";
function getExtractContent(htmlContent: string) {
const doc = new JSDOM(htmlContent);
const article = new Readability(doc.window.document).parse();
if (!article) {
throw new Error("Article not found");
}
return html2md(article!.content);
}
普段は気にしないのだが、これによって cloudflare workers にデプロイできなかった。
mastra の MCP サーバーに載せて cloudflare deployer を使おうとしたが、駄目。
書き直した
とりあえずコアロジックだけDOM非依存して、使えるようにした。一旦は htmlparser2 を使っている。
npm install --save @mizchi/readability htmlparser2 html-to-md
import { toHTML, extract } from "@mizchi/readability";
import html2md from "html-to-md";
const url = "https://zenn.dev/mizchi/articles/ts-using-sampling-logger";
const html = await fetch(url).then((res) => res.text());
const extracted = extract(html, { charThreshold: 100 });
// 結果を表示
console.log(`Title: ${extracted.title}`);
console.log(`Author: ${extracted.byline}`);
if (!extracted.root) {
process.exit(1);
}
const htmlContent = toHTML(extracted.root);
const md = html2md(htmlContent);
console.log(md);
API が違うが、テキスト密度のコアロジックを移植したつもりではある。
出力例
$ pnpm tsx examples/run.ts
Title: TS の using でプリントデバッグを確率的にサンプリングして出力する
Author: null
pageType: article
[
AI](/topics/ai)[
TypeScript](/topics/typescript)[
tech](/tech-or-idea)
スコープ単位でログをサンプリングする。
```
{
using log = createSampleLog<string>(5);
log("a");
log("b");
// スコープを抜けるときに最大5件サンプリングされて表示される
}
```
見てわかるが html-to-md のせいか自分のせいか不明だがリンクが壊れている。まあAI向けには多少壊れてても使えなくはないのだが...
書き直した手順
Gemini 2.5 に食わせて、コアロジックを解釈させた。
自分で htmlparser2 で VDOM 的な構造体を決めて、そこにつなぎ込むのを前提に、AIに元実装でコードを読ませながら翻訳させた。
元コードが3600行ぐらいだが、Gemini 2.5 のコンテキストウィンドウが大きくすんなり言った。
とはいえ実際読んでみると、2006 年のベースらしく HTML5 依然のコードが対象になってるので、いらない仕様を自分で判断して捨てた。
もう少しちゃんとやる。
Discussion