首页
/ Docling HTML 表单解析实战:从餐厅小票 kvp_data_example 到结构化 DoclingDocument

Docling HTML 表单解析实战:从餐厅小票 kvp_data_example 到结构化 DoclingDocument

2026-09-06 17:48:53作者:尤辰城Agatha

本篇以 Docling 测试数据集中的餐厅小票样本 kvp_data_example.html 及其 Markdown 基准输出为主线,完整解析 Docling HTML 后端如何将带有编号标记(marker)、字段名(key)和字段值(value)的 HTML 表单解析为 field_region / field_item 结构化节点,并说明这些节点在 Markdown 导出、JSON(DoclingDocument)和层级树(itxt)中的具体呈现方式,读完后可掌握 Docling 键值对(key-value pair)表单解析的完整数据流与可验证依据。

1. 样本来源:一份典型餐厅小票 HTML

该 Markdown 基准文件由源文档 kvp_data_example.html 经 Docling 的 HTML 后端转换而来。源文档是一份“Restaurant Receipt Form”(餐厅小票表单页),页面标题(<h1>)为 HEADER,副标题为 Order Receipt Details,并包含一段说明文本 “Receipt documentation for table service:”。

从源码结构看,页面主体是一个 .form_region 容器,内部由多行 .field-row 组成,每一行是一个 .field,由三个语义明确的子元素构成:

  • .field-marker:编号标记,如 <div id="key1_marker" class="field-marker">1</div>,在转换结果中对应 marker 标签文本;
  • .field-label:字段名,如 <span class="field-label" id="key1">Restaurant</span>,对应 field_key 标签;
  • .field-value:字段值,如 <span class="field-value" id="key1_value1">Docling</span>,对应 field_value 标签,且由于是只读展示值,kind 被标记为 read_only

表单区之后还有一段提示标题 “Order items:”、说明文本 “Itemized list of products ordered with quantities.”、一个 4 行 3 列的 <table>(Nr. / Item Description / Quantity),以及页脚粗体文本 “Docling Restaurant 2026”。这种“编号 + 字段名 + 下划线值”的排版是表单/票据类文档的典型形态,也是 Docling 键值对提取能力要覆盖的场景。

2. Markdown 基准输出的完整内容与解读

基准文件 kvp_data_example.html.md 是该样本转换结果的 Markdown 呈现,全文如下(逐行保留):

# HEADER

Order Receipt Details

Receipt documentation for table service:

<!-- missing-text -->

<!-- missing-text -->

1

Restaurant

Docling

<!-- missing-text -->

2

Telephone

(123) 456-7890

<!-- missing-text -->

3

Server

Quack Quackling

<!-- missing-text -->

4

Order Number

12345

<!-- missing-text -->

5

Seating

13

<!-- missing-text -->

6

Dining Type

Celebration

<!-- missing-text -->

7

Number of guests

5

<!-- missing-text -->

8

Receipt date

16/03/2026

Order items:

Itemized list of products ordered with quantities.

| Nr. | Item Description | Quantity |
| - | - | - |
| I | Coffee | 1 |
| II | Lunch | 2 |
| III | Cake | 1 |

Docling Restaurant 2026

可以从中读出几个关键事实:

  1. 标题层级完整继承<h1>HEADER</h1> 导出为 Markdown 一级标题 # HEADER;页面上的 <title>Restaurant Receipt Form</title> 并未混入正文,而是作为文档家具(furniture)单独存放(见下文 JSON 分析)。
  2. 8 个字段全部保真:从 Restaurant = Docling、Telephone = (123) 456-7890、Server = Quack Quackling、Order Number = 12345、Seating = 13、Dining Type = Celebration、Number of guests = 5,到 Receipt date = 16/03/2026,编号 marker、字段名、字段值按原始阅读顺序成组出现,值与键之间没有混淆。
  3. 表格被完整转换为 Markdown 管道表格:表头三列(Nr.、Item Description、Quantity)与三行数据(I/Coffee/1、II/Lunch/2、III/Cake/1)一一映射,与源 HTML 的 <thead>/<tbody> 结构一致。
  4. <!-- missing-text --> 占位注释:全文共出现 9 处。结合同目录的 JSON 基准(1 个 field_region + 8 个 field_item)与 itxt 层级文件可以推断,这些注释是 Markdown 导出器为“无文本内容的容器节点”(field_region 与每个 field_item)保留的结构性占位,使纯文本读者仍能感知到表单区的分组边界;而真正承载信息量的 marker/key/value 文本则正常导出。

3. DoclingDocument JSON 结构:表单区如何被结构化

同目录的 kvp_data_example.html.json 是该样本的 DoclingDocument 序列化结果(schema_name: DoclingDocumentversion: 1.10.0),它揭示了 Markdown 之下的完整结构:

  • 家具层(furniture)texts/0label: titlecontent_layer: furniture 的 “Restaurant Receipt Form”,即页面 <title>,与正文分离存储,用于文档元信息(如摘要、RAG 元数据)。
  • 正文层(body)texts/1HEADER 标题节点,其子节点依次为两段说明文本、field_regions/0、表格标题/说明文本、tables/0 和页脚文本,完整保留了页面阅读顺序。
  • field_regions/0:唯一的表单区域节点,label: field_region,聚合了全部 8 个 field_item
  • field_items/0 ~ field_items/7:每个字段一个容器,各自挂 3 个子文本节点:
    • label: marker(编号 1~8);
    • label: field_key(字段名,如 Restaurant);
    • label: field_value(字段值,如 Docling),并带 "kind": "read_only" 属性,表明该值来自只读展示(而非可填写输入控件),这正是 HTML 后端依据 .field-value 这类只读值元素打上的标记。
  • tables/0data 中同时包含 table_cells(扁平单元格数组)与 grid(4×3 二维网格),表头单元格带 column_header: true,单元格还携带 row_span/col_span 及起止行列偏移,可直接用于行列级定位;num_rows: 4num_cols: 3 与源表格一致。

