做 RAG 最头疼的不是向量检索,是 PDF 解析。表格变乱码、公式变图片、多栏读成一条线。MinerU 用三后端架构解决:pipeline 纯 CPU 无幻觉、vlm 精度最高、hybrid 两者结合。
MinerU 的表格识别不是 OCR 后正则匹配,是模型级结构理解。TableStructureRec 做结构识别输出 HTML,支持 rowspan/colspan。跨页表格自动合并,表格内的图片和公式也能识别。自动提取表格标题和脚注。这是把 PDF 表格变成 LLM 可用结构化数据的正确路径。

本文从实际测评的角度,分享使用MinerU做PDF解析的技战步骤,以及如何做表格识别和解析,话不多说上干货。
第一部分:PDF 解析实战指南
1.1 安装与配置
方式一:pip / uv 安装(推荐)
# 安装 uv 包管理器
pip install --upgrade pip
pip install uv
# 安装 MinerU 全功能包
uv pip install -U "mineru[all]"
方式二:源码安装
git clone https://github.com/opendatalab/MinerU.git
cd MinerU
uv pip install -e .[all]
方式三:Docker 部署(仅 Linux / WSL2)
# 参考 https://opendatalab.github.io/MinerU/quick_start/docker_deployment/
模型源配置(国内环境必设):
# 切换到 ModelScope 模型源,避免 HuggingFace 网络问题
export MINERU_MODEL_SOURCE=modelscope
系统要求: Python 3.10-3.13(Windows 因 ray 不支持 3.13,限 3.10-3.12)| 最低 16GB RAM(推荐 32GB+)| 20GB 磁盘空间(推荐 SSD)
1.2 CLI 基础用法
最简用法(GPU 环境,默认 hybrid-engine 后端):
```bash
mineru -p input.pdf -o output/
```
纯 CPU 环境(指定 pipeline 后端):
```bash
mineru -p input.pdf -o output/ -b pipeline
```
完整参数列表:
mineru --help
# 关键参数说明:
# -p, --path 输入文件路径或目录(必填)
# -o, --output 输出目录(必填)
# -b, --backend 解析后端:pipeline | vlm-engine | hybrid-engine | vlm-http-client | hybrid-http-client
# 默认:hybrid-engine
# -m, --method 解析方法:auto(默认)| txt | ocr(仅 pipeline 和 hybrid)
# --effort hybrid 解析力度:medium(默认)| high
# -l, --lang 文档语言(提升 OCR 精度,仅 pipeline)
# -s, --start 起始页码(0-based)
# -e, --end 结束页码(0-based)
# -f, --formula 是否启用公式解析(默认启用)
# -t, --table 是否启用表格解析(默认启用)
# --image-analysis 是否启用图片/图表分析(VLM 和 hybrid 后端,默认启用)
# --api-url 连接已有 mineru-api 服务(省略则自动启动临时服务)
实际使用示例:
# 示例 1:解析单页 PDF 的第 2-5 页,使用 vlm-engine 后端
mineru -p report.pdf -o output/ -b vlm-engine -s 1 -e 4
# 示例 2:批量解析目录下所有 PDF,CPU 模式
mineru -p ./pdfs/ -o ./output/ -b pipeline
# 示例 3:hybrid-engine + high effort(最高精度)
mineru -p thesis.pdf -o output/ -b hybrid-engine --effort high
# 示例 4:关闭公式解析,仅提取文本和表格
mineru -p data.pdf -o output/ -f false -t true
# 示例 5:连接远程 API 服务
mineru -p input.pdf -o output/ --api-url http://192.168.1.100:8000
1.3 三种后端选择策略
| 后端 | 精度(OmniDocBench v1.6) | 硬件需求 | 适用场景 |
| pipeline | 86.47 | CPU 可跑 / 4GB VRAM | 快速处理、无 GPU 环境、无幻觉要求 |
| vlm-engine | 95.30 | 8GB VRAM | 最高精度、复杂版面、扫描文档 |
| hybrid-engine | 95.26-95.39 | 2GB VRAM(hybrid) | 生产推荐、原生文本+VLM 补充、低幻觉 |
| vlm-http-client | 同 vlm | 无需本地 torch | 轻量客户端连接远程 VLM 服务 |
| hybrid-http-client | 同 hybrid | 本地 pipeline + 远程 VLM | 分布式部署、降低本地显存 |
选择建议:
- 有 GPU + 追求精度 →
hybrid-engine --effort high(95.39 分,原生文本提取低幻觉) - 有 GPU + 追求速度 →
hybrid-engine --effort medium(精度仅降 0.13,速度提升 35-220%) - 无 GPU →
pipeline(86.47 分,纯 CPU 可跑) - 远程部署 →
vlm-http-client或hybrid-http-client
1.4 PDF 解析输出结构
执行 mineru -p demo.pdf -o output/ 后,输出目录结构如下:

