一、前言:为什么 PDF 解析是 AI 应用的"最后一公里"难题?

在大模型(LLM)与 RAG(检索增强生成)爆发的时代,如何把非结构化文档喂给 AI 已经成为工程落地的核心瓶颈。一个企业知识库里可能有成千上万份 PDF——学术论文、财务报告、产品手册、法律合同——这些文档中混杂着多栏排版、LaTeX 公式、复杂表格、嵌套图片、印章水印……

传统方案(PyMuPDF、pdfplumber、PDFMiner)在面对这类复杂 PDF 时,往往只能提取出乱序的文本流,公式变成乱码,表格结构全部丢失。

MinerU 的出现,正是为了彻底解决这个痛点。

MinerU 是由上海人工智能实验室 OpenDataLab 团队开发的开源高精度文档解析引擎,将非结构化文档(PDF、图片、Office 文件等)转换为机器可读的 Markdown 和 JSON,专为 LLM 预训练、RAG 和 Agent 工作流场景设计。

截至 2025 年,MinerU 在 GitHub 上已获得数万 Star,成为文档解析领域最受关注的开源项目之一。本文将从功能特性、安装配置、Python SDK 实战、性能评测四个维度,带你全面了解这款工具。


二、MinerU 核心功能一览

2.1 功能矩阵

MinerU 提供全面的文档解析能力,主要功能包括:

功能模块 支持能力 备注
版面分析 多栏排版、阅读顺序恢复 保留非正文元素(页眉页脚等)
文字识别 中英文 OCR、竖排文字 印章文字识别
公式识别 LaTeX 格式输出 行内/行间公式,含序号识别,准确率 92.5%
表格解析 旋转表格、无边框/半边框表格 表格内图片/公式解析
图片提取 自动提取并关联上下文 支持图片描述生成
输出格式 Markdown、JSON、PDF 结构化输出,LLM 友好
多端部署 本地 CLI、Python SDK、在线 API、Docker、桌面客户端 全场景覆盖

2.2 最新版本亮点(MinerU 2.x)

最新版本重点强化了以下能力:

  • 突破性公式解析:支持复杂长数学公式,准确识别中英文混合方程式
  • 增强表格鲁棒性:轻松处理旋转表格、无边框表格、跨页表格
  • 印章文字识别:专门针对中国文档中的圆形印章
  • 竖排文本支持:兼容日文、古汉语等竖排排版
  • 极低资源占用:在保持高精度的同时,大幅降低内存与算力消耗

三、安装与环境配置

3.1 环境要求

操作系统:Windows / Linux / macOS  
Python:3.10 ~ 3.13  
GPU(可选):8GB+ 显存(有 GPU 解析速度可提升 3~5x)  

3.2 标准安装(推荐)

# 升级 pip  
pip install --upgrade pip  

# 安装 uv(更快的包管理器)  
pip install uv  

# 使用 pip 安装 MinerU  
pip install mineru  

或使用国内镜像加速:

pip install mineru -i https://mirrors.aliyun.com/pypi/simple  

3.3 首次初始化(下载模型)

安装完成后,需要下载模型权重。执行以下命令,MinerU 会自动下载并配置 magic-pdf.json

# 自动下载所有模型(含 OCR、公式、表格模型)  
mineru-download-models  

⚠️ 模型下载完成后,会在用户目录生成 ~/magic-pdf.json 配置文件,可通过 MINERU_TOOLS_CONFIG_JSON 环境变量指定自定义路径。

3.4 命令行快速体验

安装完成后,最快的方式是使用 CLI:

# 解析单个 PDF,输出到 output 目录  
mineru -p your_document.pdf -o ./output  

# 启用 OCR 模式(适合扫描版 PDF)  
mineru -p your_document.pdf -o ./output --method ocr  

# 关闭公式识别(加速处理纯文字文档)  
mineru -p your_document.pdf -o ./output --formula false  

四、Python SDK 完整实战

4.1 最简单的调用方式(新版 API)

MinerU 2.x 提供了极简的高层 API:

import mineru  

# 方式一:直接调用顶层接口  
result = mineru.parse("your_document.pdf")  

# 输出 Markdown 内容  
print(result.markdown)  

# 输出 JSON 结构  
import json  
print(json.dumps(result.to_dict(), ensure_ascii=False, indent=2))  

4.2 基于 magic_pdf 的底层调用(精细控制)

对于需要精细控制解析过程的场景,可以使用 magic_pdf 核心库:

import os  
import json  
from pathlib import Path  
from loguru import logger  

from magic_pdf.data.data_reader_writer import FileBasedDataWriter, FileBasedDataReader  
from magic_pdf.data.dataset import PymuDocDataset  
from magic_pdf.model.doc_analyze_by_custom_model import doc_analyze  
from magic_pdf.config.enums import SupportedPdfParseMethod  

