MinerU —- PDF 解析实战指南与表格识别解析

做 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)硬件需求适用场景
pipeline86.47CPU 可跑 / 4GB VRAM快速处理、无 GPU 环境、无幻觉要求
vlm-engine95.308GB VRAM最高精度、复杂版面、扫描文档
hybrid-engine95.26-95.392GB 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_bodyHTML 格式的表格内容,支持 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
BERT92.191.5
RoBERTa93.592.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 highOmniDocBench 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=3API 服务最大并发数,按 GPU 显存调整
PDF 渲染MINERU_PDF_RENDER_THREADS=4PDF 渲染线程数,按 CPU 核数调整
渲染超时MINERU_PDF_RENDER_TIMEOUT=300PDF 渲染超时(秒),超大文件需增大
hybrid 显存MINERU_HYBRID_BATCH_RATIO=4VLM 批处理比例,显存不足时降低
关闭无用功能-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 各后端表格识别能力对比

能力pipelinevlm-enginehybrid-engine
基础表格结构识别支持支持支持
合并单元格(rowspan/colspan)支持支持支持
表格标题/脚注提取支持支持支持
跨页表格合并不支持支持(2604+)支持(2604+)
表格内图片识别不支持支持(2604+)支持(2604+)
表格内公式识别支持支持支持
纯 CPU 运行支持不支持不支持
幻觉风险无有(VLM 生成)低(原生文本提取)

2.3 表格识别输出在不同格式中的差异

Markdown 输出(.md 文件):

  • 表格以 HTML <table> 标签嵌入 Markdown
  • 表格标题和脚注以普通文本出现在表格前后
  • 适合直接用于 LLM 上下文或 RAG 文档库

content_list.json(简化版,按阅读顺序):

  • 每个表格是一个独立 JSON 对象
  • table_body 字段存储 HTML
  • table_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)

About

Your email will not be published. Name and Email fields are required