trafilatura 是一款不依赖浏览器的 Python 内容提取器。在本文标注的测试样本里,它唯一能在同类 HTML 提取质量上正面硬刚的对手是 Mozilla Readability:trafilatura 在 17 个已标注样式噪音单元里漏掉了 1 个,而 Readability 漏掉了 5 个。基于浏览器的工具只在部署体积对比里出现,本文并没有在这些页面上对它们做质量测试。

它的核心工作就是提取正文:输入 HTML,输出面向文章的文本和元数据,并通过启发式规则过滤页面外围内容。它可以把结果序列化为 JSON 和其他格式,但不会生成你自定义的表格字段结构,也不会输出重复的、带类型的目录记录。本文测试的是 2.1.0 版本。它不会执行 JavaScript,而和 Readability 的对比也说明,清理力度和内容保留之间本来就有取舍,并不是某一方全面碾压另一方。
trafilatura 的定位,以及它明确不做什么
trafilatura 自称是一个用于在网页上采集文本和元数据的 Python 与命令行工具——涵盖爬取、抓取、提取——并支持输出 CSV、JSON、HTML、Markdown、TXT 或 XML。这个说法范围很大,但真正发挥作用的部分其实更窄、更直接:它接收一个 HTML 文档,然后返回正文内容,把页面边角的杂项信息剥离掉。

先建立一个心智模型,因为它能解释它的优势,也能解释它的边界。大多数你拿去抓网页的爬虫都依赖选择器——你会告诉它“抓取 class 为 product-price 的元素”,它就把那个位置上的内容返回给你。trafilatura 的工作方式正好相反。它会通读整份文档,然后通过内容启发式规则判断哪些区块是真正的文章,哪些只是页面噪音,而不是依赖你写好的选择器。这也是它不需要为每个站点单独配置清洗规则的原因。与此同时,这也正是它无法直接给你结构化目录的原因:它没有 schema,没有类型化行数据,只有“这里是有意义的文本”。它是提取器,不是你按字段点选的解析器。
它也不需要单独下载浏览器运行时。依赖树里包含像 lxml 这样的跨平台 wheel,因此“无需浏览器下载”不应被误解为只用源码安装,或者完全不含本地二进制组件。
安装体验:依赖体积小,不需要浏览器安装
在 Python 3.14 的全新虚拟环境里执行 pip install trafilatura 时,共安装了 17 个包;根据 pip-install-trafilatura.log,记录到的最大 wheel 是 lxml,大小 8.6 MB。整个过程不需要额外安装浏览器,也不需要安装后再跑浏览器初始化命令。
横向比较一下规模:同一研究基线里的 Scrapling 套件为了让抓取器正常工作,前后执行了四次单独的 pip 安装命令,最后装了 26 个包,其中有两个是 42.2 MB 的 Playwright driver wheel(tools/scrapling/artifacts/logs/pip-install-scrapling.log)——这还没算浏览器二进制文件的下载。Crawlee 套件中,单独下载 Chromium 的体积约为 81.7 MiB(tools/crawlee/research-materials.md)。这些工具承担的任务更重,所以这不是能力上的公平较量;这里只是反映你的磁盘和 CI 缓存会看到什么。另一个好消息是,我拿到的 pip 版本就是 2.1.0,也等于当前发布版,因此下面的数据没有“你测的是旧版本”的时间偏差问题。
实测:提取器实际返回了什么

