文章

拆解 MinerU:一个开源 PDF 解析工具,如何把花样百出的 PDF 变成干净的 Markdown?

从论文 PDF 到考研试卷,各种格式的文档都能认出标题、正文、公式、表格……MinerU 是怎么做到的?本文拆解它的四阶段流水线,讲清模型与规则如何分工协作。

拆解 MinerU:一个开源 PDF 解析工具,如何把花样百出的 PDF 变成干净的 Markdown?

如果你做过 RAG,或者试过把 PDF 喂给大模型,大概率遇到过这些翻车现场:

🔥 论文里的公式变成了乱码
🔥 表格被拆成一行一行的碎片文字
🔥 双栏排版的文章,读到一半突然跳到右边那栏
🔥 扫描版 PDF 和电子版 PDF,得用完全不同的方式处理

这些问题的根源只有一个——

🎯 PDF 是为“看起来一样”而生的,不是为“读起来对”而生的。

上海人工智能实验室开源的 MinerU,正是为了解决这个难题。它能把各种花样的 PDF——论文、教科书、财报、试卷、PPT、历史文献——统统变成干净的 Markdown 或结构化 JSON。

📢 提示:MinerU 支持多种解析后端(pipeline、hybrid、vlm)。本文拆解的是最基础、最核心的 pipeline 后端,其余暂不涉及。

它是怎么做到的?下面拆开它的四阶段管线,把核心思路讲清楚。


一、📐 一张图看清全局

MinerU Pipeline Overview

MinerU 的处理流程从上到下分成四个阶段,每个阶段的产出就是下一阶段的输入,数据像流水线一样从头流到尾:

阶段核心任务关键词
预处理把 PDF 变成模型能“看”的东西渲染图片、判定扫描/数字
内容解析用模型检测和识别所有内容Layout 总指挥 + 四个专业模型
后处理把孤立的框变成有结构的内容框归属、段落合并、阅读顺序
格式转换渲染为最终输出Markdown / JSON

整个管线内部还有一个“中间格式”(middle JSON),作为各阶段之间的约定——这个设计在后面会展开聊。


二、🎨 预处理:把 PDF 变成模型能“看”的东西

这个阶段的逻辑很直白:所有后续模型都是视觉模型,需要像素输入。 所以第一步就是把 PDF 的每一页渲染成图片。

但渲染之前要先做一件事——判断这个 PDF 是“扫描件”还是“数字 PDF”

类型特征后续文字来源
扫描件本质是图片,文字“画”在上面需要 OCR 识别
数字 PDF有文字层,可以选中和复制直接从 PDF 文本层读取

这个判定很关键——它决定了后面文字是从 OCR 来,还是从 PDF 文本层直接提取。两条路径在后面的阶段会走不同的路。

💡 统一渲染成图片还有一个隐藏好处:不同 PDF 生成器写出来的文本层格式天差地别,统一用图片做输入,直接绕开了这些格式差异。


三、🧠 内容解析:Layout 做“总指挥”,五个模型各司其职

这是整个管线里最“重”的阶段——调用了五个 ML 模型,但分工很清晰。

3.1 核心思路

Layout Detection 先跑,画出一页的布局图谱——哪里是标题、哪里是正文、哪里是表格、哪里是公式、哪里是图片。然后各专业模型按图谱各司其职:

模型做什么输入输出
Layout Detection检测所有区域类型和位置整页图片label + bbox + 阅读顺序
Formula Recognition识别公式内容公式区域裁剪图LaTeX 字符串
Table Recognition识别表格结构和内容表格区域裁剪图HTML table
Text OCR识别文字文字区域裁剪图文字 + 坐标
Seal OCR识别印章印章区域裁剪图印章文字

Layout Detection 效果示例

📊 Layout Detection 的实际效果:一页论文被切分成 title、text、table、formula、image 等区域,每个区域的类型、位置和阅读顺序都被精确标注。

3.2 为什么 Layout Detection 必须先跑?

如果不用 Layout Detection,直接把整页丢给 OCR,会发生什么?看看论文里的对比:

OCR without Layout vs with Layout

👈 左:没有 Layout Detection,多栏文档的文字顺序完全乱了。 👉 右:加了 Layout Detection,先检测出每一栏的边界,再在栏内逐行 OCR,顺序就对了。

这个对比说明了一个简单的道理:OCR 只管“认出文字”,不管“文字之间的先后关系”——后者是 Layout Detection 和后处理阶段要解决的问题。

3.3 公式处理的巧思

