动手实测 Defuddle:拆解 Obsidian 创始人的网页内容提取引擎 动手实测 Defuddle拆解 Obsidian 创始人的网页内容提取引擎本文所有内容均基于对 GitHub 源码的阅读和实际命令行测试无任何厂家供稿或转载。从一次需求说起上周在做一个知识库抓取工具时需要把网页正文提取出来转成 Markdown。用了 Mozilla 的 Readability.js发现 GitHub 上一些用 React 渲染的页面它提取不全数学公式直接丢了脚注也乱。翻了一圈替代方案发现了 Defuddle。它来自 Obsidian 创始人 Steph AngoGitHub 9K Starnpm 包周下载量持续增长。但市面上的介绍文章大多只讲「是什么」我决定直接拉源码看它到底怎么工作。安装与第一印象npminstall-gdefuddle# 或者用 npx 免安装npx defuddle parse https://stephango.com/saw--markdown跑完第一行输出干净到让我怀疑是不是只输出了个摘要When I learned to use a table saw, my teacher impressed upon me that the machine wants to cut fingers. Fear the saw! ...143 个单词没有导航栏、没有页脚、没有广告正文精准提取。90ms跑完。源码拆解它到底怎么做的拉下源码src/目录结构非常清晰src/ ├── defuddle.ts # 主入口 ├── standardize.ts # HTML 标准化 ├── metadata.ts # 元数据提取 ├── markdown.ts # Markdown 转换 ├── fetch.ts # 远程抓取 ├── frontmatter.ts # YAML 前言 ├── removals/ # 清除管道 │ ├── scoring.ts # 评分算法核心 │ ├── selectors.ts # 选择器匹配 │ ├── hidden.ts # 隐藏元素检测 │ ├── small-images.ts # 小图片过滤 │ └── content-patterns.ts ├── elements/ # 元素标准化 │ ├── headings.ts # 标题处理 │ ├── code.ts # 代码块 │ ├── footnotes.ts # 脚注 │ ├── images.ts # 图片 │ ├── math.ts # 数学公式 │ └── callouts.ts # 标注/提示框 └── extractors/ # 22 站点专用提取器 ├── bilibili.ts ├── github.ts ├── wikipedia.ts ├── reddit.ts ├── twitter.ts └── ...核心评分算法我打开removals/scoring.ts看评分逻辑核心思路不复杂遍历 DOM 树为每个容器节点打分内容密度加分p标签越多、文本越长分数越高链接密度惩罚统计区域内a标签的文本占比超过阈值大幅扣分——导航栏、标签页就是被这个筛掉的聚类取最高相邻的高分区域聚合成候选块取分数最高的作为正文通过--debug模式可以看到完整的评分过程{element:ARTICLE,selector:html body main article,score:478}实测中article标签的评分最高478 分body次之459 分main再次422 分。Defuddle 会选择最高的article作为正文区域。清除管道源码中removals/目录定义了 6 个清除阶段的管道阶段文件名作用1. 精确选择器selectors.ts匹配已知广告/社交按钮的 CSS 选择器精确删除2. 模糊选择器selectors.ts匹配关键词含 “ad”、“sidebar”、“footer” 等元素3. 隐藏元素hidden.ts移除display:none、visibility:hidden元素4. 低分内容scoring.ts评分低于阈值的块被移除5. 小图片small-images.ts移除图标、追踪像素 32px 图片6. 内容模式content-patterns.ts匹配评论区、相关文章等常见模式实测抓取 stephango.com 的日志显示35 个元素通过精确选择器删除1 个隐藏元素被移除整个过程 90ms。与 Readability.js 的关键差异读源码时发现几个设计决策上的差异1. 更宽容移除更少的不确定元素Readability 倾向于「宁可错删不可保留」而 Defuddle 的策略是「宁可保留不可错删」。这对内容提取来说是一个更安全的选择——多余的内容可以后续处理但丢失的内容无法恢复。2. 利用移动端样式辅助判断源码里hidden.ts不仅检查display:none还利用移动端响应式布局中隐藏的元素来推断哪些部分是「次要的」。3. 22 站点专用提取器这是 Readability 没有的设计。src/extractors/目录下为每个平台写了专门的提取逻辑提取器目标平台特殊处理bilibili.tsB 站视频信息、弹幕元数据github.tsGitHubREADME、Issues、PR 正文wikipedia.ts维基百科信息框、目录结构reddit.tsReddit帖子评论层级twitter.tsX/Twitter推文线程、媒体卡片medium.tsMedium自定义嵌入元素chatgpt.tsChatGPT对话格式claude.tsClaude对话格式4. 异步降级当本地 HTML 提取不到内容比如客户端渲染的 SPAparseAsync()会尝试从第三方 API 获取内容。默认开启可通过useAsync: false关闭。三套构建产物源码里package.json定义了三个入口{exports:{.:dist/index.js,// Core仅 HTML无依赖./full:dist/index.full.js,// Full含 Markdown 数学公式./node:dist/node.js// Node含服务端 DOM 解析}}产物大小能力Core~20KB仅 HTML 输出无外部依赖Full~40KBHTML Markdown 数学公式转换Node~45KB服务端 DOM 完整能力这种按需加载的设计让浏览器插件Core和笔记工具Full用同一套代码但不同体积。实测对比我拿同一篇博客stephango.com/saw测试了三种提取方式指标Readability.jsDefuddle提取字数139143处理时间112ms90ms丢失内容无无元数据提取标题作者标题作者描述域名语言字数调试模式无有输出完整决策链数学公式丢失转换为 MathML脚注格式混乱标准化sup引用当然这只是一个简单页面的测试。对于复杂页面评论区、多级导航、JavaScript 渲染差异会更明显。适用场景翻完源码、跑完测试后我认为 Defuddle 最适合以下场景1. Obsidian Web Clipper 用户这是 Defuddle 的原生场景——剪藏网页到 Obsidian 笔记库保留 Markdown 格式和 YAML 前言。2. RSS 全文抓取很多 RSS 源只有摘要用 Defuddle 在服务端提取全文后再推送比直接抓取原始 HTML 干净得多。3. AI 上下文准备把网页喂给 LLM 之前先用 Defuddle 剔除广告和导航节省 token。实测 143 词的正文原始 HTML 大约 3000 tokenDefuddle 处理后只剩下 1/10。4. 知识库构建自建知识库时Defuddle 的--frontmatter选项可以直接输出带 YAML 元数据的 Markdown天然适合 Obsidian、Logseq 等工具。不足根据源码阅读和测试几个尚未解决的问题中英文混合页面对中文内容的评分不如英文准确链接密度惩罚对中文链接效果差SPA 页面依赖parseAsync()的第三方 API 降级但 API 可用性不稳定评论区提取默认会移除评论区即使includeReplies: true部分平台如 Disqus仍无法提取总结Defuddle 不是 Readability 的简单复刻而是一次从 Obsidian 生态需求出发的重新设计。它的核心优势在于管道式清除架构每个阶段独立可调22 站点专用提取器覆盖主流平台三套构建产物按场景选择体积完整的调试模式开发者可以精确追踪每次提取的决策过程如果你正在做网页内容提取相关的工作无论是笔记工具、RSS 阅读器还是 AI 数据管道Defuddle 都值得认真评估。项目地址https://github.com/kepano/defuddlenpm 包npm install defuddleCLI 使用npx defuddle parse url --markdown本文所有结论均基于对 GitHub 源码commit 最新的分析和实际命令行测试。