Markdown 输出示例(demo.md):
# 深度学习在医学影像中的应用
近年来,深度学习技术在医学影像分析领域取得了显著进展。卷积神经网络(CNN)
能够自动从医学图像中提取特征,辅助医生进行疾病诊断。
## 实验结果
实验结果如表 1 所示:
<table>
<tr><td rowspan="2">模型</td><td colspan="2">准确率 (%)</td></tr>
<tr><td>验证集</td><td>测试集</td></tr>
<tr><td>ResNet-50</td><td>94.2</td><td>92.8</td></tr>
<tr><td>DenseNet-121</td><td>95.1</td><td>93.6</td></tr>
</table>
表 1 不同模型在医学影像数据集上的性能对比
损失函数定义为:
$$
L = -\frac{1}{N} \sum_{i=1}^{N} \left[ y_i \log(\hat{y}_i) + (1-y_i) \log(1-\hat{y}_i) \right]
$$
其中 $N$ 为样本数量,$y_i$ 为真实标签,$\hat{y}_i$ 为预测概率。
要点: 标题层级保留、表格以 HTML 嵌入 Markdown、公式转为 LaTeX(行间
$$...$$,行内$...$)、阅读顺序正确、图片提取到images/目录并在 Markdown 中引用。
1.5 表格识别详解
1.5.1 表格识别技术原理
MinerU 的表格识别不是 OCR 后正则匹配,而是模型级结构理解:
PDF 页面
→ 版面分析(识别表格区域 bbox)
→ TableStructureRec 模型(表格结构识别)
→ 输出 HTML <table> 结构(含 rowspan/colspan)
→ 自动提取表格标题(table_caption)和脚注(table_footnote)
→ 跨页表格自动合并
核心技术组件:
- TableStructureRec(来自 RapidAI):表格结构识别模型,将表格图片转为 HTML 结构
- 跨页表格合并:MinerU2.5-Pro-2604+ 模型支持,自动检测并合并跨页的表格
- 表格内图片识别:识别表格单元格中的图片内容
- 表格内公式解析:识别表格单元格中的数学公式并转为 LaTeX
1.5.2 表格输出格式
在 content_list.json 中的表示:
{
"type": "table",
"img_path": "images/e3cb4133.jpg",
"table_caption": [
"Table 2 Significance of the rainfall and time terms"
],
"table_footnote": [
"* indicates that the rainfall term was significant at the 0.05 level"
],
"table_body": "<html><body><table><tr><td rowspan=\"2\">Site</td><td colspan=\"10\">Percentile</td></tr><tr><td>10</td><td>20</td><td>30</td><td>40</td><td>50</td><td>60</td><td>70</td><td>80</td><td>90</td><td>100</td></tr><tr><td>Traralgon Ck</td><td>P</td><td>P,*</td><td>P</td><td>P</td><td>P</td><td>P</td><td>P,*</td><td>P,*</td><td>P,*</td><td>P,*</td></tr></table></body></html>",
"bbox": [62, 480, 946, 904],
"page_idx": 5
}
字段说明:
| 字段 | 说明 |
type | 固定为 "table" |
img_path | 表格区域截图路径 |
table_caption | 表格标题文本列表 |
table_footnote | 表格脚注文本列表 |
table_body | HTML 格式的表格内容,支持 rowspan/colspan |
bbox | 边界框坐标,归一化到 0-1000 范围 |
page_idx | 所在页码(0-based) |
在 content_list_v2.json 中的表示(3.0+ 统一格式):
{
"type": "table",
"content": {
"table_body": "<html><body><table>...</table></body></html>",
"table_caption": ["Table 1 性能对比"],
"table_footnote": ["注:数据来源于实验环境"],
"img_path": "images/table_001.jpg"
},
"bbox": [62, 480, 946, 904]
}
1.5.3 表格识别实际场景案例
场景一:简单表格
原始 PDF 中的表格:
| 模型 | 准确率 | F1 |
| BERT | 92.1 | 91.5 |
| RoBERTa | 93.5 | 92.8 |
MinerU 输出的 HTML:
<html><body><table>
<tr><td>模型</td><td>准确率</td><td>F1</td></tr>
<tr><td>BERT</td><td>92.1</td><td>91.5</td></tr>
<tr><td>RoBERTa</td><td>93.5</td><td>92.8</td></tr>
</table></body></html>
场景二:合并单元格表格
原始 PDF 中带有跨行跨列的表格:
<html><body><table>
<tr><td rowspan="2">方法</td><td colspan="2">数据集 A</td><td colspan="2">数据集 B</td></tr>
<tr><td>Precision</td><td>Recall</td><td>Precision</td><td>Recall</td></tr>
<tr><td>CNN</td><td>0.85</td><td>0.82</td><td>0.78</td><td>0.75</td></tr>
<tr><td>Transformer</td><td>0.91</td><td>0.89</td><td>0.86</td><td>0.83</td></tr>
</table></body></html>
rowspan="2"和colspan="2"被正确识别,表头层级结构完整保留。
场景三:跨页表格
一篇论文中的大型表格跨越第 3 页和第 4 页。MinerU2.5-Pro-2604+ 模型自动检测跨页表格并合并为单个完整表格,输出在表格结束页对应的 content_list 条目中,table_caption 和 table_footnote 分别从上下文提取。
场景四:表格内含公式
原始表格单元格中包含数学公式(如 \alpha + \beta = 1),MinerU 会将公式部分转为 LaTeX,嵌入 HTML 表格单元格中:
<tr><td>约束条件</td><td>$\alpha + \beta = 1$</td></tr>
1.5.4 表格识别开关与调优
通过 CLI 参数控制:
# 关闭表格识别(仅提取文本)
mineru -p input.pdf -o output/ -t false
# 确保表格识别开启(默认已开启)
mineru -p input.pdf -o output/ -t true
通过环境变量控制:
# 关闭表格识别
export MINERU_TABLE_ENABLE=false
# 关闭表格合并功能(跨页表格不合并)
export MINERU_TABLE_MERGE_ENABLE=false
调优建议:
| 场景 | 推荐配置 |
| 表格密集型文档(财报、论文) | hybrid-engine --effort high,表格合并开启 |
| 无表格文档(纯文本报告) | -t false 关闭表格识别,提升速度 |
| 扫描件表格 | -b hybrid-engine -m ocr,OCR + VLM 双引擎 |
| 超大表格跨页 | 确保 MINERU_TABLE_MERGE_ENABLE=true(默认) |
1.6 Python SDK 调用示例
同步解析(通过本地 API 服务):
import subprocess
import requests
import json
import os
# 启动本地 mineru-api 服务(或手动运行 mineru-api --port 8000)
# 方式一:直接使用 CLI
# subprocess.run(["mineru", "-p", "input.pdf", "-o", "output/"])
# 方式二:通过 API 调用
# 1. 提交异步任务
with open("input.pdf", "rb") as f:
response = requests.post(
"http://127.0.0.1:8000/tasks",
files={"files": f},
data={"return_md": "true"}
)
task_id = response.json()["task_id"]
print(f"Task submitted: {task_id}")
# 2. 轮询任务状态
import time
while True:
status_resp = requests.get(f"http://127.0.0.1:8000/tasks/{task_id}")
status = status_resp.json()
if status["status"] == "completed":
break
elif status["status"] == "failed":
print(f"Task failed: {status}")
exit(1)
time.sleep(2)
# 3. 获取结果
result = requests.get(f"http://127.0.0.1:8000/tasks/{task_id}/result")
print(f"Result: {json.dumps(result.json(), indent=2, ensure_ascii=False)}")
同步解析(兼容旧接口):
import requests
# POST /file_parse 会等待任务完成并同步返回结果
with open("input.pdf", "rb") as f:
response = requests.post(
"http://127.0.0.1:8000/file_parse",
files={"files": f},
data={
"return_md": "true", # 返回 Markdown
"response_format_zip": "true", # 以 ZIP 格式返回
"return_original_file": "true" # 包含原始文件
}
)
# 结果保存到本地
with open("result.zip", "wb") as out:
out.write(response.content)
读取解析结果并提取表格:
import json
# 读取 content_list.json
with open("output/auto/input_content_list.json", "r", encoding="utf-8") as f:
content_list = json.load(f)
# 提取所有表格
tables = [item for item in content_list if item.get("type") == "table"]
for i, table in enumerate(tables):
print(f"\n=== 表格 {i+1} ===")
print(f"标题: {table.get('table_caption', '无')}")
print(f"脚注: {table.get('table_footnote', '无')}")
print(f"HTML:\n{table.get('table_body', '无')}")
print(f"页码: {table.get('page_idx', '未知')}")
1.7 API 服务部署
单机部署:
# 启动 FastAPI 服务
mineru-api --host 0.0.0.0 --port 8000
# 预加载 VLM 模型(避免首次请求延迟)
mineru-api --host 0.0.0.0 --port 8000 --enable-vlm-preload true
多 GPU 负载均衡部署:
# 启动 router,自动管理本地 GPU workers
mineru-router --host 0.0.0.0 --port 8002 --local-gpus auto
# 或聚合多个已有 mineru-api 服务
mineru-router --host 0.0.0.0 --port 8002 \
--upstream-url http://192.168.1.101:8000 \
--upstream-url http://192.168.1.102:8000
OpenAI 兼容服务(远程 VLM 推理):
# 终端 1:启动 OpenAI 兼容服务(需要 vllm 或 lmdeploy)
mineru-openai-server --port 30000
# 终端 2:通过 http-client 连接
mineru -p input.pdf -o output/ -b hybrid-http-client -u http://127.0.0.1:30000
Gradio WebUI:
mineru-gradio –server-name 0.0.0.0 –server-port 7860
1.8 性能调优建议
| 调优维度 | 参数/配置 | 说明 |
| 精度优先 | -b hybrid-engine --effort high | OmniDocBench 95.39 分,最高精度 |
| 速度优先 | -b hybrid-engine --effort medium | 精度仅降 0.13,速度提升 35-220% |
| CPU 环境 | -b pipeline | 纯 CPU 可跑,86.47 分 |
| 长文档 | MINERU_PROCESSING_WINDOW_SIZE=64 | 滑动窗口大小,增大提升吞吐但增加内存 |
| 并发处理 | MINERU_API_MAX_CONCURRENT_REQUESTS=3 | API 服务最大并发数,按 GPU 显存调整 |
| PDF 渲染 | MINERU_PDF_RENDER_THREADS=4 | PDF 渲染线程数,按 CPU 核数调整 |
| 渲染超时 | MINERU_PDF_RENDER_TIMEOUT=300 | PDF 渲染超时(秒),超大文件需增大 |
| hybrid 显存 | MINERU_HYBRID_BATCH_RATIO=4 | VLM 批处理比例,显存不足时降低 |
| 关闭无用功能 | -f false(关闭公式)/ -t false(关闭表格) | 无公式/表格的文档可加速 |
| OCR 优化 | -l ch(指定语言) | 仅 pipeline 后端,指定语言提升 OCR 精度 |
生产环境推荐配置:
# 典型生产部署:hybrid-engine + medium effort + 8GB VRAM GPU
export MINERU_MODEL_SOURCE=modelscope
export MINERU_PROCESSING_WINDOW_SIZE=64
export MINERU_API_MAX_CONCURRENT_REQUESTS=3
mineru-api --host 0.0.0.0 --port 8000 --enable-vlm-preload true
第二部分:表格识别专项解析
2.1 表格识别技术栈全景
MinerU 的表格识别能力由以下技术组件协同实现:
输入 PDF
│
├─ pypdfium2 → PDF 页面渲染为图像
│
├─ 版面分析模型 → 识别表格区域(bbox)
│
├─ TableStructureRec(RapidAI)→ 表格结构识别
│ ├─ 行列结构检测
│ ├─ 合并单元格识别(rowspan/colspan)
│ └─ 输出 HTML <table> 结构
│
├─ PP-OCRv6(PaddleOCR)→ 表格内文字识别
│ └─ 支持 109 种语言
│
├─ UniMERNet → 表格内公式识别
│ └─ 输出 LaTeX
│
├─ MinerU2.5-Pro VLM → 跨页表格合并 + 表格内图片识别
│
└─ 上下文提取 → table_caption + table_footnote
2.2 各后端表格识别能力对比
| 能力 | pipeline | vlm-engine | hybrid-engine |
| 基础表格结构识别 | 支持 | 支持 | 支持 |
| 合并单元格(rowspan/colspan) | 支持 | 支持 | 支持 |
| 表格标题/脚注提取 | 支持 | 支持 | 支持 |
| 跨页表格合并 | 不支持 | 支持(2604+) | 支持(2604+) |
| 表格内图片识别 | 不支持 | 支持(2604+) | 支持(2604+) |
| 表格内公式识别 | 支持 | 支持 | 支持 |
| 纯 CPU 运行 | 支持 | 不支持 | 不支持 |
| 幻觉风险 | 无 | 有(VLM 生成) | 低(原生文本提取) |
2.3 表格识别输出在不同格式中的差异
Markdown 输出(.md 文件):
- 表格以 HTML
<table>标签嵌入 Markdown - 表格标题和脚注以普通文本出现在表格前后
- 适合直接用于 LLM 上下文或 RAG 文档库
content_list.json(简化版,按阅读顺序):
- 每个表格是一个独立 JSON 对象
table_body字段存储 HTMLtable_caption和table_footnote为字符串列表- 适合程序化处理和二次开发
middle.json(完整结构化数据):
- 表格作为
para_blocks中的table类型块 - 包含
table_body、table_caption、table_footnote二级块 - 保留完整 bbox 坐标和层级关系
- 适合需要精确版面位置信息的场景
content_list_v2.json(3.0+ 统一格式):
- 使用
type + content统一结构 - 所有后端均生成此文件
- 适合跨后端的统一数据处理流程
2.4 常见表格识别问题与排查
| 问题 | 可能原因 | 解决方案 |
| 表格未被识别 | 版面分析未检测到表格区域 | 切换 vlm-engine 或 hybrid-engine 后端 |
| 表格结构错误 | TableStructureRec 模型对复杂表格识别不准 | 使用 --effort high 提升精度 |
| 跨页表格未合并 | VLM 模型版本过低或后端不支持 | 确保 vlm-engine/hybrid-engine + MinerU2.5-Pro-2604+ |
| 表格内文字丢失 | OCR 未正确识别 | 指定语言 -l ch 或切换 hybrid-engine |
| 表格内公式未转换 | 公式解析未启用 | 确保 -f true(默认启用) |
| 表格 HTML 格式不规范 | 模型输出异常 | 检查 layout.pdf 可视化确认表格区域 |
| 输出无表格 | 表格识别被关闭 | 检查 -t 参数和 MINERU_TABLE_ENABLE 环境变量 |
2.5 与 RAG 框架集成示例
与 LangChain 集成:
from langchain.text_splitter import MarkdownHeaderTextSplitter
import json
# 1. 使用 MinerU 解析 PDF 得到 Markdown
# mineru -p paper.pdf -o output/ -b hybrid-engine --effort high
# 2. 读取 Markdown
with open("output/auto/paper.md", "r", encoding="utf-8") as f:
markdown_content = f.read()
# 3. 按标题层级切分
header_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
]
)
chunks = header_splitter.split_text(markdown_content)
# 4. 表格作为独立 chunk 保留(HTML 格式可直接被 LLM 理解)
for chunk in chunks:
print(f"Section: {chunk.metadata}")
print(f"Content: {chunk.page_content[:200]}...")
与 LlamaIndex 集成:
from llama_index.core import Document, VectorStoreIndex
import json
# 1. 读取 content_list.json
with open("output/auto/paper_content_list.json", "r", encoding="utf-8") as f:
content_list = json.load(f)
# 2. 将每个内容块转为 Document
documents = []
for item in content_list:
if item["type"] == "text":
documents.append(Document(text=item["content"]))
elif item["type"] == "table":
# 表格以 HTML 格式作为文档
table_text = f"表格标题: {item.get('table_caption', '')}\n"
table_text += f"表格内容: {item['table_body']}\n"
table_text += f"脚注: {item.get('table_footnote', '')}"
documents.append(Document(text=table_text))
# 3. 构建索引
index = VectorStoreIndex.from_documents(documents)