MinerU 把公式分成两类处理:

  • 行间公式(独占一行的公式块):Layout Detection 直接检测出来,交给公式识别模型转为 LaTeX。
  • 行内公式(嵌在文字里的公式,如 $x^2+y^2=z^2$):Layout Detection 之后,还有一个专门的公式检测模型(YOLOv8 fine-tune),负责在文字区域里把行内公式抠出来。

为什么要单独一个模型?因为 100cm²(α₁,α₂,...,αₙ) 这类符号,Layout Detection 很难跟普通文字区分开。一旦漏检,直接丢给 OCR(或从 PDF 文本层硬读),公式符号大概率变成乱码。更稳的做法是:先把它检测出来、掩码掉,等文字提取跑完,再把 LaTeX 结果回填进去。

行内公式检测对扫描件和数字 PDF 同样必要。数字 PDF 虽然文字层自带坐标,但公式符号在里面常常是乱码或直接缺失——不同 PDF 生成器对数学符号的编码五花八门,谁也说不准会遇到哪种写法。所以不管文字从哪来,行内公式都得靠专门的检测 + 识别模型来兜底。

行内公式检测效果

3.4 扫描件 vs 数字 PDF:分化点

到这里,两种 PDF 的路线开始分化。注意:Layout Detection、Formula Detection/Recognition、Table Recognition 完全相同——不管什么 PDF,公式和表格都得靠模型来认。差异只在于正文文字怎么来:

 扫描件数字 PDF
Layout / Formula / Table与数字 PDF 完全相同与扫描件完全相同
Text OCR全页面文字检测+识别只对表格内文字、印章等特殊情况
阶段产出layout_dets 里带 ocr_textlayout_dets 只有区域框,没有正文文字

数字 PDF 的正文文字留到后处理阶段从 PDF 文本层直接读。

举个具体例子。一页内容包含标题、正文(嵌了一个行内公式 $E=mc^2$)、表格。两种 PDF 经过 Content Parsing 后的 layout_dets 分别长这样:

扫描件——正文由 OCR 逐段识别,文字直接挂在结果里:

1
2
3
4
5
6
7
8
9
10
[
  {"index": 1, "bbox": [100,50,400,80],   "label": "title"},
  {"index": 2, "bbox": [100,100,400,210],  "label": "text"},
  {"index": 3, "bbox": [100,230,400,370],  "label": "table",  "html": "<table>…</table>"},
  {"bbox": [200,130,240,150],   "label": "ocr_text",         "text": "这是正文,其中"},
  {"bbox": [245,130,290,150],   "label": "ocr_text",         "text": "E=mc"},
  {"bbox": [295,130,320,150],   "label": "ocr_text",         "text": "2"},
  {"bbox": [325,130,400,150],   "label": "ocr_text",         "text": " 是一个著名公式"},
  {"bbox": [245,128,320,152],   "label": "inline_formula",   "latex": "E=mc^2"}
]

数字 PDF——正文不在这里,只有区域框和公式:

1
2
3
4
5
6
[
  {"index": 1, "bbox": [100,50,400,80],   "label": "title"},
  {"index": 2, "bbox": [100,100,400,210],  "label": "text"},
  {"index": 3, "bbox": [100,230,400,370],  "label": "table",  "html": "<table>…</table>"},
  {"bbox": [245,128,320,152],   "label": "inline_formula",   "latex": "E=mc^2"}
]

两种一对比就很清楚:扫描件里那一堆 ocr_text 是 OCR 逐段吐出来的正文;数字 PDF 里正文完全不在——等后处理阶段从文本层提取。但行内公式 inline_formula 两边都在,因为公式符号从文字层读出来大概率是乱的,必须靠模型来认。


四、🔧 后处理:把孤立的框变成有结构的内容

这是整个管线里最体现“工程味”的阶段。上一步模型输出的是一堆孤立的 label + bbox——“这块是标题”、“那块是正文”、“这个框是表格”——但块与块之间的关系?完全不知道。

后处理分两步:先逐页结构化,再跨页整理。

4.1 逐页结构化:MagicModel

核心任务是把文字 span 按 bbox 重叠关系匹配到对应 block,然后把零散的 span 聚合成行、行聚合成段落。有几个关键操作:

1. Visual Block 的归属分类

模型输出的原始标签是孤立的:

1
2
3
4
5
6
[image]      ← 这是一张图
[caption]    ← 这是图注还是表注?不知道
[text]       ← 这是正文还是脚注?不知道
[table]      ← 这是一张表
[caption]
[footnote]

