首页
/ Docling HTML 文档代码片段提取实战:从代码块样例到源码级实现解析

Docling HTML 文档代码片段提取实战:从代码块样例到源码级实现解析

2026-09-06 17:31:26作者:邓越浪Henry

本文以 Docling 仓库中的 HTML 代码片段基准样例(html_code_snippets.html.md)为主线,完整讲解一份包含行内代码、<kbd> 按键样式、<samp> 示例输出和 <pre> 代码块的 HTML 文档是如何被 Docling 转换、标记并导出为 Markdown 的,并结合 html_backend.pycode_language.py 的源码,说明 code_language 字段的检测原理和模型预取(docling-tools models download / download-hf-repo)的操作细节。读完本文,你可以复现该样例的转换结果,理解 Docling 文档对象中 code 节点与语言标签的生成机制,并知道如何用仓库自带的测试与基准数据校验自己的 HTML 转换效果。

样例文档与基准数据是什么

该基准文件对应的源文档是 tests/data/html/sources/html_code_snippets.html,标题为 “Code snippets in HTML”。它刻意集合了 HTML 中几种典型的“代码样式”标签:

  • <var>:斜体变量(勾股定理中的 abc);
  • 行内 <code><kbd>:如 doclingpip install docling
  • <pre><code>:一段完整的 Python 转换示例代码块;
  • <samp>:程序输出的示例文本;
  • <pre hidden>:一个带 hidden 属性的代码块(应被排除在输出之外)。

转换后的基准结果就是本文核心文档 html_code_snippets.html.md,配套的机器可读结构是 html_code_snippets.html.jsonDoclingDocument 格式,schema 版本 1.10.0)。三者构成“源文档 → 人类可读基准 → 结构化基准”的完整对照,这也是 Docling 验证 HTML 后端的测试范式。

基准文档内容:安装、转换与输出

基准 Markdown 完整保留了源 HTML 的正文语义,逐段对照如下:

第一段——斜体变量的普通文本:

The Pythagorean theorem can be written as an equation relating the lengths of the sides a , b and the hypotenuse c .

第二段——安装说明。要使用 Docling,直接从包管理器安装 docling(例如 pip):pip install docling

第三段——用 Python 转换单个文档,调用 convert(),示例:

from docling.document_converter import DocumentConverter

source = "https://arxiv.org/pdf/2408.09869"
converter = DocumentConverter()
result = converter.convert(source)
print(result.document.export_to_markdown())

这段示例正是仓库文档中的最小转换流程:入口类是 document_converter.py 中的 DocumentConverterconvert() 接受本地路径或 URL 字符串,返回的 ConversionResult.documentDoclingDocument,可通过 export_to_markdown() 导出 Markdown。

第四段——程序输出:## Docling Technical Report[...],即 arXiv 论文首页标题被识别为二级标题后的 Markdown 前缀。

最后一段——模型预取,共三种方式:

  • 使用 docling-tools models download 命令行工具;
  • 以编程方式调用 docling.utils.model_downloader.download_models()
  • 使用 download-hf-repo 参数按仓库 id 下载 HuggingFace 上的任意模型:
$ docling-tools models download-hf-repo ds4sd/SmolDocling-256M-preview
Downloading ds4sd/SmolDocling-256M-preview model from HuggingFace...

这三种方式的实现分别在 model_downloader.py 和 CLI 模块 cli/models.py:后者导入 download_models 并在第 216 行注册了 download-hf-repo 子命令(@app.command("download-hf-repo")),因此基准文档中展示的命令行输出与该实现直接对应。

源码剖析:HTML 后端如何识别“代码”片段

行内代码标签集:code / kbd / samp 一视同仁

html_backend.py 中定义了关键常量:

_CODE_TAG_SET: Final = {"code", "kbd", "samp"}

解析过程中(约 L1886 附近),只要元素的标签属于该集合,对应的 AnnotatedText 就会被打上 code=True 标记:

code = any(code_tag in self.format_tags for code_tag in _CODE_TAG_SET)

这解释了基准文档中的一个现象:源 HTML 里的 <code>docling</code><kbd>pip install docling</kbd><samp>## Docling Technical Report[...]</samp> 在基准 JSON 里全部是 "label": "code" 节点(见 html_code_snippets.html.json#/texts/10#/texts/12#/texts/18)。也就是说,Docling 不只把 <code> 视为代码,按键提示和输出示例同样被归入 code 类别,保证下游 RAG 或结构化消费时不会把它们与普通正文混淆。

<var> 标签走的是另一条路:它映射为斜体格式而非代码。基准 JSON 中 #/texts/3(文本 "a")带有 "formatting": {"italic": true, ...},普通片段则无 formatting 字段——这与源 HTML 中 <var>a</var> 的语义完全一致。

<pre><code> 代码块:换行保留与语言检测

对于块级代码,html_backend.py 的处理逻辑是:

  1. 提取 <pre> 内的文本并做 Unicode 清洗(_clean_unicode);
  2. 通过 _code_language_hint(tag) 读取高亮器(如 Pygments/Highlight.js)写在 <pre> 或内部 <code> 上的语言 class;
  3. 调用 doc.add_code(...) 创建代码节点,并将 detect_code_language(text, language_hint) 的结果写入 code_language

