MinerU + 本地大模型提取 JSON
Laiyong Wang Lv6

本地mac电脑(m4芯片+16G)
调研了PaddleOCR 、 minerU,最终选择 minerU 解析PDF,生成文本文件及图片
大模型国内基本都可用,但是基于开源、本地部署简单、中文能力强、未来发展有保障等原因,选择了千问

问题:
1、pdf文件解析,文件内有关图片会提取错误,图片尽量不使用
2、解析生成的md、json等各种文件,通过千问大模型获取指定的结构化内容时,每次请求返回的内容都可能不一致,但是大体相同,使用时需要规则校验,如下图

upload successful
python代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/env python3
"""
PDF → MinerU 转 Markdown → 本地大模型按规则提取 JSON
"""
import argparse
import json
import re
import subprocess
import sys
from pathlib import Path


def query_llm_for_json(rule: str, text: str, model: str = "qwen2.5:14b") -> dict:
"""
用本地 Ollama 大模型根据规则从文本中提取信息,返回解析后的 JSON。

:param rule: 提示语/规则,例如「从以下文本中提取所有密评人员的姓名、是否有证书...」
:param text: 待分析的文本(如 Markdown 内容)
:param model: Ollama 模型名,默认 qwen2.5:14b
:return: 解析得到的 dict,若解析失败则抛出或返回空结构
"""
cmd = ["ollama", "run", model, rule]
try:
proc = subprocess.run(
cmd,
input=text.encode("utf-8"),
capture_output=True,
timeout=300,
cwd=None,
)
out = proc.stdout.decode("utf-8", errors="replace").strip()
err = proc.stderr.decode("utf-8", errors="replace").strip()
if err:
sys.stderr.write(f"ollama stderr: {err}\n")
except subprocess.TimeoutExpired:
raise TimeoutError(f"Ollama 调用超时 (model={model})")
except FileNotFoundError:
raise RuntimeError("未找到 ollama 命令,请先安装并确保在 PATH 中")

# 尽量从输出中剥离出纯 JSON
json_str = _extract_json_string(out)
if not json_str:
raise ValueError(f"无法从模型输出中解析 JSON。原始输出:\n{out[:500]}")

return json.loads(json_str)


def _extract_json_string(raw: str) -> str | None:
"""从模型输出中提取第一个完整的 JSON 字符串(支持 ```json ... ``` 包裹)。"""
raw = raw.strip()
# 尝试 ```json ... ``` 代码块
m = re.search(r"```(?:json)?\s*([\s\S]*?)\s*```", raw)
if m:
return m.group(1).strip()
# 尝试找 { ... } 包裹的 JSON
start = raw.find("{")
if start == -1:
return None
depth = 0
for i in range(start, len(raw)):
if raw[i] == "{":
depth += 1
elif raw[i] == "}":
depth -= 1
if depth == 0:
return raw[start : i + 1]
return None


def pdf_to_markdown_path(pdf_path: Path, output_dir: Path) -> Path:
"""
根据 MinerU 的产出规则,得到 PDF 对应的 Markdown 文件路径。
约定:output_dir / {pdf_stem} / hybrid_auto / {pdf_stem}.md
"""
stem = pdf_path.stem
return output_dir / stem / "hybrid_auto" / f"{stem}.md"


def run_mineru(pdf_path: Path, output_dir: Path) -> Path:
"""
调用 mineru 将 PDF 转为 Markdown,返回生成的 .md 文件路径。
"""
pdf_path = pdf_path.resolve()
output_dir = output_dir.resolve()
if not pdf_path.is_file():
raise FileNotFoundError(f"PDF 不存在: {pdf_path}")

cmd = ["mineru", "-p", str(pdf_path), "-o", str(output_dir)]
subprocess.run(cmd, check=True, timeout=600)
md_path = pdf_to_markdown_path(pdf_path, output_dir)
if not md_path.is_file():
raise FileNotFoundError(f"MinerU 未生成预期 Markdown: {md_path}")
return md_path


