MinerU - 将非结构化文档(PDF、图片、Office 文件等)转换为机器可读的 Markdown 和 JSON
MinerU 是一款开源高精度文档解析引擎,将非结构化文档(PDF、图片、Office 文件等)转换为机器可读的 Markdown 和 JSON,专为 LLM 预训练、RAG 和 Agent 工作流场景设计。
当前整体效果仍在持续改进中。如果你遇到效果不理想、输出质量不稳定或明显的版式异常,欢迎提交 issue 或附带样例文件 / 截图反馈。
核心解析能力:
- 公式 → LaTeX · 表格 → HTML,精准还原复杂版面
- 支持扫描件、手写体、多栏布局、跨页表格合并
- 输出符合人类阅读顺序,自动去除页眉页脚
- VLM + OCR 双引擎,支持 109 种语言识别
🐍 Python SDK
安装
pip install mineru-open-sdk
uv add mineru-open-sdk --default-index https://pypi.tuna.tsinghua.edu.cn/simple
Agent 轻量解析(免 Token)
from mineru import MinerU
client = MinerU()
result = client.flash_extract("https://cdn-mineru.openxlab.org.cn/demo/example.pdf")
print(result.markdown)
精准解析(需 Token)
from mineru import MinerU
# 从 https://mineru.net 获取免费 Token
client = MinerU("your-api-token")
result = client.extract("https://cdn-mineru.openxlab.org.cn/demo/example.pdf")
print(result.markdown)
print(result.images) # 提取的图片列表
🦜 LangChain RAG 集成
langchain-mineru 是官方 LangChain 文档加载器,一行代码将任意文档转为 LangChain Document 对象,直接接入 RAG 流水线。
安装
pip install langchain-mineru
最简示例(无需 Token)
1. 基础用法(默认 flash 模式,无需 Token)
from langchain_mineru import MinerULoader
loader = MinerULoader(source="demo.pdf") # 默认 flash 模式,无需 Token
docs = loader.load()
print(docs[0].page_content[:500])
print(docs[0].metadata)
默认使用 mode="flash",适合快速预览和轻量解析。
2. Precision 模式(需 Token)
适合长文档、大文件,以及对解析保真度或标准 API 输出要求更高的场景。Flash 模式也支持 OCR、公式、表格开关,但仍受 flash API 自身限制。
from langchain_mineru import MinerULoader
loader = MinerULoader(
source="/path/to/manual.pdf",
mode="precision",
token="your-api-token", # 或设置 MINERU_TOKEN 环境变量
split_pages=True,
pages="1-5",
)
docs = loader.load()
for doc in docs:
print(doc.metadata.get("page"), doc.page_content[:200])
3. 接入 LangChain RAG 流水线
from langchain_mineru import MinerULoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
loader = MinerULoader(source="demo.pdf", split_pages=True)
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=1200, chunk_overlap=200)
chunks = splitter.split_documents(docs)
vs = FAISS.from_documents(chunks, OpenAIEmbeddings())
results = vs.similarity_search("这个文档的核心结论是什么?", k=3)
for r in results:
print(r.page_content[:200])
默认使用 mode="flash"(无需 API Token);切换到 mode="precision" 可获得更高精度的解析结果(需要 Token 认证)。如果用于 RAG,建议对 PDF 开启 split_pages=True,这样可以得到更细粒度的页级 Document。
🔗 全部集成
| 框架 / 工具 | 状态 | 说明 |
|---|---|---|
| LangChain | ✅ 官方 | pip install langchain-mineru |
| LlamaIndex | ✅ 社区 | 见 MinerU-Ecosystem |
| RAGFlow | ✅ 支持 | 文档加载器集成 |
| RAG-Anything | ✅ 支持 | 多模态 RAG 流水线 |
| Flowise | ✅ 支持 | 可视化 RAG 构建器 |
| Dify | ✅ 原生插件 | 内置文档加载器 |
| FastGPT | ✅ 原生插件 | 接入文档 |
| Claude Desktop | ✅ MCP | uvx mineru-open-mcp |
| Cursor | ✅ MCP | .cursor/mcp.json 配置 |
| Windsurf | ✅ MCP | stdio / streamable-http |
| OpenClaw / ZeroClaw | ✅ Agent 技能 | ClawHub |
| Go SDK | ✅ 官方 | go get .../sdk/go@latest |
| TypeScript SDK | ✅ 官方 | npm install mineru-open-sdk |
| Python SDK | ✅ 官方 | pip install mineru-open-sdk |
MinerU Open API SDK (Python)
MinerU Open API SDK 是一个完全免费的 Python 库,用于连接 MinerU 文档提取服务。只需一行代码,即可将任何文档(PDF、图片、Word、PPT、Excel)或网页转换为高质量的 Markdown。
🚀 核心特性
- 完全免费:文档提取服务没有任何隐藏费用。
- Agent 轻量解析 (No Auth):无需 API Token 即可立即提取。
- 精准解析:提供完整的版式保留、图片、表格及公式支持。
- 批量与轮询原语:既提供开箱即用的阻塞式接口,也提供适合异步工作流的 submit/query 接口。
- 内置保存辅助方法:可直接保存 Markdown、HTML、LaTeX、DOCX,或解压完整结果包。
📦 安装指南
pip install mineru-open-sdk
🛠️ 快速上手
1. Agent 轻量解析 (Flash Extract - 免登录)
适合快速预览。无需配置 Token。
from mineru import MinerU
# Agent 轻量解析无需传入 Token
client = MinerU()
result = client.flash_extract("https://cdn-mineru.openxlab.org.cn/demo/example.pdf")
print(result.markdown)
2. 精准解析 (Precision Extract - 需登录)
支持超大文件、丰富的资产(图片/表格)及多种输出格式。
from mineru import MinerU
# 从 https://mineru.net/apiManage/token 获取免费 Token
client = MinerU("your-api-token")
result = client.extract("https://cdn-mineru.openxlab.org.cn/demo/example.pdf")
print(result.markdown)
print(result.images) # 获取提取出的图片列表
🧩 支持的公开接口
Client 生命周期
MinerU(token: str | None = None, base_url: str = ..., flash_base_url: str | None = None)client.close()client.set_source("your-app")- 支持上下文管理器:
with MinerU(...) as client:
阻塞式解析接口
client.extract(...) -> ExtractResultclient.extract_batch(...) -> Iterator[ExtractResult]client.crawl(...) -> ExtractResultclient.crawl_batch(...) -> Iterator[ExtractResult]client.flash_extract(...) -> ExtractResult
提交 / 查询接口
client.submit(...) -> strclient.submit_batch(...) -> strclient.get_batch(batch_id) -> list[ExtractResult]client.get_task(task_id) -> ExtractResult
结果保存辅助方法
result.save_markdown(path, with_images=True)result.save_docx(path)result.save_html(path)result.save_latex(path)result.save_all(dir)image.save(path)
常用结果字段
result.stateresult.progressresult.markdownresult.imagesresult.content_listresult.docxresult.htmlresult.latexresult.task_id
📊 模式对比
| 特性 | Agent 轻量解析 (Flash) | 精准解析 (Precision) |
|---|---|---|
| 身份认证 | 免登录 (No Auth) | 需登录 (Token) |
| 处理速度 | 极速 | 标准 |
| 文件大小上限 | 最大 10 MB | 最大 200 MB |
| 文件页数上限 | 最大 20 页 | 最大 200 页 |
| 支持格式 | PDF, 图片, Docx, PPTx, Excel | PDF, 图片, Doc/x, Ppt/x, Html |
| 内容完整度 | Markdown(公式和表格默认开启,OCR 默认关闭) | 完整资源 (图片、表格、公式全部保留) |
| 输出格式 | Markdown | MD, Docx, LaTeX, HTML, JSON |
⚙️ 默认行为与参数说明
MinerU(...)
| 参数 | 默认值 | 省略时行为 |
|---|---|---|
token |
None |
若未传入,则读取环境变量 MINERU_TOKEN |
base_url |
https://mineru.net/api/v4 |
标准 API 的默认地址 |
flash_base_url |
SDK 内置默认 flash 地址 | 可用于测试或私有部署 |
如果既没有传入 token,环境变量 MINERU_TOKEN 也未设置,则 client 进入 flash-only mode:flash_extract() 可用,其他需要鉴权的方法会抛出 NoAuthClientError。
全功能接口
这些默认值适用于 extract()、extract_batch()、submit()、submit_batch(),crawl() / crawl_batch() 也会间接继承其中的大部分行为。
| 参数 | 默认值 | 省略时行为 |
|---|---|---|
model |
None |
自动推断:.html / .htm 走 "html",其余默认 "vlm" |
ocr |
不设置 | 默认关闭 OCR(API 默认行为) |
formula |
不设置 | 默认开启公式识别(API 默认行为) |
table |
不设置 | 默认开启表格识别(API 默认行为) |
language |
不设置 | 默认中文 "ch"(API 默认行为) |
pages |
None |
默认处理完整文档 |
extra_formats |
None |
仅返回默认的 Markdown / JSON 结果 |
file_params |
None |
批量方法中的 per-file 参数覆盖。dict[str, FileParam],key 为路径/URL,FileParam 包含 pages、ocr、data_id 字段 |
timeout |
单任务 300 秒 |
extract() / crawl() 的总轮询超时 |
timeout |
批量 1800 秒 |
extract_batch() / crawl_batch() 的总轮询超时 |
Flash Extract
| 参数 | 默认值 | 省略时行为 |
|---|---|---|
language |
"ch" |
默认中文 |
page_range |
None |
默认处理 flash API 允许的完整页范围 |
is_ocr |
None |
OCR 默认关闭(API 默认行为) |
enable_formula |
None |
公式识别默认开启(API 默认行为) |
enable_table |
None |
表格识别默认开启(API 默认行为) |
timeout |
300 秒 |
总轮询超时 |
crawl() / crawl_batch()
crawl()等价于extract(url, model="html", ...)crawl_batch()等价于extract_batch(urls, model="html", ...)
📖 详细用法
全功能提取选项
result = client.extract(
"./论文.pdf",
model="vlm", # "vlm" | "pipeline" | "html"
ocr=True, # 启用 OCR 识别扫描件
formula=True, # 公式识别
table=True, # 表格识别
language="en", # "ch" | "en" | 等
pages="1-20", # 页码范围
extra_formats=["docx"], # 额外导出为 docx, html, 或 latex
timeout=600,
)
result.save_all("./output/") # 保存 Markdown 和所有相关资源
上下文管理器
from mineru import MinerU
with MinerU("your-api-token") as client:
result = client.extract("./论文.pdf")
print(result.markdown)
批量处理
# 边处理边返回结果
for result in client.extract_batch(["a.pdf", "b.pdf", "c.pdf"]):
print(f"{result.filename}: 已完成")
批量处理 - 为每个文件指定不同页码
from mineru import FileParam
batch_id = client.submit_batch(
["a.pdf", "b.pdf"],
file_params={
"a.pdf": FileParam(pages="1-5"),
"b.pdf": FileParam(pages="10-20"),
},
)
网页爬取 (Crawl)
result = client.crawl("https://www.baidu.com")
print(result.markdown)
🔄 submit() / get_batch() 语义说明
这一组接口最容易被误用:
submit()返回的是 batch_idsubmit_batch()返回的也是 batch_id- 因此最常见的异步流程应该是
submit(...) -> get_batch(batch_id) - 对于异步轮询,建议始终沿用 batch 这一套语义
推荐的异步流程
batch_id = client.submit("大报告.pdf")
# 轮询 batch,直到第一个结果完成
while True:
results = client.get_batch(batch_id)
result = results[0]
if result.state in ("done", "failed"):
break
if result.state == "done":
do_something(result.markdown)
🤖 AI Agent 自动化集成
本 SDK 设计时充分考虑了 LLM 工作流集成。您可以通过 result.state 和 result.progress 轻松监控任务状态。
batch_id = client.submit("大报告.pdf")
# ... 稍后 ...
result = client.get_batch(batch_id)[0]
if result.state == "done":
do_something(result.markdown)
发票快速生成汇总表
PDF 发票 → MinerU SDK 批量提取 → LLM 结构化提取 → Excel
pip install mineru-open-sdk openai openpyxl python-dotenv
.env
MINERU_TOKEN=你的MinerU Token
LLM_API_KEY=你的Moonshot API Key
LLM_BASE_URL=https://api.moonshot.cn/v1
LLM_MODEL=moonshot-v1-128k
完整代码
整个脚本只有三个函数。关键设计:extract_batch 是 yield 式返回——解完一张就立刻喂给 LLM,不用等全部解完。
"""
MinerU 发票提取器
PDF 发票 → MinerU SDK 批量提取 JSON → LLM 结构化提取 → Excel
"""
import os
import sys
import json
from pathlib import Path
from dotenv import load_dotenv
from mineru import MinerU, FileParam
from openai import OpenAI
from openpyxl import Workbook
# -------- 加载环境变量 --------
load_dotenv()
MINERU_TOKEN = os.getenv("MINERU_TOKEN")
LLM_API_KEY = os.getenv("LLM_API_KEY")
LLM_BASE_URL = os.getenv("LLM_BASE_URL", "https://api.moonshot.cn/v1")
LLM_MODEL = os.getenv("LLM_MODEL", "moonshot-v1-128k")
EXTRACT_PROMPT = (
'你是一个发票信息提取器。以下是MinerU SDK从发票图片中提取的content_list。'
'每个文本块格式:{"type": "text", "text": "发票号码:144000000000", ...} '
'或 {"type": "table", "table_body": "<table>...<td>价税合计(小写)</td>'
'<td>¥ 90000.00</td>...</table>"}。'
'请找出:1)发票号码(以"发票号码:"开头的text冒号后数字) '
'2)开票日期("开票日期:"开头,转YYYY-MM-DD) '
'3)价税合计(表格中"价税合计(小写)"对应¥后面数字)。'
'只返回JSON,不要任何解释或思考过程。'
'格式:{"invoice_number":"","date":"","total":""}'
)
# -------- 函数 1:MinerU 批量解析,解完一个 yield 一个 --------
def parse_invoices(pdf_dir: str):
"""将目录下所有 PDF/图片通过 MinerU SDK 批量提取,解完一个 yield 一个"""
files = []
for ext in ["*.pdf", "*.jpg", "*.jpeg", "*.png", "*.bmp"]:
files.extend(sorted(Path(pdf_dir).glob(ext)))
if not files:
print("No files found (supported: pdf, jpg, png, bmp)")
return
client = MinerU(MINERU_TOKEN)
# extract_batch 支持的全局参数:
# model - 模型版本: "vlm"(推荐) / "pipeline" / "html",默认自动推断
# ocr - 是否开启 OCR,默认 False
# formula - 是否开启公式识别,默认 True
# table - 是否开启表格识别,默认 True
# language - 文档语言,默认 "ch"
# extra_formats - 额外导出格式: ["docx", "html", "latex"]
# timeout - 轮询超时秒数,默认 1800
# file_params - 按文件覆盖参数,key 为文件路径,value 为 FileParam
# FileParam 支持的字段:
# pages - 页码范围,如 "1-5"
# ocr - 覆盖全局 OCR 开关
# data_id - 自定义业务标识
# 发票是扫描件/图片,需要开 OCR;通过 FileParam 按文件设置参数
file_params = {str(f): FileParam(ocr=True) for f in files}
for result in client.extract_batch(
[str(f) for f in files],
table=True, # 发票中的金额在表格里,确保开启
file_params=file_params,
):
print(f" Parsed: {result.filename}")
yield {"filename": result.filename, "content": result.content_list}
client.close()
# -------- 函数 2:LLM 提取结构化字段 --------
def extract_fields(invoice: dict, llm: OpenAI) -> dict:
"""将单张发票的 content_list 喂给 LLM,提取发票号码/日期/金额"""
content_str = json.dumps(invoice["content"], ensure_ascii=False)
resp = llm.chat.completions.create(
model=LLM_MODEL,
messages=[{"role": "user", "content": EXTRACT_PROMPT + content_str}],
temperature=0,
)
raw = resp.choices[0].message.content.strip()
try:
fields = json.loads(raw)
except json.JSONDecodeError:
fields = {"invoice_number": "PARSE_ERROR", "date": "", "total": ""}
fields["filename"] = invoice["filename"]
print(f" Extracted: {invoice['filename']}")
return fields
# -------- 函数 3:写入 Excel --------
def write_excel(data: list[dict], output: str = "发票汇总.xlsx"):
"""将提取结果写入 Excel"""
wb = Workbook()
ws = wb.active
ws.append(["文件名", "发票号码", "开票日期", "价税合计"])
for row in data:
ws.append([
row.get("filename"),
row.get("invoice_number"),
row.get("date"),
row.get("total")
])
wb.save(output)
print(f"已保存: {output}")
# -------- 主流程:流水线式处理 --------
def main():
pdf_dir = sys.argv[1] if len(sys.argv) > 1 else "./invoices"
llm = OpenAI(api_key=LLM_API_KEY, base_url=LLM_BASE_URL)
results = []
for invoice in parse_invoices(pdf_dir): # 解完一张
results.append(extract_fields(invoice, llm)) # 立刻提取
write_excel(results)
if __name__ == "__main__":
main()
python invoice_extractor.py ./invoices

本文来自博客园,作者:VipSoft 转载请注明原文链接:https://www.cnblogs.com/vipsoft/p/20766758
浙公网安备 33010602011771号