Defuddle

de·fud·dle — to remove unnecessary elements from a web page, and make it easily readable.

1 — 概要

Defuddle は、ウェブページからメインコンテンツだけを抜き出し、コメント・サイドバー・ヘッダー・フッターなどを除いた「きれいな HTML」を返す TypeScript ライブラリ。Obsidian Web Clipper 向けに書かれており、Turndown などの HTML→Markdown 変換の前段として使いやすい出力を目指している。

Browserdocument をそのまま
NodeJSDOM
CLIdefuddle parse
Readabilityより寛容

Mozilla Readability の代替として使える。違いとしては「より寛容(不確実な要素を削りすぎない)」「脚注・数式・コードブロックを統一フォーマットに」「モバイル用 CSS からノイズ要素を推測」「schema.org を含むメタデータ抽出」がある。

2 — パイプライン

メイン処理は Defuddle#parse()(同期的)と parseAsync()(ローカルで取れない場合に非同期エクストラクタへフォールバック)の 2 本。同期パースの大まかな流れは以下。

schema.org 抽出 サイト別エクストラクタ クローン + モバイルスタイル適用 メインコンテンツ検出 脚注標準化 小画像・非表示・低スコア・セレクタ除去 HTML 標準化・URL 解決
  • メインコンテンツ検出: ENTRY_POINT_ELEMENTS#post, .entry-content, article, main, #content, body など)を優先し、ContentScorer でスコアを付けて最良の要素を選ぶ。一覧ページでは「子の article が複数ある場合は親をコンテンツとする」などのヒューリスティックあり。
  • 除去: 完全一致セレクタ(広告・ナビ・コメント等)、部分一致(class/id のキーワード)、display:none / visibility:hidden / hidden クラス、スコアの低いブロック、小さい img/SVG(33px 未満など)を削除。脚注コンテナは保護。
  • リトライ: 語数が 200 未満のときは removePartialSelectors: false で再パース。50 未満のときはスコア除去と部分セレクタ除去の両方 off で再試行(一覧ページ対策)。
  • schema.org フォールバック: 抽出テキストが schema.org の text / articleBody より短い場合、DOM から該当要素を探すか、schema の文字列をそのまま content に使う。

クローン時にシャドウルートの中身をフラット化してクローン側に取り込む。カスタム要素(ハイフン付きタグ)は div に置き換えて再初期化でシャドウが復活しないようにしている。

3 — バンドル・API
バンドル用途備考
defuddleブラウザ用コア依存なし。数式は扱うが MathML↔LaTeX のフォールバックはなし。
defuddle/full数式・Markdown 強化mathml-to-latex, temml で <math> 生成。Turndown で Markdown 変換。
defuddle/nodeNode.js(JSDOM)HTML 文字列や URL からパース。full 相当の数式・Markdown 対応。peer: jsdom。

返却オブジェクト(DefuddleResponse)

content(クリーンな HTML), title, author, description, domain, favicon, image, published, site, schemaOrgData, wordCount, parseTime, metaTags。オプションで contentMarkdowndebug(contentSelector / removals)を付与できる。

4 — サイト別エクストラクタ

URL パターンに合致すると、汎用パイプラインの前にサイト専用の BaseExtractorcanExtract() / canExtractAsync() で判定し、extract() または extractAsync() で本文とメタデータを返す。同じドメインに複数登録されている場合は登録順で先にマッチしたものが使われる(例: X は XArticle → Twitter → XOembed の順)。

エクストラクタパターン例
XArticlex.com, twitter.com
Twittertwitter.com, /x.com/.*
XOembedx.com, twitter.com(oembed 利用)
Redditreddit.com, old.reddit.com, *.reddit.com
Youtubeyoutube.com, youtu.be
HackerNewsnews.ycombinator.com/item?id=*
ChatGPTchatgpt.com/(c|share)/*
Claudeclaude.ai, claude.ai/(chat|share)/*
Grokgrok.com/(chat|share)*
Geminigemini.google.com/app/*
GitHubgithub.com/*
非同期フォールバック: parseAsync() でローカルから本文が取れない場合(例: SPA で中身が空)、useAsync: true なら FxTwitter API など外部 API を叩くエクストラクタが使われる。無効にするには useAsync: false
5 — オプション・標準化
オプションデフォルト説明
debugfalseログ増量 + 返却に debug.contentSelector / removals を付与
url相対 URL 解決のベース URL
markdownfalsecontent を Markdown に変換
separateMarkdownfalsecontent は HTML のまま、contentMarkdown を別途返す
removeExactSelectorstrue広告・ナビ等の完全一致セレクタで除去
removePartialSelectorstrueclass/id の部分一致で除去
removeHiddenElementstruedisplay:none 等で非表示の要素を除去
removeLowScoringtrueスコアの低いブロックを除去
removeSmallImagestrue小さい img/SVG を除去
removeImagesfalse画像をすべて除去
standardizetrue見出し・コード・脚注・数式の標準化
contentSelectorメインコンテンツの CSS セレクタ(指定時は自動検出をスキップ)
useAsynctrueローカルで取れないとき非同期エクストラクタを試す

HTML 標準化の内容

  • 見出し: タイトルと一致する最初の H1/H2 を削除。H1→H2 に変換。見出し内のアンカーリンクは削除。
  • コードブロック: 行番号・シンタックス用クラスを整理し、言語を data-langclass="language-*" で保持。
  • 脚注: インライン参照と脚注リストを統一フォーマット(<sup id="fnref:*">, <div id="footnotes"> 内の <li class="footnote">)に変換。
  • 数式: MathJax / KaTeX などを標準 MathML に変換。full バンドルでは LaTeX 往復や <math> 生成のフォールバックあり。