MinerU支持REST API吗?Flask封装实战指南
MinerU 2.5-1.2B 是一款专为复杂PDF文档设计的深度学习提取工具,能精准识别多栏排版、嵌入表格、数学公式和矢量图片,并输出结构清晰、语义完整的Markdown。但原生MinerU命令行工具只提供本地终端调用方式,不直接暴露HTTP接口——这意味着你无法把它轻松集成进Web系统、自动化流水线或低代码平台。那么问题来了:MinerU到底支不支持REST API?答案是:它本身不支持,但我们可以亲手给它加上。
本文不讲空泛概念,不堆砌理论,而是带你从零开始,用最轻量、最稳定的方式,把MinerU封装成一个真正可用的REST服务。全程基于镜像预装环境(Python 3.10 + magic-pdf[full] + MinerU2.5-2509-1.2B),无需重装依赖、不改模型路径、不碰CUDA配置——三步启动,五步上线,连curl测试都给你写好。如果你正卡在“模型跑得通,但业务调不动”的阶段,这篇文章就是为你写的。
1. 先说结论:MinerU原生不带API,但封装成本极低
MinerU官方提供的mineru命令本质是一个CLI入口,底层调用的是magic_pdf.api.parse_pdf等Python函数。它没有内置Web服务器,也没有Flask/FastAPI胶水层——这恰恰是好事。因为这意味着:
- 无侵入改造:你不需要动一行MinerU源码,也不用fork仓库、提PR
- 零依赖冲突:镜像已预装全部环境(包括
libgl1、libglib2.0-0等图像库),Flask可直接pip安装 - GPU开箱即用:CUDA驱动、cuDNN、PyTorch CUDA版本均已对齐,无需额外适配
- 路径完全可控:模型权重固定在
/root/MinerU2.5/,配置文件在/root/magic-pdf.json,不用猜路径
换句话说:MinerU不是不能做API,而是它把“做API”的自由,留给了真正需要它的人。接下来,我们就用最朴实的方式,把它变成你的私有PDF解析服务。
2. Flask封装四步走:从命令行到HTTP接口
我们不追求炫技,只做最小可行封装(MVP)。整个服务只包含一个Python文件、一个配置、一个启动命令,所有逻辑直击核心——把mineru -p xxx.pdf变成POST /parse。
2.1 创建API服务文件
进入工作目录,新建app.py:
cd /root/MinerU2.5 touch app.py用你喜欢的编辑器(如nano app.py)填入以下内容:
# app.py from flask import Flask, request, jsonify, send_file import os import tempfile import subprocess import shutil from pathlib import Path app = Flask(__name__) # 预设输出目录(每次请求独立生成,避免并发冲突) OUTPUT_BASE = "/tmp/mineru_output" @app.route('/parse', methods=['POST']) def parse_pdf(): # 检查是否上传了PDF文件 if 'file' not in request.files: return jsonify({"error": "缺少文件字段 'file'" }), 400 pdf_file = request.files['file'] if pdf_file.filename == '': return jsonify({"error": "未选择文件"}), 400 if not pdf_file.filename.lower().endswith('.pdf'): return jsonify({"error": "仅支持PDF格式"}), 400 # 创建临时目录存放输入和输出 temp_dir = tempfile.mkdtemp(dir="/tmp") input_path = os.path.join(temp_dir, "input.pdf") output_dir = os.path.join(temp_dir, "output") try: # 保存上传的PDF pdf_file.save(input_path) # 执行mineru命令(复用镜像预装的CLI) cmd = [ "mineru", "-p", input_path, "-o", output_dir, "--task", "doc" ] result = subprocess.run( cmd, capture_output=True, text=True, cwd="/root/MinerU2.5" # 确保在正确路径下执行 ) if result.returncode != 0: return jsonify({ "error": "MinerU执行失败", "stderr": result.stderr[:500] # 截断过长日志 }), 500 # 查找生成的Markdown文件(优先取main.md, fallback to first .md) md_files = list(Path(output_dir).rglob("*.md")) if not md_files: return jsonify({"error": "未生成Markdown输出文件"}), 500 md_path = str(md_files[0]) # 读取Markdown内容并返回 with open(md_path, "r", encoding="utf-8") as f: content = f.read() return jsonify({ "success": True, "markdown": content, "output_dir": output_dir }) except Exception as e: return jsonify({"error": f"服务内部错误: {str(e)}"}), 500 finally: # 清理临时文件(保留output_dir供调试,生产环境建议删除) if os.path.exists(temp_dir): shutil.rmtree(temp_dir) @app.route('/health', methods=['GET']) def health_check(): return jsonify({"status": "healthy", "model": "MinerU2.5-2509-1.2B"}) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False)关键点说明:
subprocess.run直接调用镜像自带的mineru命令,完全复用原有逻辑,不重复实现解析器cwd="/root/MinerU2.5"确保命令在正确路径执行,自动加载magic-pdf.json配置- 使用
tempfile.mkdtemp隔离每次请求,避免文件名冲突和并发污染/health端点用于K8s探针或监控系统健康检查,返回模型标识便于运维定位
2.2 安装Flask(仅需一行)
镜像已预装Python 3.10和Conda环境,直接pip安装即可:
pip install flask验证:运行
python -c "import flask; print(flask.__version__)",输出2.3.3或更高即成功
2.3 启动服务(后台常驻)
使用nohup让服务在后台持续运行,日志输出到mineru_api.log:
nohup python app.py > mineru_api.log 2>&1 & echo $! > mineru_api.pid验证:执行
curl http://localhost:5000/health,返回{"status":"healthy","model":"MinerU2.5-2509-1.2B"}即服务就绪
2.4 测试API(三行命令搞定)
准备一个PDF文件(比如镜像自带的test.pdf),用curl发送:
# 方法1:直接传本地文件(推荐测试用) curl -X POST http://localhost:5000/parse \ -F "file=@/root/MinerU2.5/test.pdf" | python -m json.tool # 方法2:用Python requests(适合集成进脚本) python3 -c " import requests with open('/root/MinerU2.5/test.pdf', 'rb') as f: r = requests.post('http://localhost:5000/parse', files={'file': f}) print(r.json()['markdown'][:200] + '...') "成功响应示例(截取前段):
{ "success": true, "markdown": "# 标题\n\n## 1. 引言\n\n本文介绍MinerU2.5在复杂PDF解析中的能力...\n\n### 表格示例\n\n| 列A | 列B |\n|-----|-----|\n| 数据1 | 数据2 |\n\n\n\n", "output_dir": "/tmp/tmpabc123/output" }3. 进阶优化:让API更稳、更快、更实用
上面的MVP版已能工作,但在真实业务中,你还可能遇到这些需求。我们提供轻量级解决方案,全部基于现有镜像环境,无需额外安装。
3.1 支持大文件上传(突破默认16MB限制)
Flask默认限制表单大小。修改app.py开头,添加配置:
# 在 from flask import ... 下方添加 from werkzeug.utils import secure_filename # 在 app = Flask(__name__) 后添加 app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100MB验证:上传100MB PDF测试,确认不报
413 Request Entity Too Large
3.2 返回完整结果包(Markdown + 图片 + 公式)
原生API只返回Markdown文本,但实际业务常需整套资源。扩展/parse响应,增加zip_url字段:
# 在 jsonify前添加打包逻辑 import zipfile zip_path = os.path.join(temp_dir, "result.zip") with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf: for file_path in Path(output_dir).rglob("*"): if file_path.is_file(): arcname = file_path.relative_to(output_dir) zf.write(file_path, arcname) # 修改返回值 return jsonify({ "success": True, "markdown": content, "zip_url": f"/download/{os.path.basename(zip_path)}" })再添加下载路由(完整代码见文末附录),即可通过/download/xxx.zip获取全部产物。
3.3 GPU显存智能降级(OOM自动切CPU)
当处理超大PDF触发CUDA OOM时,服务不应崩溃,而应优雅降级。在subprocess.run后添加容错:
if result.returncode != 0 and "CUDA out of memory" in result.stderr: # 自动切换到CPU模式重试 cmd_cpu = cmd + ["--device-mode", "cpu"] result = subprocess.run(cmd_cpu, capture_output=True, text=True, cwd="/root/MinerU2.5") if result.returncode != 0: return jsonify({"error": "CPU模式仍失败", "stderr": result.stderr[:300]}), 500效果:首次GPU失败后自动用CPU重试,响应时间变长但保证成功
4. 生产就绪建议:安全、监控与部署
这个Flask服务已能在开发环境稳定运行,若要投入生产,只需三个小动作:
4.1 用Gunicorn替代Flask内置服务器
Flask开发服务器不适用于生产。安装Gunicorn并启动:
pip install gunicorn gunicorn -w 2 -b 0.0.0.0:5000 --timeout 300 app:app-w 2:启动2个工作进程,提升并发能力--timeout 300:设置5分钟超时,避免大PDF卡死- 日志自动输出到stdout,方便容器日志收集
4.2 添加基础认证(防止未授权调用)
在app.py中加入简单Token校验(无需数据库):
# 在顶部定义密钥 API_TOKEN = os.getenv("MINERU_API_TOKEN", "your-secret-token-here") # 在 /parse 路由开头添加 auth_header = request.headers.get('Authorization') if auth_header != f"Bearer {API_TOKEN}": return jsonify({"error": "Unauthorized"}), 401调用时加Header:curl -H "Authorization: Bearer your-secret-token-here" ...
4.3 监控关键指标(无需Prometheus)
在/health端点中加入实时状态:
import psutil @app.route('/health', methods=['GET']) def health_check(): cpu = psutil.cpu_percent(interval=1) mem = psutil.virtual_memory().percent return jsonify({ "status": "healthy", "model": "MinerU2.5-2509-1.2B", "cpu_usage": f"{cpu}%", "memory_usage": f"{mem}%" })运维人员可通过
curl /health一眼掌握服务负载,无需额外监控组件
5. 总结:你已拥有一个企业级PDF解析API
回看整个过程,我们没做任何高深操作:
- 没修改MinerU一行源码,完全尊重原生逻辑;
- 没重装CUDA或PyTorch,100%复用镜像预置环境;
- 没引入复杂框架,Flask + subprocess 就是最佳胶水;
- 每一步都可验证、可回滚、可监控。
你现在拥有的,不是一个玩具Demo,而是一个:
🔹真能处理业务PDF(多栏/公式/表格全支持)
🔹真能抗住并发请求(Gunicorn多进程+超时保护)
🔹真能融入现有架构(标准REST + Bearer Token + Health Check)
🔹真能快速横向扩展(Docker化后一键部署多实例)
MinerU的价值,从来不在它“能不能”,而在于你“敢不敢用最简单的方式,把它变成自己系统的一部分”。今天你封装的不仅是一个API,更是把前沿AI能力,稳稳握在自己手中的第一步。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。