def parse_pdf_to_markdown(pdf_path: str, output_dir: str) -> str:  
    """  
    将 PDF 解析为 Markdown 格式  
    
    Args:  
        pdf_path: PDF 文件路径  
        output_dir: 输出目录  
    
    Returns:  
        Markdown 字符串  
    """  
    # 创建输出目录  
    pdf_name = Path(pdf_path).stem  
    output_path = os.path.join(output_dir, pdf_name)  
    os.makedirs(output_path, exist_ok=True)  
    
    # 图片输出子目录  
    image_dir = os.path.join(output_path, "images")  
    os.makedirs(image_dir, exist_ok=True)  

    # 初始化读写器  
    writer = FileBasedDataWriter(output_path)  
    image_writer = FileBasedDataWriter(image_dir)  
    reader = FileBasedDataReader("")  

    # 读取 PDF 字节流  
    pdf_bytes = reader.read(pdf_path)  

    # 构建数据集对象  
    ds = PymuDocDataset(pdf_bytes)  

    # 执行文档分析(自动选择 OCR 或文字层解析)  
    infer_result = doc_analyze(ds, ocr=True)  

    # 选择解析方法(AUTO / TXT / OCR)  
    pipe_result = infer_result.pipe_ocr_mode(image_writer)  

    # 导出 Markdown  
    md_content = pipe_result.get_markdown(image_dir)  
    
    # 保存 Markdown 文件  
    md_path = os.path.join(output_path, f"{pdf_name}.md")  
    with open(md_path, "w", encoding="utf-8") as f:  
        f.write(md_content)  

    # 导出 JSON(含完整结构信息)  
    content_list = pipe_result.get_content_list(image_dir)  
    json_path = os.path.join(output_path, f"{pdf_name}_content.json")  
    with open(json_path, "w", encoding="utf-8") as f:  
        json.dump(content_list, f, ensure_ascii=False, indent=2)  

    logger.info(f"解析完成!Markdown: {md_path}")  
    return md_content  

if __name__ == "__main__":  
    md = parse_pdf_to_markdown("paper.pdf", "./output")  
    print(md[:2000])  # 打印前 2000 字符预览  

4.3 批量处理多个 PDF

import os  
import glob  
from concurrent.futures import ThreadPoolExecutor, as_completed  
import mineru  

def batch_parse(input_dir: str, output_dir: str, max_workers: int = 4):  
    """  
    批量解析目录下所有 PDF  
    
    Args:  
        input_dir: 输入目录  
        output_dir: 输出目录  
        max_workers: 并发线程数  
    """  
    pdf_files = glob.glob(os.path.join(input_dir, "**/*.pdf"), recursive=True)  
    print(f"共发现 {len(pdf_files)} 个 PDF 文件,开始批量解析...")  

    results = {}  
    failed = []  

    def parse_single(pdf_path):  
        try:  
            result = mineru.parse(pdf_path)  
            return pdf_path, result.markdown, None  
        except Exception as e:  
            return pdf_path, None, str(e)  

    with ThreadPoolExecutor(max_workers=max_workers) as executor:  
        futures = {executor.submit(parse_single, f): f for f in pdf_files}  
        for future in as_completed(futures):  
            pdf_path, markdown, error = future.result()  
            filename = os.path.basename(pdf_path)  
            if error:  
                print(f"❌ 失败:{filename} | 错误:{error}")  
                failed.append(pdf_path)  
            else:  
                print(f"✅ 成功:{filename} | 字符数:{len(markdown)}")  
                # 保存结果  
                out_path = os.path.join(output_dir, filename.replace(".pdf", ".md"))  
                with open(out_path, "w", encoding="utf-8") as f:  
                    f.write(markdown)  
                results[pdf_path] = markdown  

    print(f"\n完成!成功:{len(results)} 个,失败:{len(failed)} 个")  
    return results  

# 调用示例  
batch_parse("./pdfs", "./output", max_workers=2)  

4.4 与 LangChain / RAG 集成

MinerU 官方提供了 langchain-mineru 集成包,可以直接嵌入 LangChain 管道:

from langchain_mineru import MinerULoader  
from langchain.text_splitter import RecursiveCharacterTextSplitter  
from langchain_openai import OpenAIEmbeddings  
from langchain_community.vectorstores import FAISS  

# 1. 使用 MinerU 加载并解析 PDF  
loader = MinerULoader("research_paper.pdf")  
documents = loader.load()  

print(f"成功加载 {len(documents)} 个文档块")  
print(f"首块内容预览:\n{documents[0].page_content[:500]}")  