注意源 HTML 的 Python 代码块并没有任何 class="language-..." 标注,但基准 JSON 中该节点(#/texts/16)的 code_language"Python"——这说明是内容检测命中了 Python 规则。检测逻辑在 code_language.py 中,其设计原则(见文件头部 docstring)是:显式 hint 优先,内容检测只在出现强标记时才给出结论,否则保持 UNKNOWN,因为错误的猜测比 unknown 更糟糕

具体地,_CONTENT_RULES 中 Python 规则(code_language.py)匹配如下特征之一:

re.compile(
    r"^[ \t]*def\s+\w+\s*\([^\n]*\)\s*(->[^\n:]+)?:"
    r"|^[ \t]*elif\b|\b__name__\b|^[ \t]*from\s+\S+\s+import\b",
    re.MULTILINE,
)

基准样例里的 from docling.document_converter import DocumentConverter 正好命中 from ... import 规则,因此被判为 Python。而所有行内短片段(doclingpip install docling 等)不含任何强标记,基准 JSON 中它们的 code_language 都是 "unknown",与检测器的保守策略一致。

对于显式语言提示,normalize_code_language()code_language.py)会先剥离 language- / lang- 前缀(_HINT_PREFIXES),再查 CodeLanguageLabel 全名表与别名表(如 py→Python、sh→Bash、yml→YAML)。单元测试 test_backend_html.py 中的 test_code_language_hint_prefers_prefixed_class 专门验证了一个细节:当 <pre class="bash"><code class="language-python"> 同时存在时,带 language- 前缀的 class 优先,避免高亮器的真实提示被一个恰好形似语言的 utility class 抢占:

soup = BeautifulSoup(
    '<pre class="bash"><code class="language-python">x = 1</code></pre>',
    "html.parser",
)
assert HTMLDocumentBackend._code_language_hint(soup.pre) == "language-python"

plain = BeautifulSoup("<pre><code>x = 1</code></pre>", "html.parser")
assert HTMLDocumentBackend._code_language_hint(plain.pre) is None

hidden 代码块被正确丢弃

源 HTML 末尾的 <pre hidden><code>$ docling-tools</code></pre> 在基准 Markdown 和基准 JSON 中均不存在——基准 JSON 的 texts 恰好止于第 30 项(#/texts/30,即 download-hf-repo 那段命令行输出)。从源码结构看,HTML 后端在遍历节点时会跳过 hidden 属性元素,这使得作者可以在 HTML 中放置“仅用于导航/辅助”的隐藏代码而不污染转换结果。

测试如何验证这份基准

test_backend_html.py 通过 html_paths fixture 枚举 tests/data/html/sources/ 目录下的所有 *.html 文件(rglob("*.html")),对每个源文档运行 DocumentConverter(allowed_formats=[InputFormat.HTML]) 转换,并在(约 L468 处)按约定 html_path.parent.parent / "groundtruth" / html_path.name 找到对应基准文件进行比对。换句话说,html_code_snippets.html 只要放在 sources 目录中就会被自动纳入回归测试:任何后端改动若改变了代码块的标签判定、语言检测或 hidden 元素的过滤行为,都会因与基准 Markdown/JSON 不一致而被捕获。

模型预取的三种方式再展开

基准文档最后一段的“Prefetch the models”在仓库中都有落地实现,适用前提是你打算使用 Docling 的模型能力(布局检测、表格结构、OCR 等)并希望在首次转换前把权重拉取到本地:

方式 入口 说明
命令行 docling-tools models download 实现位于 cli/models.py,内部调用 download_models(约 L175 处)
Python API docling.utils.model_downloader.download_models() 便于在部署脚本或容器构建阶段预取
任意 HuggingFace 仓库 docling-tools models download-hf-repo <repo-id> cli/models.py 注册的子命令,示例 ds4sd/SmolDocling-256M-preview

三者最终都汇聚到 model_downloader.py,差异只在于入口形态与可指定范围(预置模型集合 vs 任意 repo id)。

小结

  • 这份基准文档展示了 Docling 处理 HTML 中代码片段的完整约定:<code>/<kbd>/<samp> 统一标记为 code 节点,<pre> 块保留换行并经语言检测器填充 code_languagehidden 元素被过滤;
  • 语言检测采用“hint 优先、内容保守”策略(code_language.py),基准样例中 Python 块被识别为 Python、行内片段保持 unknown,均符合该策略;
  • 该样例由 test_backend_html.py 的目录级回归测试自动校验,配套结构基准 html_code_snippets.html.json 可直接用于检查节点层级(inline group、list、code_language 字段);
  • 转换侧的最小可用流程(DocumentConverter().convert() + export_to_markdown())与模型预取的三种入口(CLI / Python API / download-hf-repo)在 document_converter.pycli/models.pymodel_downloader.py 中均可直接查证。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388