MagicModel 基于空间位置和阅读顺序,把 caption 和 footnote 归属到正确的 image/table/chart block 下。 找不到归属的 caption 会被降级为普通 text,避免错乱。

2. 过滤与清洗

header、footer、页码、边栏——这些对理解内容没有帮助的东西,会在 MagicModel 阶段被识别出来,直接丢弃。

3. 两条路径的汇合点

这是整个管线设计的精髓:不管扫描件还是数字 PDF,到了 MagicModel 这一步,处理逻辑完全一致。 唯一的区别只在入口。扫描件的文字,从 Content Parsing 阶段的 OCR 结果里直接取;数字 PDF 则要等到这一步,才通过 txt_spans_extract 从文本层提取(字符自带坐标)。

为什么数字 PDF 不早点提取文字?因为 Layout Detection 需要先知道每一页有哪些区域,才能给文字提取划定范围。与其提前“盲读”,不如等 Layout 图谱画好了按图索骥。

4.2 跨页文档级处理

逐页搞完之后,以整篇文档的视角再做一遍梳理:

步骤做什么有意思的地方
段落拆分合并把相邻的同类 block 合并,跨页段落也接上纯几何启发式——看对齐、缩进、空格模式
列表/目录识别区分普通段落和列表靠几何特征:数字前缀、行尾模式、悬挂缩进
跨页表格合并拼接被分到两页的大表格用表格结构特征判断是不是同一张表
标题层级整理大标题、小标题的层级关系基于字体大小、位置和序号规则
公式编号配对如 “E=mc² (1)” 中公式和编号的关联基于 bbox 距离和编号格式

💡 段落拆分和列表识别完全不用 NLP,纯粹是几何启发式规则——靠行间距、缩进差、对齐方式这些视觉特征,不看语义。方法简单,但在排版规范的文档上表现很好。

输入: Content Parsing 产出的 layout_dets 输出: middle_json——MinerU 内部的“中间约定”,三个核心字段:

1
2
3
4
middle_json
├─ preproc_blocks    ← MagicModel 处理后的结构化 block 列表
├─ discarded_blocks  ← 被丢弃的 header/footer/页码
└─ para_blocks       ← 段落合并和列表识别后的最终结构

五、📝 格式转换:中间的 JSON,变成最终的 Markdown

最后一步是按 block 类型逐个分发、渲染:

Block 类型渲染方式
title# × level(如 ## 二级标题
text / abstract段落文本,CJK 不加空格,单词间加空格
inline_equation$latex$
interline_equation$$latex$$,没识别出 LaTeX 就降级为图片
image / chart![caption](path)
table直接嵌入 HTML(Markdown 表格能力有限)
code` lang `,自动检测编程语言
list / index按列表项换行分隔

MinerU 支持四种输出模式:

模式说明
MM_MD多模态 Markdown,保留图片、表格等全部内容
NLP_MD纯文本 Markdown,去掉图片和表格
Content List v1/v2结构化 JSON,v2 类型划分更细

来看论文里的实际效果:

MinerU Extraction Showcase

📊 四行对应四种文档类型(学术论文、教科书、试卷、研究报告),三列分别是 Layout Detection 检测结果 → span 识别结果 → 最终 Markdown。不同类型的文档在每一步都得到了准确定位和提取。


六、✨ 总结

MinerU 做对了几件事:

四阶段分层设计 —— 每层解决一个层次的问题,产出明确、边界清晰。

模型 + 规则的混合策略 —— 模型做它擅长的“检测”,规则做它擅长的“理解”,各取所长。

扫描件和数字 PDF 的分合处理 —— 两条路径,一条主线,汇合后完全统一。

多样本数据驱动的模型训练 —— Layout Detection 在教科书等复杂文档上远超通用模型,靠的是多样性数据的迭代训练。


当然,MinerU 也有局限性:目前主要支持中英文,其他语言效果不保证;遇到复杂手写、极端排版,质量也会打折扣。

但这些局限不影响它代表的工程思路——

🎯 用专业模型处理各自擅长的问题,用规则把结果缝合起来。
🎯 不要指望一个“通吃”的模型解决一切。

对于做 RAG 或文档处理的同学来说,理解 MinerU 的管线设计,比自己从头踩一遍坑划算得多。


参考:

  • MinerU 论文:MinerU: An Open-Source Solution for Precise Document Content Extraction
  • 源码分析:基于 MinerU 源码仓库的 pipeline 模块逐层拆解
本文由作者按照 CC BY 4.0 进行授权