我用两类样本去测它:一类是它擅长的场景,一类是它的边界场景,原始输出都保存在benchmark 仓库里。文章测试样本最让我信服。
我取了一个本地文章样本,在真实正文外面包了一层噪音:登录提示、“Subscribe” 弹窗、导航链接、版权页脚——这些都是朴素爬虫很容易一并抓下来的典型页面杂项。trafilatura 返回了标题 + 3/3 段正文,而且所有这些噪音标记——Login、Subscribe、Copyright——都没有出现在结果里。除了正文文本,它还正确提取了页面元数据里的作者和日期。完整结果分别保存在 .txt、.md 和 .json 中,路径是 results/local_article.json。
同一份 HTML 保存成了纯文本、Markdown 和 JSON。文章并没有证明这三种格式是一次调用同时产出的;它们只是同一条提取路径的不同序列化方式。这里的 JSON 包装的是提取出的文本和元数据,不是你自定义的重复记录集合。
下面把第一次运行的结果放在一起,方便你直接核对,而不是只听我说:
| 测试样本 | 返回内容 | 耗时 | 证据 |
|---|---|---|---|
| 本地文章,外层包有导航 / 登录 / 订阅 / 版权内容 | 标题 + 3/3 段正文,零噪音区块泄漏,作者为 Thunderbit Research Lab,日期为 2026-07-09 | 0.007 秒 | local_article.json |
| 本地产品目录 | 12 个商品名称及其 12 个价格,以扁平文本形式返回,0 条结构化记录,478 个字符 | 0.064 秒 | local_catalog_extraction.txt |
| 返回 HTTP 500 的页面 | fetch_url 返回 None,没有抛出异常 | 30.012 秒 | local_failure_500.json |
| Books to Scrape 商品页(公开页面) | 1,324 个字符的干净文本,以及对应的 Markdown 版本 | 0.753 秒 | public_books_product.txt / .md |
以上每一行都来自 trafilatura 套件里的 artifacts/raw/trafilatura-test-summary.json——trafilatura 2.1.0、Python 3.14、macOS arm64、单机单次运行。请把这些时间看作观测值,而不是正式基准测试成绩。
在那个 500 错误样本上,fetch_url 大约在 30 秒后返回了 None,而附近的本地接口却很快就返回了。这个运行结果只能说明它有延迟,不能说明内部到底是重试/退避、固定超时,还是走了别的内部路径。本文没有验证 fetch_url 是否支持逐次调用的超时参数;已有证据表明,更可控的做法是使用你自己掌控的客户端先把页面拿下来,再把 HTML 传给 trafilatura。

同一组测试也暴露出三个边界。
首先,也是最关键的:trafilatura 是内容提取器,不是结构化爬虫。 我把它用在一个产品目录样本上。它返回了全部 12 个商品名称——以文本形式——以及恰好 0 条结构化记录(按 results/local_catalog_extraction.txt 统计,共 478 个字符的扁平文本)。价格也都回来了,从 $18.00 到 $51.00,每个价格都单独放在对应商品名下方的独立行里。你想要的内容都在输出里,但没有任何一项是字段。如果你需要 [{name, price, rating}, …] 这种结构,那它就是错的工具,怎么配都不会变——这是设计选择,不是 bug。

第二:它不会渲染 JavaScript。 它只处理静态 HTML。把它丢到客户端渲染页面上,你拿到的只是 JS 执行前服务器已经发出来的内容,而那通常并不是什么有用的信息。如果你的目标站点大量依赖 JS,就得配一个渲染器;trafilatura 不负责那一半。
第三,关于我自己的证据还有一个说明:第一次公开页面的提取测试用的是产品描述块,而不是真正的新闻文章,因为公开沙盒里没有新闻页面。上面的文章清理结果来自一个受控的本地样本。我相信它——因为噪音剥离得非常明确——但我不愿意把产品页包装成“新闻级提取”的证据,所以研究包里把这部分标成了一个开放缺口。2026-07-14 的后续运行把这个缺口补上了,结果就在下一节。
真实页面的噪音清理核验