def extract_from_pdf(
pdf_path: str | Path,
rule: str,
output_dir: str | Path = ".",
model: str = "qwen2.5:14b",
) -> dict:
"""
一次调用:传入 PDF 路径和提取规则,先 MinerU 转 Markdown,再用大模型按规则提取 JSON。

:param pdf_path: PDF 文件路径
:param rule: 给大模型的提示语/规则,例如「从以下文本中提取所有密评人员的姓名、是否有证书、证书成绩、考试通过时间,提取后直接返回json数据给我,直接给我json字符串,不要有任何别的信息」
:param output_dir: MinerU 输出目录,默认当前目录
:param model: Ollama 模型名
:return: 大模型返回并解析后的 JSON(dict)
"""
pdf_path = Path(pdf_path)
output_dir = Path(output_dir)
md_path = run_mineru(pdf_path, output_dir)
text = md_path.read_text(encoding="utf-8", errors="replace")
return query_llm_for_json(rule, text, model=model)


# 预设规则示例,之后可在此扩展不同话术
DEFAULT_RULE = (
"从以下文本中提取所有密评人员的姓名、是否有证书、证书成绩、考试通过时间,"
"提取后直接返回json数据给我,直接给我json字符串,不要有任何别的信息"
)


def main():
parser = argparse.ArgumentParser(description="PDF → MinerU → 大模型提取 JSON")
parser.add_argument("pdf", type=Path, nargs="?", help="PDF 文件路径(使用 --only-query 时可不传)")
parser.add_argument("-o", "--output-dir", type=Path, default=Path("."), help="MinerU 输出目录,默认当前目录")
parser.add_argument(
"-r",
"--rule",
type=str,
default=DEFAULT_RULE,
help="大模型提取规则/话术,默认提取密评人员姓名、证书、成绩、通过时间",
)
parser.add_argument("-m", "--model", type=str, default="qwen2.5:14b", help="Ollama 模型名")
parser.add_argument("--only-query", action="store_true", help="仅对已有 Markdown 做一次大模型查询(需同时传 --md)")
parser.add_argument("--md", type=Path, help="已有 Markdown 文件路径(与 --only-query 一起使用)")
args = parser.parse_args()

if args.only_query:
if not args.md or not args.md.is_file():
parser.error("使用 --only-query 时必须指定已存在的 --md 文件")
text = args.md.read_text(encoding="utf-8", errors="replace")
result = query_llm_for_json(args.rule, text, model=args.model)
else:
if not args.pdf:
parser.error("未使用 --only-query 时必须指定 PDF 文件路径")
result = extract_from_pdf(
args.pdf,
args.rule,
output_dir=args.output_dir,
model=args.model,
)

print(json.dumps(result, ensure_ascii=False, indent=2))
return 0


if __name__ == "__main__":
sys.exit(main() or 0)

MinerU + 本地大模型提取 JSON

将 PDF 用 MinerU 转为 Markdown,再用本地 Ollama 大模型按规则从文本中提取 JSON。

环境要求

  • Python:3.10+
  • MinerU:用于 PDF → Markdown
  • Ollama:用于本地大模型推理

安装

1. 安装 Python 依赖

1
pip install -r requirements.txt

2. 安装并配置 Ollama

  • 安装:https://ollama.combrew install ollama
  • 拉取模型(例如 qwen2.5:14b):
1
ollama pull qwen2.5:14b

确保命令行可直接执行 ollama

运行

命令行:PDF → MinerU → 大模型 → 打印 JSON

1
2
3
4
5
6
7
8
# 使用默认规则(密评人员姓名、证书、成绩、通过时间)
python main.py 测试-扫描件.pdf -o ./

# 自定义规则
python main.py 测试-扫描件.pdf -o ./ -r "你的提取规则话术"

# 指定模型
python main.py 测试-扫描件.pdf -o ./ -m qwen2.5:14b

仅对已有 Markdown 做一次大模型查询

1
python main.py --only-query --md ./测试-扫描件/hybrid_auto/测试-扫描件.md -r "你的规则"

在代码中调用

1
2
3
4
5
6
7
8
9
from main import query_llm_for_json, extract_from_pdf

# 独立方法:规则 + 文本 → JSON(可定制不同话术)
rule = "从以下文本中提取..."
text = open("xxx.md").read()
data = query_llm_for_json(rule, text)

# 一次调用:PDF → MinerU → 大模型 → JSON
data = extract_from_pdf("xxx.pdf", rule, output_dir="./")

输出说明

  • MinerU 会在 -o 指定目录下生成 {PDF 文件名}/hybrid_auto/{PDF 文件名}.md
  • 脚本最终将大模型返回的 JSON 解析后打印到标准输出(可直接重定向或管道)。