这套 field_region → field_item → marker/field_key/field_value 的四级结构,使得下游任务(RAG 抽取、表单字段问答、数据录入校验)能够按“区域—字段—角色”精确取值,而不必对 Markdown 文本再做正则解析。

4. itxt 层级视图:容器节点在树中的位置

同目录的 kvp_data_example.html.itxt 以缩进树展示了同一文档的节点层级,其前几行为:

item-1 at level 1: title: HEADER
  item-2 at level 2: text: Order Receipt Details
  item-3 at level 2: text: Receipt documentation for table service:
    item-4 at level 2: field_region: ignored
      item-5 at level 3: field_item: ignored
      item-6 at level 4: marker: 1
      item-7 at level 4: field_key: Restaurant
      item-8 at level 4: field_value: Docling
      ...(7~8 号字段同构)
  item-37 at level 2: text: Order items:
  item-38 at level 2: text: Itemized list of products ordered with quantities.
  item-39 at level 2: table with [4x3]
  item-40 at level 2: text: Docling Restaurant 2026

可以观察到两点:其一,field_region/field_item 是结构容器,本身无文本,故在 itxt 中标记为 ignored(不产生文本内容);其二,marker、field_key、field_value 是同一 field_item 下的并列子节点(level 4),与 JSON 中的 children 引用完全一致。表格在树中压缩为一行 table with [4x3] 摘要,与 JSON 中 num_rows/num_cols 呼应。三个基准文件(md / json / itxt)互为印证,构成 Docling 对该 HTML 样本的完整地面真值(ground truth)。

5. 源码级原理:HTML 后端如何产出 field_region / field_item

上述结构由 html_backend.py 的表单容器处理链生成,核心调用路径如下:

  1. _handle_form_containerhtml_backend.py#L4307):当遍历到表单容器标签(如示例中的 .form_region 区域)时,先探测目标文档是否支持 add_field_region / add_field_item / add_field_key / add_field_value 四个方法。支持时走新式字段化路径;不支持时回退到旧的 add_form + add_key_values(KVP 图)路径,即生成 FormItem/KeyValuesItem 的单元格与 GraphLink(TO_VALUE) 链接结构。
  2. _extract_form_regionhtml_backend.py#L3831):在容器标签内收集全部子标签,按“key 标签 → marker → values”的映射关系抽取为 _ExtractedFormRegion,并记录 consumed_tag_ids(已被表单逻辑消费的标签),避免后续普通遍历重复处理。
  3. _add_field_item_from_extractedhtml_backend.py#L3750):为每个 _ExtractedFormField 依次创建 field_item,并把 keymarkervalueextra_text 四类片段按 DOM 中的 order 排序后逐一落库——key 调用 add_field_key,marker 以 DocItemLabel.MARKER 落为普通文本,value 调用 add_field_valuekind 透传,即 JSON 中出现的 read_only),带 checkbox_label 的值则降级为普通文本。这正是 itxt/JSON 中 marker、field_key、field_value 保持原始阅读顺序的原因。
  4. 落库与回退:确认字段解析成功后,add_field_region 先挂载到当前父节点(html_backend.py#L4367),再以 _use_form_container / _use_form_fields_by_key_id 上下文重走标签遍历;若无有效字段(fields_by_key_id 为空),表格容器会退化为普通 _handle_block 表格解析,普通容器则按常规块级内容处理——这解释了示例中订单明细 <table> 为何以标准表格(而非表单字段)呈现。

6. 如何在仓库中查看与验证该样本

  • 源 HTML 与基准三元组分别位于 tests/data/html/sources/kvp_data_example.htmltests/data/html/groundtruth/ 下的 kvp_data_example.html.md / .json / .itxt;HTML 后端的测试入口为 test_backend_html.py
  • 验证方式:使用 Docling 的 HTML 后端转换该源文件后,依次比对 Markdown 导出、DoclingDocument 序列化 JSON 与 itxt 树,三者应与上述基准一致——重点核对 8 组 marker/key/value、field_valueread_only 标记、4×3 表格网格及 <!-- missing-text --> 占位数量(9 处,对应 1 个 field_region + 8 个 field_item)。
  • 适用前提:field_region/field_item 结构要求文档版本支持相应的 add_field_* 系列方法(基准 JSON 中 version 为 1.10.0);旧版文档模型下同一 HTML 会走 KVP 图回退路径,输出形态不同,跨版本比对时需留意。

7. 小结

kvp_data_example 系列基准文件用一份餐厅小票完整示范了 Docling 处理键值对表单的能力边界:编号标记、字段名与字段值按 DOM 顺序保真落到 field_item 子节点,只读值通过 kind: read_only 显式标注,表格以网格化 grid 数据承载,页面标题下沉为家具层文本,Markdown 导出则以 <!-- missing-text --> 注释保留容器结构痕迹。源码层面,这套结果由 html_backend.py_handle_form_container → _extract_form_region → _add_field_item_from_extracted 的表单解析链生成,并带有面向旧文档模型的 KVP 图回退机制。对从事表单/票据数据抽取的开发者而言,这组“源 HTML + md + json + itxt”四件套既是现成的对照基准,也是理解 DoclingDocument 字段化结构的最佳教学样本。

登录后查看全文
热门项目推荐
相关项目推荐