两张真实页面只抓取一次,保存为离线样本,并附上 SHA-256 哈希,然后在断网状态下重新运行。没有记录耗时,也没有人工标注的真值。这是一次真实页面的噪音清理核验,不是准确度评分。
| 真实文章样本 | 原始 HTML | 提取正文 | 正文/原文 | 页面杂项标记移除数 | 标题 | 日期 | 主机名 | 作者 | 站点名 |
|---|---|---|---|---|---|---|---|---|---|
| Wikipedia,“Web scraping” | 230,049 字节 | 26,673 字节 | 0.116 | 4/4 | 是 | 2005-09-17 | wikipedia.org | null | null |
| Wikinews,“7th Heaven”(归档页) | 79,716 字节 | 2,200 字节 | 0.028 | 5/5 | 是 | 2005-11-29 | wikinews.org | null | null |
这两行都来自 artifacts/results/trafilatura-fidelity-summary.json。检查的标记包括 “Jump to content”、“Privacy policy”、“Powered by MediaWiki”、“This page was last edited”,以及 Wikinews 页面上的 “free news source”——这些都被成功剔除,在两页上都没有漏出来。
正文/原文比值衡量的是压缩程度,不是准确率;被遗漏的文章内容没有单独评分。这两张 MediaWiki 模板上的 author 和 sitename 都是 null,而上面的本地受控样本则提供了作者信息。这是测试中观察到的 MediaWiki 特定缺失,不代表它整体的署名提取能力差。提取到的日期按返回值如实记录,未与真值再做比对。
两个提取器、同一份 HTML 字节、一个评分脚本