# 2. 文本分块  
splitter = RecursiveCharacterTextSplitter(  
    chunk_size=512,  
    chunk_overlap=50  
)  
chunks = splitter.split_documents(documents)  
print(f"分块后共 {len(chunks)} 个片段")  

# 3. 构建向量库  
embeddings = OpenAIEmbeddings()  
vectorstore = FAISS.from_documents(chunks, embeddings)  

# 4. 相似度检索  
query = "这篇论文的核心贡献是什么?"  
relevant_docs = vectorstore.similarity_search(query, k=3)  
for i, doc in enumerate(relevant_docs):  
    print(f"\n--- 相关片段 {i+1} ---")  
    print(doc.page_content)  

五、性能评测:MinerU 在 OmniDocBench 上的表现

5.1 评测基准介绍

OmniDocBench 是目前最权威的 PDF 文档解析评测基准,由 OpenDataLab 团队在 CVPR 2025 发布。它涵盖:

  • 📄 9 种文档类型:学术论文、教材、PPT、试卷、财务报告等
  • 📊 评测维度:文字识别、公式识别、表格解析、版面分析
  • 📐 评测指标:Edit Distance、BLEU、TEDS(表格)、CDM(公式)

5.2 MinerU 综合评测得分

MinerU2.5-Pro 在 OmniDocBench v1.6 上取得了 SOTA(当前最优) 成绩:

模型/工具 OmniDocBench 综合得分 文字识别(英文 NED) 公式识别(CDM) 开源
MinerU2.5-Pro 95.69 0.061 高精度
MinerU2.5 (baseline) 92.98 0.092 高精度
Mathpix ~88 较好 优秀 ❌(商业)
Docling ~82 一般 较弱
Marker ~80 一般 较弱
LlamaParse Agentic 84.88 较好 较好 ❌(商业)

📌 关键数据:MinerU2.5-Pro 通过将训练数据从不足 1000 万样本扩展至 6550 万样本,在不修改任何模型架构的前提下,综合得分提升 2.71 分,超越所有现有方法(含商业工具)。

5.3 文字识别详细对比

根据 EulerAI 的深度评测报告,在 OmniDocBench 文字识别子任务上:

指标 MinerU Docling Marker Unstructured
英文 NED(↓越低越好) 0.061 0.142 0.118 0.203
中文 NED(↓越低越好) 0.215 0.389 0.312 0.467
排名 🥇 第一 第三 第二 第四

MinerU 在英文和中文文字识别上均排名第一。

5.4 速度与资源消耗横向对比

基于社区实测数据(20 页学术 PDF,RTX 3090 环境):

工具 平均耗时 GPU 显存占用 CPU 模式
MinerU(GPU) 8.3s ~6GB 支持(慢 3-5x)
Docling 12.1s ~4GB 支持
Marker 9.8s ~5GB 支持
LlamaParse ~5s(API) N/A(云端) N/A

⚠️ 注:速度数据来自社区基准测试,实际结果因文档复杂度和硬件环境存在差异。


六、典型使用场景与最佳实践

6.1 场景一:学术论文批量转换(RAG 预处理)

import mineru  
import os  

def papers_to_rag_corpus(paper_dir: str, output_file: str):  
    """将目录下所有论文 PDF 转为 RAG 语料库"""  
    corpus = []  
    for pdf in os.listdir(paper_dir):  
        if not pdf.endswith(".pdf"):  
            continue  
        try:  
            result = mineru.parse(os.path.join(paper_dir, pdf))  
            corpus.append({  
                "source": pdf,  
                "content": result.markdown,  
                "metadata": {  
                    "chars": len(result.markdown),  
                    "pages": result.page_count  
                }  
            })  
            print(f"✅ {pdf}: {result.page_count} 页,{len(result.markdown)} 字符")  
        except Exception as e:  
            print(f"❌ {pdf}: {e}")  
    
    import json  
    with open(output_file, "w", encoding="utf-8") as f:  
        json.dump(corpus, f, ensure_ascii=False, indent=2)  
    
    print(f"\n共处理 {len(corpus)} 篇论文 → {output_file}")  
    return corpus  

6.2 场景二:财务报告关键信息提取

import mineru  
import re  

def extract_financial_tables(pdf_path: str) -> list:  
    """从财务报告 PDF 中提取所有表格"""  
    result = mineru.parse(pdf_path)  
    
    # MinerU 输出的 Markdown 中,表格以标准 Markdown 表格格式呈现  
    # 使用正则提取所有表格块  
    table_pattern = r'(\|.+\|[\r\n]+(?:\|[-:| ]+\|[\r\n]+)(?:\|.+\|[\r\n]*)+)'  
    tables = re.findall(table_pattern, result.markdown)  
    
    print(f"共发现 {len(tables)} 个表格")  
    for i, table in enumerate(tables):  
        print(f"\n=== 表格 {i+1} ===")  
        print(table[:300])  # 预览前 300 字符  
    
    return tables  

