在文档解析领域,嵌套表格(一个单元格里套着另一个完整的表格)是绝对的“劝退”场景。传统的 OCR 方案(比如 Tesseract 或 简单的 PaddleOCR 流程)通常会把它们压扁成一堆乱序的文本,因为它们只看“行”和“列”,看不懂“层级”。

Docling 之所以能把嵌套表格完美拆解成 Markdown 或 HTML,是因为它采用了一套 “自顶向下检测 + 自底向上重构” 的混合机制。

1. 核心架构:不仅仅是 Object Detection

Docling 的 Pipeline 不是线性的,它是一个多层叠加的过程。处理复杂页面时,它主要依赖两个核心组件的协作:

  1. DocLayNet 模型(视觉层):负责“看”出哪里是表格。
  2. TableFormer / TableMaster(结构层):负责“理解”表格内部的逻辑。

但针对嵌套表格,Docling 引入了一个关键逻辑:层级感知的区域建议(Hierarchical Region Proposal)

当一张页面进入 Docling 时:

  1. 宏观检测:首先,基于 ResNet/Mask R-CNN 的布局模型会扫视全图,框出“最外层”的表格边界。
  2. 递归扫描:如果模型在某个单元格(Cell)内部检测到了高密度的网格线或对齐的文本块,它会触发递归(Recursion),将该单元格视为一个新的“子页面”,再次运行布局分析。

2. 难点攻克:Cell 内的“微观世界”

嵌套表格最难的地方在于坐标对齐。外层表格的边框和内层表格的边框往往靠得非常近,单纯靠 IoU(交并比)很容易误判。

Docling 在底层做了一个非常工程化的处理:逻辑坐标与物理坐标的解耦

它不直接生成最终的 HTML,而是先生成一个中间表示(Intermediate Representation, IR)。这个 IR 是一棵树(Tree),而不是一张网(Grid)。

  • Root Table
    • Row 1
    • Row 2
      • Cell A
      • Cell B (Container) -> Child Table
        • Row i
        • Row ii

这种树状结构保证了,即使内层表格把布局撑坏了,外层表格的逻辑结构依然是闭合的。

3. 代码实战:手动干预表格解析流程

虽然 DocumentConverter 封装得很好,但作为技术人员,我们需要知道怎么通过代码去“看见”这个嵌套结构,甚至在解析出错时进行干预。

Docling 暴露了 TableStructure 对象,我们可以遍历它来看看底层的树状逻辑。

以下代码展示了如何加载一个 PDF,并专门提取出表格的层级结构数据,而不是仅仅导出 CSV。

Python

from docling.document_converter import DocumentConverter
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions, TableStructureOptions

def inspect_nested_tables(pdf_path):
    # 1. 配置 Pipeline,开启增强的表格结构识别
    pipeline_options = PdfPipelineOptions()
    pipeline_options.do_table_structure = True
    # 针对复杂嵌套表格,提高 OCR 和 结构分析的精细度
    pipeline_options.table_structure_options = TableStructureOptions(
        do_cell_matching=True,  # 强制匹配单元格内容
        mode="accurate"         # 使用更耗时但更准的模型
    )

    converter = DocumentConverter(
        format_options={
            InputFormat.PDF: PdfPipelineOptions(pipeline_options=pipeline_options)
        }
    )

    print(f"正在解析嵌套结构: {pdf_path}...")
    doc = converter.convert(pdf_path).document

    # 2. 遍历检测到的表格
    for i, table in enumerate(doc.tables):
        print(f"\n=== Table #{i+1} ===")
        print(f"表格物理坐标 (BBox): {table.prov[0].bbox}")
        
        # 3. 深入单元格 (Grid) 层面
        # Docling 将表格解析为 DataGrid 对象
        grid = table.data.grid
        
        # 遍历每一行
        for row_idx, row in enumerate(grid):
            for col_idx, cell in enumerate(row):
                # cell.text 是单元格内的纯文本
                # 但我们需要检查这个 cell 是否包含了子结构
                
                content = cell.text.strip()
                if not content:
                    continue

                # 在底层,如果单元格内有复杂的子布局,
                # Docling 会在 structure item 里标记
                # 这里我们简单打印出单元格的跨度,这是判断嵌套的重要依据
                # 嵌套表格通常会导致外层单元格出现巨大的 rowspan/colspan 或者奇怪的 bbox 比例
                if cell.row_span > 1 or cell.col_span > 1:
                    print(f"  [Row {row_idx}, Col {col_idx}] 复合单元格: '{content[:20]}...' (Span: {cell.row_span}x{cell.col_span})")
                
                # 如果要完美复现嵌套,建议导出为 HTML 而不是 Markdown
                # 因为 Markdown 原生不支持嵌套表格

    # 4. 导出为 HTML 以观察 DOM 树结构
    html_output = doc.export_to_html()
    with open("nested_debug.html", "w", encoding="utf-8") as f:
        f.write(html_output)
    print("\n已导出 nested_debug.html,请用浏览器打开查看 DOM 树的 <table> 嵌套情况。")

if __name__ == "__main__":
    # 找一个带有“表格里套表格”的 PDF 跑一下
    inspect_nested_tables("complex_layout.pdf")

4. 为什么混合模型比纯 CV 模型强?

你可能会问,为什么不直接用 GPT-4V 或者 LLaVA 这种多模态大模型直接“看”图写 HTML?

在工程落地上,纯 CV 大模型有两个致命弱点:

  1. 幻觉:它可能把表格里的数字 100.00 看成 1000.00,这在财务场景是灾难。
  2. 对齐丢失:对于没有边框的隐形表格(Implicit Tables),纯视觉模型很难对齐列。

Docling 的混合模型厉害在:它用 CV 模型定“骨架”,用 OCR 文字坐标做“校准”。

当处理嵌套表格时,视觉模型给出一个大概的子表格区域,然后算法会拿 OCR 抓到的具体文字坐标(Bounding Box)去反向修正这个区域的网格线。这种Pixel-level Alignment(像素级对齐) 是保证嵌套表格数据不串行的关键。

总结

Docling 处理嵌套表格的秘诀不在于一个超级厉害的端到端模型,而在于递归的版面分析策略严格的坐标树重构

作为开发者,如果你在做 PDF 解析,遇到复杂的嵌套表格搞不定,不要试图用 Regex 去修补文本,直接切换到 Docling 的 HTML 导出模式,去解析它的 DOM 树,那才是最稳健的路径。