在这个研究基线里,只有一份针对 trafilatura 的同测试床对比,而这份对比并不是 trafilatura 自己构建的。真正构建它的是 mozilla-readability 套件,把它作为对照组:22 个手工标注样本中,每个文本块都被标记为 ARTICLE 或 BOILERPLATE,而且每个词都有唯一的标记 token,因此提取出的词能精确映射回唯一一个标注区块。两个工具拿到的 HTML 字节完全相同。Readability 运行在 jsdom 29.1.1 和 Node v22.22.3 上;trafilatura 则使用自己的解析器。
| 指标(微平均,内容保真集) | trafilatura 2.1.0 | @mozilla/readability 0.6.0 | 证据 |
|---|---|---|---|
| 文章单元恢复数 | 40/40 | 40/40 | comparison.json |
| 泄漏的噪音单元数 | 1/17 | 5/17 | comparison.json |
| 污染输出的噪音 token 数 | 3 | 41 | comparison.json |
| Token 精度 | 0.939 | 0.902 | comparison.json |
| Token F1 | 0.969 | 0.948 | comparison.json |
非散文召回率,f6_nonprose 样本 | 7/8 | 8/8 | comparison.json |
超短文章,f3_short_120 的 F1 | 0.571 | 0.800 | comparison.json |
来源是这个研究基线里的 tools/mozilla-readability/artifacts/raw/comparison.json——内容保真集共 11 个样本,40 个文章单元,17 个噪音单元。这些都是故意偏向对抗性场景的合成样本,所以应该把它们看作机制展示,而不是对真实语料库的排名。
两个工具都不是万能的,差异才是最值得关注的部分。trafilatura 更“干净”——只泄漏了 1 个区块,而 Readability 泄漏了 5 个;污染 token 也只有 3 个,而 Readability 有 41 个。这就是“干净文章文本”的精确率那一半,它决定了你的 LLM 上下文窗口会不会被侧栏链接塞满。但 Readability 也保留了 trafilatura 丢掉的内容:在非散文样本上,trafilatura 丢掉了一个 <figcaption>,而 Readability 保留了它;在一篇非常短的文章上,trafilatura 只有 0.571,而 Readability 达到了 0.800。激进清理是有代价的,不是白赚的。如果你的语料里有很多短文、图注和表格密集页面,这个取舍可能反而对你不利。
如何把这个对比变成你的选型测试
先明确你的流水线里,哪类错误代价更高。如果页面杂项会吃掉下游 token,或者污染搜索结果,那么噪音单元泄漏数和污染 token 数就应该赋予更高权重。如果不能接受图注、短文或非散文块被删掉,那么就该更重视按页面形态衡量的内容召回率。共享样本集能把这种取舍显性化,但它不会替你决定新闻档案库、文档语料库或检索流水线该怎么加权。
更广泛的压力测试背景可以看十个库的内存与畸形 HTML 对比。
做对比实验时,最好从保存好的 HTML 开始,而不是直接抓线上 URL,这样两个提取器拿到的字节才完全一致。样本应包含普通长文、短通知、图注密集页面、表格或代码密集文档,以及你实际要接入的每个发布源模板。在运行工具之前,先标注少量必须保留的内容单元和已知的页面杂项区块。然后分别统计空输出、必需单元召回、非目标单元泄漏,以及元数据字段。单一的总“质量分”很可能掩盖真正重要的失败模式。
同时也要验证下游对输出的契约。trafilatura 的 JSON 可以携带提取内容和元数据,但 JSON 序列化并不等于你定义好的类型化行结构。对于文章文本,应该检查最小正文长度和必要标记,而不是只要非空就算过。对于元数据,要区分“缺失”和“值错误”,并保留来源 URL 和提取版本,这样以后才能重放漏检案例。
抓取环节也应该单独测试。前面提到的 30 秒本地失败现象针对的是 fetch_url,不是针对你已经拿到 HTML 之后的提取过程,而且它内部具体机制并未被证实。如果你在意超时、重试、登录认证或代理策略,就用一个你能控制行为的客户端,记录最终响应字节,再把这些字节交给提取器。这样可以把采集失败和内容选择失败分开,也让对比更容易复现。
最后,要在目标平台上评估部署属性。在 Python 3.14 环境里安装 17 个包且无需单独浏览器,确实是优势,但这并不能说明吞吐、内存增长、各架构 wheel 可用性,或者并发工作线程下的行为。本文证明的是它在和 Readability 对比时有一个有价值的精确率/保留率机制;是否适合生产,还得看你的语料和运行环境测试。
如果你想看一个外部交叉验证,而不是只看这个基线自己的结果,可以参考 trafilatura 的官方评估页以及公开的 ScrapingHub article-extraction-benchmark。这两个来源都表明,它在大约 181 个真实页面上的 word-F1 表现优于 readability-lxml,方向与上面的表格一致。很多引用这个基准测试的文章会跳过一个细节:广为流传的 trafilatura F1(约 0.945)对应的是更早的 0.5.1 版本,而不是本文测试的 2.1.0。
优缺点
优点:
- 在共享样本集上,比 Readability 更少泄漏被标注为噪音的内容:17 个区块里只漏了 1 个,而 Readability 漏了 5 个。
- 在本地样本上正确提取了作者和日期;在两张真实文章页面上则正确提取了标题、日期和主机名。
- 同一份 HTML 可序列化为文本、Markdown 或 JSON。
- 只需安装 17 个包,不需要额外浏览器运行时。
- 失败时表现安静:
fetch_url遇到 HTTP 500 时返回None,不会抛异常。 - 测试版本就是最新版本(2.1.0),不存在版本漂移问题。
- 采用 Apache-2.0 许可证,宽松且适合商业使用。
缺点:
- 不是结构化爬虫:目录测试只返回了 12 个名称和 12 个价格的文本,0 条类型化记录。没有 schema,也没有字段。
- 不支持 JavaScript 渲染——只处理静态 HTML;如果页面是客户端渲染的,就需要单独配一个渲染器。
- 在两张真实的 MediaWiki 页面上,
author和sitename都是 null,因此署名提取并不是稳妥保证的。 - 清理过于激进有代价:它丢掉了 Readability 保留的
<figcaption>,而在超短文章上只拿到 0.571,对比 Readability 的 0.800。 - 那次安静返回的 500 错误耗时 30.012 秒;如果你关心截止时间,就该用你可控的抓取器。
- 内置的抓取 / sitemap 爬虫以及 CSV/XML 输出格式在这次测试中没有覆盖,因此我不对它们下结论。
适合谁,不适合谁
trafilatura 面向的是文章型正文提取。如果你要从静态 HTML 里构建文本语料、可读性档案或 NLP 输入,而且能接受启发式清理,它是一个值得考虑的候选。共享样本对比显示,它比 Readability 更少泄漏噪音内容,而 Readability 在短文本和非散文场景中保留得更多。
如果你真正需要的是结构化提取——比如带价格的商品行、类型化记录、key: value 字段——那就跳过它,因为它给你的是文本,不是表格(目录测试里虽然拿回了 12 个名称和 12 个价格,但 0 条记录)。如果目标页面的内容是在浏览器里渲染出来的,而你又不想额外接一个渲染器,那也不适合,因为 trafilatura 只读静态 HTML,读完就停。要是你的语料大多是短摘要、图片图注和表格密集页面,也要三思:在标注样本上,这正是它清理开始伤到真实内容的地方(超短文章 F1 只有 0.571,还丢了一个 <figcaption>)。工具要配任务:需要完整文章文本可以,用它;需要结构化 JSON、JS 页面或 100 字以内的短条目,就另找方案。
替代方案,以及 Thunderbit 的位置
trafilatura 是一个 Apache-2.0 的自托管库,没有按次收费;但计算资源、带宽、抓取、监控和维护都仍由使用方承担。它能处理你提供的 HTML 里的文章型内容提取,但不负责浏览器执行,也不负责你自定义的重复行 schema。
如果你想看同样六个提取器在同一组样本上的结果,可以参考六个库的提取对比。
托管服务则可以把采集、渲染和 schema 构建都放到供应商边界之后。我们开发 Thunderbit,但没有拿它跑这些样本,所以本文无法给出质量、渲染、延迟、反爬或成本方面的对比。选择的关键在于:你是要自己托管、可控的 HTML 到内容提取,还是要一个托管式的采集与结构化提取边界。
相关基准评测还包括:完整的开源爬虫对比、Crawl4AI 的浏览器驱动 Markdown 评测,以及 Firecrawl 的自托管 Markdown 评测。
结论
如果你的任务是从静态 HTML 中提取文章正文,并且最在意的是屏蔽被标注为噪音的内容,那么 trafilatura 值得考虑。在共享样本上,它只漏掉了 1 个标注噪音单元,而 Readability 漏掉了 5 个。不过,这个优势是以超短文本和非散文样本上的更低保留率换来的,所以最终应由你的语料形态来决定。
它不会执行 JavaScript,也不会输出你自定义的重复行。对那两张 MediaWiki 页面来说,author 和 sitename 都是 null,这是模板范围内的观察结果。Readability 保留了 trafilatura 丢掉的图注,并且在标记为 f3_short_120 的样本上得分更高;可见证据支持的是“在超短样本上 F1 更低”,并不支持字符数之类的说法,也不支持“输出被弄坏了”这种结论。
试用 Thunderbit 进行网页数据提取 Get Started Free
常见问题
trafilatura 会返回什么样的结构? 它可以把提取内容和元数据序列化为 JSON、Markdown、文本、HTML、XML 或 CSV。这些是结构化序列化,但不是你自定义的重复行 schema,例如商品名称、价格和评分。目录样本返回的是扁平内容,而不是类型化行数据。
trafilatura 能把产品目录抓成结构化行吗? 不能。它是内容提取器,不是结构化爬虫。在目录样本上,它确实返回了全部 12 个商品名称的扁平文本,但 0 条结构化记录——名称虽然在输出里,但不是字段。如果你需要 name/price/rating 这类类型化记录,应该改用基于选择器的解析器,或者基于 schema 的提取 API。
trafilatura 支持 JavaScript 渲染吗? 不支持。它只处理静态 HTML。把它用于客户端渲染页面时,你拿到的只是 JS 执行前服务器发回来的内容,而那通常并不是你想要的内容。如果目标站点大量依赖 JS,请配一个单独的渲染器。
trafilatura 安装起来麻烦吗?
在测试用的 Python 3.14 虚拟环境里,安装过程拉取了 17 个包,也不需要单独的浏览器运行时。记录到的最大 wheel 是 lxml,体积 8.6 MB。另外,单次本地 500 调用返回 None 花了 30.012 秒;如果你需要可验证的截止时间,就用你自己掌控的 HTTP 客户端。
trafilatura 可以免费用于商业用途吗? 可以,它采用 Apache-2.0 许可证,宽松且适合商业使用。和往常一样,在基于它开发之前,最好先到仓库确认最新许可证状态。


