跳到主要内容

清洗与 Markdown 化

本章讲清 aprocess_html 的前两步:① 清洗 DOM(scraping strategy)② HTML→Markdown(markdown generator)。这是「网页变干净文本」的主体。

1. 这一章解决什么

原始 HTML 不能直接喂模型:全是 <script><style>、属性噪声、嵌套容器。我们要分两步:

  1. 清洗:把 HTML 洗成只剩有意义结构的 cleaned_html,顺便把图片/视频/链接/表格抽成结构化字段。
  2. Markdown 化:把 cleaned_html 转成 Markdown,并把链接处理成「引用编号 + 文末列表」的形式(对模型更友好、省 token)。

2. 第一步:DOM 清洗(scraping strategy)

默认实现是 LXMLWebScrapingStrategy(content_scraping_strategy.py:101),入口 scrap(:122)。它基于 lxml(比 BeautifulSoup 快)做以下事:

scrap(url, html)

├─ 解析 HTML(lxml)
├─ process_element() 递归遍历: :201
│ • 删脚本/样式/注释等不要的标签
│ • remove_unwanted_attributes_fast 去属性噪声 :577
│ • 收集图片/音频/视频 → media
│ • 收集 <a> → links(内链/外链分开)
│ • 收集 <table> → tables
├─ flatten_nested_elements 拍平无意义嵌套 :397
└─ 产出 ScrapingResult{cleaned_html, media, links, metadata}

几个要点:

  • 媒体/链接/表格被抽成独立字段,不只是留在 HTML 里。所以 result.mediaresult.linksresult.tables 能直接拿到结构化数据。
  • 拍平嵌套(flatten_nested_elements,:397):把 <div><div><div>文字</div></div></div> 这种无意义套娃压扁,减少 Markdown 噪声。
  • 表格抽取另有专门策略(table_extraction.py,DefaultTableExtraction),能把复杂表格转成规整结构。

清洗产出的 cleaned_html 回到 aprocess_html,媒体/链接/表格被 model_dump 成 dict(async_webcrawler.py:799-814)。

3. 第二步:HTML → Markdown

3.1 选哪份 HTML 当输入

Markdown 生成前,先选「拿哪份 HTML」。由 markdown generator 的 content_source 决定(async_webcrawler.py:825-842):

content_source用哪份 HTML适合
cleaned_html(默认)清洗后的 HTML大多数情况
raw_html原始 HTML你想自己控制清洗
fit_htmlschema 预处理后的 HTML配合结构抽取

这用一个 dict 派发实现,选错 key 就回落到 cleaned_html,很稳。

3.2 转换:CustomHTML2Text

核心是 DefaultMarkdownGenerator.generate_markdown(markdown_generation_strategy.py:148)。它用项目内 fork 的 CustomHTML2Text(html2text/ 目录)做转换,默认参数很讲究(:181-190):

# markdown_generation_strategy.py:181-190(默认 html2text 选项)
default_options = {
"body_width": 0, # 不自动折行(模型不需要)
"ignore_links": False,
"ignore_images": False,
"single_line_break": True,
"mark_code": True, # 标注代码块
"escape_snob": False,
}

body_width=0 是个细节:关掉文本折行,因为对 LLM 来说硬折行只会污染内容。

3.3 链接引用化:⟨n⟩ + References

这是 Crawl4AI 一个标志性处理。普通 Markdown 里链接是 [文字](http://很长的url),内联长 URL 既费 token 又打断阅读。convert_links_to_citations(markdown_generation_strategy.py:82)把它们改成「学术引用」风格:

原理演示(# 示意,非源码):

# 把内联链接换成编号,URL 收集到文末
# 输入: See [the docs](https://x.com/docs) here.
# 输出正文: See the docs⟨1⟩ here.
# 输出引用: ⟨1⟩ https://x.com/docs: the docs

真实实现用一个预编译正则 LINK_PATTERN(markdown_generation_strategy.py:11)扫所有链接,给每个唯一 URL 分配递增编号,正文里换成 文字⟨num⟩,同时累积一份 References(:109-146)。图片则保留 ![...⟨num⟩] 形式。相对 URL 会先用 fast_urljoin 补成绝对 URL,并缓存避免重复计算。

产物是 MarkdownGenerationResult(models.py:120),含:

字段内容
raw_markdown直转的 Markdown
markdown_with_citations引用化后的正文
references_markdown文末 References 列表
fit_markdown过滤后的精简版(见下一章)
fit_html过滤后的 HTML

4. fit_markdown 从哪来

如果 markdown generator 配了 content_filter,generate_markdown 会多做一步:用过滤器筛出「正文块」,再把这些块单独转成 fit_markdown(markdown_generation_strategy.py:229-242)。过滤器怎么决定哪些块算正文,是下一章 04-content-filters.md 的主题。

5. 巧妙之处

  • 媒体/链接/表格结构化分离,不混在文本里——下游要图片清单、要外链列表都现成。
  • body_width=0 + 引用化链接:两个小决定,都是冲着「对 LLM 友好、省 token」去的。
  • content_source 三选一:同一套 Markdown 生成器,输入可换,灵活适配清洗强度。

6. 代码地图

主题文件路径符号名
DOM 清洗crawl4ai/content_scraping_strategy.pyLXMLWebScrapingStrategy.scrapprocess_elementflatten_nested_elements
去属性噪声crawl4ai/content_scraping_strategy.pyremove_unwanted_attributes_fast
表格抽取crawl4ai/table_extraction.pyDefaultTableExtraction
Markdown 生成crawl4ai/markdown_generation_strategy.pyDefaultMarkdownGenerator.generate_markdown
链接引用化crawl4ai/markdown_generation_strategy.pyconvert_links_to_citationsLINK_PATTERN
html2text 引擎crawl4ai/html2text/CustomHTML2Text
结果模型crawl4ai/models.pyMarkdownGenerationResult