tables = extract_financial_tables("annual_report_2024.pdf")  

6.3 场景三:数学论文公式提取

import mineru  
import re  

def extract_formulas(pdf_path: str) -> dict:  
    """提取 PDF 中的所有 LaTeX 公式"""  
    result = mineru.parse(pdf_path)  
    markdown = result.markdown  
    
    # 提取行间公式(
$$ ... $$

)  
    block_formulas = re.findall(r'\$\$(.*?)\$\$', markdown, re.DOTALL)  
    
    # 提取行内公式($...$)  
    inline_formulas = re.findall(r'(?<!\$)\$(?!\$)(.*?)(?<!\$)\$(?!\$)', markdown)  
    
    print(f"行间公式:{len(block_formulas)} 个")  
    print(f"行内公式:{len(inline_formulas)} 个")  
    
    # 打印前 5 个行间公式  
    for i, formula in enumerate(block_formulas[:5]):  
        print(f"\n公式 {i+1}:\n
$$ {formula.strip()} $$

")  
    
    return {  
        "block_formulas": block_formulas,  
        "inline_formulas": inline_formulas  
    }  

formulas = extract_formulas("math_paper.pdf")  

七、与竞品的综合对比

7.1 MinerU vs 主流方案对比表

维度 MinerU Docling Marker LlamaParse PyMuPDF
开源免费 ❌(收费)
公式识别(LaTeX) ✅ 优秀 ⚠️ 较弱 ⚠️ 较弱 ✅ 较好
复杂表格 ✅ 优秀 ✅ 较好 ⚠️ 一般 ✅ 优秀
中文支持 ✅ 原生 ⚠️ 有限 ⚠️ 有限 ✅ 支持 ⚠️ 基础
OmniDocBench 得分 95.69 ~82 ~80 84.88 N/A
本地部署
Python SDK
MCP 协议支持
LangChain 集成 ✅ 官方 ✅ 官方 ⚠️ 社区

7.2 选型建议

📌 优先选择 MinerU,当你需要:  
  ✅ 处理含大量公式的学术论文  
  ✅ 解析中文复杂排版文档  
  ✅ 构建企业私有化 RAG 系统(不能用云端 API)  
  ✅ 需要高精度表格识别  
  ✅ 预算有限,必须开源免费  

📌 考虑其他方案,当你需要:  
  → 极致处理速度、对精度要求不高 → Marker  
  → 极简集成 LlamaIndex → LlamaParse  
  → 仅做基础文字提取 → PyMuPDF  

八、常见问题与解决方案

Q1:解析速度很慢怎么办?

# 确认是否在使用 CPU 模式,建议切换到 GPU  
# 检查设备配置  
python -c "import torch; print(torch.cuda.is_available())"  

# 若无 GPU,可关闭公式和表格识别来加速  
mineru -p doc.pdf -o output --formula false --table false  

Q2:中文乱码或识别错误?

# 确保使用最新版本(2.x 对中文支持大幅改善)  
pip install --upgrade mineru  

# 对扫描版中文 PDF,显式启用 OCR 模式  
result = mineru.parse("chinese_doc.pdf", method="ocr")  

Q3:表格无法正确识别?

# 确认 table 功能已开启(默认开启)  
result = mineru.parse("doc.pdf", table=True)  

# 查看 JSON 输出中的表格结构  
for block in result.to_dict()["content"]:  
    if block["type"] == "table":  
        print(block["table_body"])  

九、总结

MinerU 已经从一个实验性工具成长为工业级文档解析引擎。在 OmniDocBench v1.6 上以 95.69 分的 SOTA 成绩证明了自身实力,在中英文文字识别上均排名第一,同时完全开源、支持本地部署、拥有完整的 Python SDK 与 LangChain 集成生态。

核心优势总结

🏆 领先优势 📊 量化数据
综合解析精度 OmniDocBench 95.69 分,全球 SOTA
英文文字识别 NED 仅 0.061,排名第一
公式识别准确率 92.5%,支持 LaTeX 输出
训练数据规模 6550 万样本,持续迭代
开源协议 Apache 2.0,商用友好

如果你正在构建 RAG 系统、LLM 训练语料管道、或者只是需要一个靠谱的 PDF 转 Markdown 工具,MinerU 是目前开源领域毫无疑问的首选


📎 项目地址:https://github.com/opendatalab/MinerU

📎 在线体验:https://mineru.net

📎 技术报告:https://arxiv.org/abs/2604.04771

如果本文对你有帮助,欢迎点赞收藏!有任何问题欢迎在评论区留言交流 🙌

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