ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Docling实战:PDF版面分析与表格结构恢复指南

Docling实战:PDF版面分析与表格结构恢复指南 我真正开始认真留意 Docling是在一个被 PDF 折磨的下午。当时我从一批审计报告里抽表格报告是双栏排版页眉页脚还带着公司公告常用的解析库给我返回了一段连顺序都不对的纯文本表格里的数字和左侧标题完全错位光清洗数据就花了我两天。后来我才意识到问题出在我用“读 PDF 的库”去做“理解文档的活”这两件事根本不是同一个量级。Docling 是 IBM 开源的一个文档转换引擎它能把 PDF、DOCX、PPTX、图片、HTML 等常见格式转成 Markdown 或 JSON。和普通解析库最本质的区别在于它在转换前会对文档做完整的结构理解版面布局、表格结构、阅读顺序、标题层级都会在这一步被识别出来然后才导出成结构清晰的输出。换句话讲你拿到的不是“从 PDF 里抠出来的字符串”而是一份保留了文档逻辑的“结构化副本”。如果你正在搭建检索问答RAG、做文档批量预处理的自动化管线或者长期和扫描版 PDF、复杂表格打交道这篇文章值得看完。我会从原理讲起给出完整的安装、测试、调优经验再把实战中踩过的坑和边界问题说清楚尽量做到看完就能上手。1. PDF 解析的真相能提取文字不等于能看懂文档1.1 “读出文字”和“读懂文档”的差距先看一个最常见的失败场景。假设你拿到一篇学术论文双栏排版。PDF 里第一页左上角是第一栏的开头右上角是第二栏。如果用 PyMuPDF 这类基于坐标的文本提取工具默认做法是沿着页面从上往下扫完全不考虑“左右两个栏其实是并列阅读”这件事。结果就是你拿到 A 段的第一句紧接着是 B 段的第一句然后是 A 段的第二句、B 段的第二句整个内容被完全打乱。人类一眼能看出这是两栏但提取出来的文本没有任何结构信息。再叠加页眉、页脚、图表标题、公式编号之后情况还会更糟。我经常遇到的一种状况是文本流中间突然插进来一个页码或者表格里的数据横七竖八地散落成一长串。为什么会这样因为 PDF 本身是“为打印而生”的格式它记录的是每个字符应该放在纸面的哪个坐标而不记录“这一段是标题”“这个区域是表格”“这句在前、那句在后”的语义关系。要从 PDF 里得到干净的信息就必须额外做一层版面理解。这一层理解恰恰是普通 PDF 读取库和 Docling 这类文档解析引擎的分水岭。前者负责“把字取出来”后者负责“把字放回正确的上下文里”。1.2 文档解析真正要做的事情真正意义上的文档解析至少要解决三件事版面分析区分哪些区域是正文、标题、表格、图片、页眉页脚、公式结构恢复把表格里散布的单元格坐标还原成行列关系把正文按阅读顺序重新排列语义标识明确哪些文本是段首、哪些是列表项、哪些是公式引用方便后续程序消费。传统方案里版面分析依赖人工规则表格恢复靠坐标聚类遇到无边框表格或者跨页大表基本就崩了。Docling 走的是机器学习模型推断的路线先由模型预测每个区域是什么类型再用另一个模型单独恢复表格结构最后按阅读顺序重新整理全页内容。这样输出的 Markdown 或 JSON 里表格是表格、标题是标题正文才会正常流动而不是一个大文本块平铺直叙。2. 逐个拆解 Docling 的文档理解管线2.1 版面分析DocLayNet 数据集与 DETR 检测模型Docling 的版面分析核心是基于 DocLayNet 数据集微调的 DETR 类模型。DocLayNet 是 IBM 联合多家机构构建的一个大规模文档版面标注数据集收集了约八万页带标注的文档页面领域覆盖财务报告、科学论文、技术手册、法律法规文件等。每个页面的标注类别包括标题、正文、列表项、表格、图片、页眉、页脚、脚注、公式、图注等。版面模型的输出不是“全页文字块”而是“区域边界框 类型标签”。它可以直接告诉你“第 2 页左上角这块是 Section-header中间偏下这块是 Table右下角这块是 Picture”。有了这层信息后续的文本提取才知道哪些文字属于标题、哪些文字属于表格不会再一锅乱炖。这种感知能力在实际项目中的价值非常直观。同样是解析一份财报普通文本提取工具会把表格里的“营业收入”和正文里的“营业收入说明”混在一起版面模型则能清清楚楚地把它们分开。下游做检索时用户问“去年营收多少”系统能直接命中表格区域而不是被一段段正文淹没。需要强调一点版面分析只是“框出哪里是表格”真正把表格拆成行列是下面这个模型的工作。这两个环节各司其职缺一不可。2.2 表格结构恢复TableFormer 的看家本领表格是文档解析里公认的硬骨头。一个在视觉上结构清晰的表格在 PDF 的底层数据里可能只是一堆分散的文本框和线条。有些表格没有线框只能靠距离判断各元素属于哪一行、哪一列有些表格跨页下一页还带着“续表”标识有些表头是合并单元格还原出的结构必须同时包含 rowspan 和 colspan 信息。Docling 用 TableFormer 来做表格结构恢复。它会基于版面分析给出的表格区域结合文本层或 OCR 识别出的内容推断出表格的行列结构、合并单元格范围以及每个单元格内的具体文本。最终这些信息会写入文档对象的表格结构中导出 Markdown 时变成规范的管道符表格导出 JSON 时则保留完整的行列关系。表格为什么这么重要因为大部分文档问答和数据分析场景真正想要的不是大段正文而是表里的结构化数据。如果解析结果把表格碾成一行行读不懂的纯文本下游做 RAG 检索时用户问“某一年某公司的净利润是多少”系统根本找不到对应的数值。我自己的项目里表格解析质量直接决定了问答准确率这一点在财经类、医疗类、法律类文档上特别明显。2.3 阅读顺序与导出接口版面模型还负责确定页面上各区域的阅读顺序。Docling 会综合考虑区域的位置关系把同一层级的内容按基本阅读规则排序并尽量把多栏文档的顺序保持为“先左栏后右栏”。这个能力在做 RAG 预处理时很关键因为只有保证上下文的连续语义切块才不会把一句话或一个段落拦腰截断。解析完成之后Docling 会在内存里构建一个DoclingDocument对象。这是一份中间表示的文档模型所有上游信息包括版面、表格、阅读顺序、元数据都以树形结构的节点保存。基于这个对象你可以导出 Markdown、HTML、JSON 等格式。导出的 JSON 不是简单的“文本坐标”而是带语义标签的层次结构你可以在自己的程序里按需读取标题、表格、列表等组件而不是面向纯文本做二次猜测。这种对象化设计给我的感觉很像“编译器里的中间表示”先做通用分析再按目标格式生成结果。好处是后续 Docling 要新增一种导出格式不需要重写解析逻辑你的业务代码要换消费方式也不需要重新解析文档。3. 环境搭建与跑通第一个转换3.1 安装与依赖准备Docling 用 Python 编写底层模型推理依赖 PyTorch 和 Hugging Face Transformers。官方推荐的安装方式很简单pip install docling如果你用uv管理 Python 环境也可以uv pip install docling我的建议是务必在一个干净的虚拟环境里安装。Docling 的依赖树里有很多重包最容易出现版本冲突的场景是你机器上已经装了特定版本的 torch 用于另一个人工智能项目。给 Docling 单独建一个环境或者在 Docker 容器里部署能省去后面排查依赖问题的精力。硬件方面CPU 也能跑只是模型推理速度会慢一些。我实测下来纯 CPU 跑一页普通 PDF 大约一到两秒扫描版加 OCR 后耗时明显放大。如果你要批量处理几百上千份文件建议配 GPU 或至少把处理任务做成异步队列不要同步等待。3.2 命令行工具的使用Docling 自带命令行入口这也是我日常最常用的方式。一条命令就能完成解析和导出docling ./sample.pdf --to md这条命令会把sample.pdf解析完并在同目录生成.md文件。多种格式同时导出也支持docling ./sample.pdf --to json --to md --to html批量处理时直接把目录作为输入传给命令即可docling ./reports/ --to md命令行还支持直接传 URL适合快速验证一份在线文档的解析效果docling https://arxiv.org/pdf/2206.01062 --to md我第一次用这个命令时甚至没有下载 PDF直接就拿到了 Markdown体验相当顺滑。CLI 内部会自动处理网络读取、模型缓存和格式转换对只想快速看效果的人来说非常友好。3.3 Python API 的常用操作如果你要把 Docling 嵌入到自己的数据管线里用 Python API 更灵活。核心代码其实很短from docling.document_converter import DocumentConverter converter DocumentConverter() result converter.convert(./sample.pdf) doc result.document # 导出 Markdown print(doc.export_to_markdown()) # 导出 JSON print(doc.export_to_dict())convert方法返回结果对象里包含了解析后的文档、输入文件的元数据以及每页的解析状态。日常开发中我通常先检查结果对象里有没有解析失败的页再决定是否接受这次结果。对于扫描版 PDF需要开启 OCR。Docling 支持 EasyOCR 和 PaddleOCR 两种后端中文识别场景我更推荐 PaddleOCR。配置方式并不复杂思路是通过流水线选项开启 OCR选择引擎再传给转换器。不同版本的 API 细节略有调整建议先跑通最简版本再按官方文档调整开关不要一上来把所有参数堆满否则出了问题你都分不清是模型的问题还是配置的问题。4. 实测结果中文文档、扫描件与复杂表格4.1 中文文档与 OCR 实测我拿了一份中文扫描版 PDF 测试页面是图文混排带一个跨页表格。默认关闭 OCR 时有文本层的页面解析正常但纯扫描页输出几乎是空的因为 PDF 里根本没有可提取的文本。开启 OCR 之后PaddleOCR 的识别结果基本可用中文正文的断句和分行没有太大问题。有个细节值得注意OCR 识别出的文字本身只有像素信息分词、标点、全半角这些处理全都依赖识别质量。如果原文档扫描分辨率偏低或者有印章、水印叠在文字上识别结果会混入乱码。这属于所有 OCR 工具的共性限制不是 Docling 特有。我的经验是在 OCR 后加一道清洗逻辑比如把全角数字统一转成半角、修正“0/O”“1/I”这类易混淆字符能明显提升下游检索效果。4.2 复杂表格和双栏页面的转换质量我找了一份带三线表、合并单元格的 PDF 报告做测试。Docling 输出的 Markdown 表格结构基本正确表头能对应上合并单元格在 JSON 里也能查到跨行跨列的标记。但要强调一点Markdown 语法本身不支持复杂的单元格合并如果你需要保留全部表格语义必须走 JSON而不是只看 Markdown。双栏论文的解析我也测了。版面模型能正确区分左右栏正文阅读顺序没有出现两栏内容强行拼在一起的情况。页眉页脚会被识别成对应类型不会混进正文段落。和之前用文本提取库拿到的一团乱序相比这个改善是肉眼可见的。当然也有翻车的时候。版面模型在遇到特别少见的版式时比如分栏极不规则、嵌套图形很多的设计稿区域边界框会标错输出 Markdown 会出现段落错位。我的应对方法是把这种文档里的页面截成高分辨率图片再交给带 OCR 的管线解析。这样做往往比直接啃原始 PDF 更稳虽然多了一步但效果更可控。4.3 接入 RAGLangChain 与 LlamaIndexDocling 官方提供了对 LangChain 和 LlamaIndex 的适配。以 LangChain 为例有专门的加载器可以读取 Docling 的解析输出按语义结构把文档拆成适合向量化的块不用自己写“从 Markdown 里抠段落”的代码。我在生产里的用法是先用 Docling 把文档转成 JSON存进预处理目录。索引构建时再从 JSON 里读标题、正文、表格这些语义组件自己决定哪些块该单独建索引。这样做的好处是解析和检索完全解耦后续想换分块策略不需要重新解析原始文件。对 RAG 场景表格内容的处理尤其值得用心。直接吃 Markdown 表格文本检索器很容易被管道符和分隔符干扰。把表格的每一行转成一条自然语言描述比如“2022 年甲公司营业收入为 12.3 亿元同比增长 8%”再送去向量化问答质量会有明显提升。这一步不是 Docling 自动完成的但 Docling 给出的结构化 JSON 让这种转换变得非常容易。5. 实战中的坑模型下载、依赖与性能5.1 模型权重下载与离线部署Docling 在第一次解析文档时会自动从 Hugging Face 下载版面分析和表格结构模型然后缓存在本地。这个过程最常遇到的坑是网络下载慢或内网没有外网权限。离线部署的解法是提前在一台有外网的机器上把模型缓存整个打包拷贝到目标机器并放到对应路径。缓存位置一般在~/.cache/huggingface下也可以通过环境变量HF_HOME自定义路径。批量部署时只要把模型包分发到各节点解析时就不会再触发下载。另外一个容易让人误判的点是模型首次下载时控制台会刷大量进度条很像是卡住了。其实只要网络稳定等下载完成就好。如果反复失败检查代理设置或者直接走离线模式把模型加载路径指到本地目录。5.2 依赖冲突与运行环境Docling 和已有项目的依赖冲突主要出现在 torch 上。我遇到过一种情况另一个项目要求torch2.0.1Docling 安装时把 torch 升到了新版导致那个项目的模型加载直接崩溃。我最终的解决方案是给 Docling 单独建虚拟环境并把它封装成一个独立的解析服务。上游业务通过 HTTP 接口提交文件、拿结果完全不依赖 Docling 的 Python 包。这样带来两个额外的好处一是版本升级影响面可控二是别的团队要接入时不需要处理 Python 环境。还要提醒一句Docling 依赖某些系统级图像处理库比如和 libgl 相关的动态库。如果你用容器部署建议基于 Debian 系镜像而不是最小的 Alpine 镜像否则可能遇到缺共享库的报错。5.3 性能调优经验批量处理时性能瓶颈通常不在模型推理本身而在文件读取和 OCR 环节。我反复尝试后总结出几个见效最快的优化方向关闭不需要的组件。纯电子版 PDF 不需要 OCR把 OCR 开关关掉能省下大量时间多进程并行。文件之间没有强依赖时按文件分片用多进程处理CPU 环境效果立竿见影降低图片采样。有的 PDF 内嵌超高分辨率图片拖慢整体流程但在下游应用里根本用不到在解析配置里忽略图片内容即可。实测同一批 200 页 PDF开启 4 进程并行后总耗时从半个多小时压到了十分钟左右。做上千份文档的批量迁移时这个差距就意味着几个小时的省时值得认真调一调。6. 边界、选型对比与进阶玩法6.1 什么场景不适合用 DoclingDocling 的定位是“版面可理解的文档转换”它不擅长需要更细粒度视觉理解的场景。具体来说手写体识别。OCR 后端主要面向印刷体手写内容基本不可用图表里的数据提取。饼图、折线图内的数值它不会替你读出来只能识别这是图片区域对实时性要求极高的在线解析。模型推理带来的延迟让它更适合离线批处理或准实时任务不适合直接塞进低延迟接口。想清楚边界比追求“一个工具通吃”重要。我见过有人拿 Docling 解析聊天记录导出文件其实那种场景直接读结构化消息列表才是正解没必要过一遍文档理解模型。6.2 和常见替代方案的对比文档解析这个赛道有很多选项我根据自己的使用经验做了一个简表方便选型时快速判断方案擅长方向主要局限PyMuPDF / PdfMiner快速文本提取轻量级无版面理解表格和阅读顺序基本靠运气pdfplumber / Camelot规则式表格提取对复杂表格和无边框表格非常吃力Marker轻量 PDF 转 Markdown结构化细节弱于 Docling表格语义保留有限Unstructured面向 RAG 的分块清洗生态好表格结构和版面分析深度不如 DoclingDocling版面理解 表格恢复 结构化 JSON模型重、依赖多首次下载权重慢直接给结论如果只是想从 PDF 里拿几段文字PyMuPDF 完全够用没必要上 Docling。但要做知识库建设、文档问答、复杂报表自动化处理Docling 的版面理解和表格恢复能力就会体现价值。对比下来它和 Unstructured 的定位最接近但 Docling 在表格结构恢复和版本规范程度上更胜一筹。6.3 进阶玩法把 Docling 变成预处理基础设施我在实际工程里会做一层封装把 Docling 变成独立的文档预处理服务对外只暴露“传入文件路径返回结构化 JSON”的接口。这样做的好处前面提过业务方不需要直接依赖 Docling 的 Python 包后续模型升级或替换解析引擎对上游完全无感。另一个非常推荐的模式是把解析结果缓存起来。同一个文档只要解析一次就把输出存成文件后续所有下游任务都读缓存。分块策略改十遍也不需要重新跑一遍模型省下的计算时间非常可观。更进一步可以把 Docling 的表格结果和数据库表结构做映射把 PDF 里的财务三表、事故报告批量写入数据库做分析。对金融、审计、医疗这些成天接触报表文档的行业这条链路几乎可以直接落地。最后再分享一点个人经验。文档解析领域没有银弹Docling 的价值在于把“从 PDF 里提取文字”升级成了“从 PDF 里重建文档结构”。但我建议你不要无脑把它接入所有流程先说清楚需求你这批文档的主要难点是版面混乱、表格复杂还是扫描质量差先拿一小批覆盖各种版式的样本做评估确认版面分析准确率足够高再全面铺开。跑通一条“特殊版式文档 → 人工校验 → 修正配置 → 回灌测试集”的正反馈循环比单纯调参数更有价值。想试的话直接拿一个 PDF 跑一下docling your_file.pdf --to md看看输出效果你就知道它值不值得进入你的管线了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进