2026年7月2日 周四晚上19:30,报名腾讯会议了解“如何构建自进化的动态知识库(Brain)”(限30人)
免费POC, 零成本试错
FDE知识库

FDE知识库

学习大模型的前沿技术与行业落地应用


收藏

深入拆解 MinerU 解析处理流程

发布日期:2025-05-19 07:03:52 浏览次数: 3908
作者:我有一计

微信搜一搜,关注“我有一计”

推荐语

探索MinerU最新版本v1.3.10文档解析的内部机制

核心内容:
1. MinerU更新频率及版本概述
2. 环境准备与magic-pdf安装升级
3. MinerU文档解析步骤详细拆解

杨芳贤
53AI创始人/腾讯云(TVP)最具价值专家

概述

MinerU更新频率也相当频繁,在短短一个月内,更新了10个小版本。

本文结合最新版本v1.3.10,深入拆解下它进行文档解析时的内部操作细节。

环境准备

在之前的文章中,已经安装了magic-pdf(MinerU的解析包名),先通过以下的命令进行升级。

pip install magic-pdf --upgrade

如果是第一次安装,不需要加--upgrade参数:

pip install magic-pdf

解析步骤拆解

图源:MinerU: An Open-Source Solution for Precise Document Content Extraction

下面根据官方提供的运行示例,一步步拆解具体的流程。

import os

# 导入必要的模块和类
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

# 参数设置
pdf_file_name = "file.pdf"# 要处理的PDF文件路径,使用时替换为实际路径
name_without_suff = pdf_file_name.split(".")[0]  # 去除文件扩展名

# 准备环境
local_image_dir, local_md_dir = "output/images""output"# 图片和输出目录
image_dir = str(os.path.basename(local_image_dir))  # 获取图片目录名

# 创建输出目录(如果不存在)
os.makedirs(local_image_dir, exist_ok=True)

# 初始化数据写入器
image_writer, md_writer = FileBasedDataWriter(local_image_dir), FileBasedDataWriter(
    local_md_dir
)

# 读取PDF文件内容
reader1 = FileBasedDataReader("")  # 初始化数据读取器
pdf_bytes = reader1.read(pdf_file_name)  # 读取PDF文件内容为字节流

# 处理流程
## 创建PDF数据集实例
ds = PymuDocDataset(pdf_bytes)  # 使用PDF字节流初始化数据集

## 推理阶段
if ds.classify() == SupportedPdfParseMethod.OCR:
    # 如果是OCR类型的PDF(扫描件/图片型PDF)
    infer_result = ds.apply(doc_analyze, ocr=True)  # 应用OCR模式的分析

    ## 处理管道
    pipe_result = infer_result.pipe_ocr_mode(image_writer)  # OCR模式的处理管道

else:
    # 如果是文本型PDF
    infer_result = ds.apply(doc_analyze, ocr=False)  # 应用普通文本模式的分析

    ## 处理管道
    pipe_result = infer_result.pipe_txt_mode(image_writer)  # 文本模式的处理管道

### 绘制模型分析结果到每页PDF
infer_result.draw_model(os.path.join(local_md_dir, f"{name_without_suff}_model.pdf"))

### 获取模型推理结果
model_inference_result = infer_result.get_infer_res()

### 绘制布局分析结果到每页PDF
pipe_result.draw_layout(os.path.join(local_md_dir, f"{name_without_suff}_layout.pdf"))

### 绘制文本块(span)分析结果到每页PDF
pipe_result.draw_span(os.path.join(local_md_dir, f"{name_without_suff}_spans.pdf"))

### 获取Markdown格式的内容
md_content = pipe_result.get_markdown(image_dir)  # 包含图片相对路径

### 保存Markdown文件
pipe_result.dump_md(md_writer, f"{name_without_suff}.md", image_dir)

### 获取内容列表(JSON格式)
content_list_content = pipe_result.get_content_list(image_dir)

### 保存内容列表到JSON文件
pipe_result.dump_content_list(md_writer, f"{name_without_suff}_content_list.json", image_dir)

### 获取中间JSON格式数据
middle_json_content = pipe_result.get_middle_json()

### 保存中间JSON数据
pipe_result.dump_middle_json(md_writer, f'{name_without_suff}_middle.json')

1. 数据读取

调用:magic_pdf\data\data_reader_writer\filebase.pyFileBasedDataReaderread_at方法

def read_at(self, path: str, offset: int = 0, limit: int = -1) -> bytes:
    """Read at offset and limit.

    Args:
        path (str): the path of file, if the path is relative path, it will be joined with parent_dir.
        offset (int, optional): the number of bytes skipped. Defaults to 0.
        limit (int, optional): the length of bytes want to read. Defaults to -1.

    Returns:
        bytes: the content of file
    """

    fn_path = path
    if not os.path.isabs(fn_path) and len(self._parent_dir) > 0:
        fn_path = os.path.join(self._parent_dir, path)

    with open(fn_path, 'rb'as f:
        f.seek(offset)
        if limit == -1:
            return f.read()
        else:
            return f.read(limit)

可设定offsetlimit两个参数:

  • offset:控制读取时的偏移量,比如从第n个字符后开始读取
  • limit:控制读取的长度,比如读取n个字符

默认读取文件所有内容,以二进制形式读取,直接加载进内存。

如果文件特别大,可以考虑用这两个参数做分批读取。

2. 文件类型分类

对于PDF文件,进一步判断是图片型PDF(扫描件)还是文本型PDF。

具体判断逻辑在magic_pdf\filter\pdf_classify_by_type.pyclassify函数:

def classify(total_page: int, page_width, page_height, img_sz_list: list, text_len_list: list, img_num_list: list,
             # text_layout_list: list,
             invalid_chars: bool)
:

    results = {
        'by_image_area': classify_by_area(total_page, page_width, page_height, img_sz_list, text_len_list),
        'by_text_len': classify_by_text_len(text_len_list, total_page),
        'by_avg_words': classify_by_avg_words(text_len_list),
        'by_img_num': classify_by_img_num(img_sz_list, img_num_list),
        # 'by_text_layout': classify_by_text_layout(text_layout_list),
        'by_img_narrow_strips': classify_by_img_narrow_strips(page_width, page_height, img_sz_list),
        'by_invalid_chars': invalid_chars,
    }

这里进行了多方面的判断,具体方式如下:

  • classify_by_area
    如果 PDF 里大部分页面(超过一半)的主要内容都是一张大图片(图片面积占了页面面积的一半以上),那就认为这个 PDF 是扫描件,需要 OCR 处理,否则认为这个 PDF 是文本型的(返回 True)。(在判断前,它会先尝试去掉水印图片,并把可能被切成小块的图片拼起来。)
  • classify_by_text_len
    随机抽取一部分页面,只要其中任何一页的文字数量超过 100 个字符,就认为这个 PDF 是文本型的(返回 True)。
  • classify_by_avg_words
    计算 PDF 所有页面的平均字符数,如果平均每页字符数超过 100,就认为这个 PDF 是文本型的(返回 True)。
  • classify_by_img_num
    检查 PDF 是否属于一种特殊扫描件,其特点是:去除重复图片后,几乎每页都没有有效图片了,但原始图片数量在大部分页面上都非常多且数量一致。如果不是这种情况,就认为它是文本型 PDF(返回 True)。
  • classify_by_img_narrow_strips
    检查 PDF 中是否有一半以上的页面,其绝大部分图片(至少 5 张且占该页图片总数的 80% 以上)都是非常细长的(即宽度占页面宽度的 90% 以上且至少是高度的 4 倍,或者高度占页面高度的 90% 以上且至少是宽度的 4 倍)。如果这种情况的页面比例低于 50%,则认为 PDF 在这个维度上倾向于是文本型的(返回 True)
  • invalid_chars
    在扫描 PDF 文件内容时,是否检测到了无效或无法正常显示的字符,如果没有,判定为文本型(返回 True)。

最后,综合这6个条件,如果这6个条件都为True,即判定文件是文本型的PDF,有任意条件不通过,则判定文件为图片型PDF。

3. 设备选择

在正式处理前,需要先指定运行设备。

首先需要读取配置文件,相关代码在MinerU\magic_pdf\libs\config_reader.py

# 定义配置文件名常量
CONFIG_FILE_NAME = os.getenv('MINERU_TOOLS_CONFIG_JSON''magic-pdf.json')

def read_config():
    if os.path.isabs(CONFIG_FILE_NAME):
        config_file = CONFIG_FILE_NAME
    else:
        home_dir = os.path.expanduser('~')
        config_file = os.path.join(home_dir, CONFIG_FILE_NAME)

    ifnot os.path.exists(config_file):
        raise FileNotFoundError(f'{config_file} not found')

    with open(config_file, 'r', encoding='utf-8'as f:
        config = json.load(f)
    return config

该代码表明,它会根据系统环境变量MINERU_TOOLS_CONFIG_JSON去查找json的配置文件。

如果未设置此环境变量,默认会到用户家目录下,寻找magic-pdf.json这个文件。

该文件中,device-mode设定了模型运行的设备,有cpucudanpu三种模式进行选择(如果填入值非这三类,视作cpu)。

如果选择了cudanpu,会自动计算显存容量,并自适应调节batch_ratio,具体调节规则如下:

if gpu_memory >= 16:
    batch_ratio = 16
elif gpu_memory >= 12:
    batch_ratio = 8
elif gpu_memory >= 8:
    batch_ratio = 4
elif gpu_memory >= 6:
    batch_ratio = 2
else:
    batch_ratio = 1

对于CPU设备,batch_ratio默认设为1。

顺带一提,在配置文件中,无法显性指定多卡同时解析,如有多卡解析的需求,可参考仓库中基于 LitServe 的多 GPU 并行处理方案。

文件地址: https://github.com/opendatalab/MinerU/tree/master/projects/multi_gpu

4. 解析处理

具体的处理流程在magic_pdf\model\batch_analyze.py

图片型PDF会比文本型PDF多一个OCR处理的步骤,其它步骤一致。

1.布局分析

布局分析提供了两套模型可供选择:layoutlmv3doclayout_yolo,默认采用后者。

doclayout_yolo也是MinerU所在组织OpenDataLab的研究成果。

仓库地址:https://github.com/opendatalab/DocLayout-YOLO

该算法是基于YOLO-v10对文件布局标记的数据集D4LADocSynth300K进行训练,从而检测出不同的布局信息。

数据集包含的文档布局示例如下:

DocSynth300K数据集类别可视化

2.公式处理

公式处理包含两个阶段:公式检测(MFD) 和 公式识别(MFR) 。

公式检测是指检测出公式所在位置,采用yolo_v8_mfd算法,未找到该算法的详细介绍,根据揣测是采用yolov8算法,在公式块标记的相关数据集上进行训练得到。

公式识别是指在得到公式区域的基础上,识别其中的具体内容,将其中的数学表达式图像转换为 LaTeX 语言,具体算法采用unimernet算法。

该算法也是OpenDataLab的自研算法。

仓库地址:https://github.com/opendatalab/UniMERNet

考虑到并不是所有的PDF文件中都有公式,因此,可以在配置文件中,将其关闭。默认的"enable"设置为开启状态,如需关闭公式处理,设为false

"formula-config": {
    "mfd_model""yolo_v8_mfd",
    "mfr_model""unimernet_small",
    "enable"true
},

3.OCR处理

OCR是指将图像区域内容识别成文本,和公式处理类似,同样包含区域检测和区域识别两个阶段。

这两个阶段均是使用百度的PaddleOCR,该算法原生使用的PaddlePaddle框架,这里使用了pytorch的实现版本。

在这个文件magic_pdf\model\sub_modules\ocr\paddleocr2pytorch\pytorch_paddle.py中,有一个测试样例。

对于不同的语言进行OCR,需要使用不同的模型,在magic_pdf\model\sub_modules\ocr\paddleocr2pytorch\pytorchocr\utils\resources\models_config.yml这个配置文件,规定了不同语言所对应的采取的不同模型。

对于中文文本,检测模型使用ch_PP-OCRv3_det_infer,识别模型采用ch_PP-OCRv4_rec_infer

在下载模型时,会将所有常见语言(中文、英文、日文、拉丁文等语言)的模型一次性下载下来,每个模型体积不大,基本在10-20MB左右。

4.表格识别

表格识别包含两个部分:表格文本识别和表格结构识别。

表格文本识别默认采用rapid_table这个依赖库,底层同样使用的是PaddleOCR,用来识别表格中的文本内容。

表格结构识别默认采用slanet_plus算法,用来识别表格结构。

该部分代码来自RapidTable这个仓库。

仓库地址:https://github.com/RapidAI/RapidTable

表格识别同样可以在配置文件中自由选择关闭,和公式处理一样,通过enable参数来控制开启和关闭。

    "table-config": {
        "model""rapid_table",
        "sub_model""slanet_plus",
        "enable"true,
        "max_time"400
    },

5. 结果输出

最后可输出结果包括:

  • model.pdf:布局分析的可视化结果
  • file_layout.pdf:布局分析后,去除页眉页脚,标记真正内容的可视化结果
  • spans.pdf:文本/图像/公式/表格区域检测的可视化结果
  • middle.json:每一区域识别的中间结果
  • content_list.json:分块内容识别结果,对每段落的小块区域进行合并

具体的相关接口参见magic_pdf\operators\pipes.py

其它文件处理

由于MinerU是针对PDF进行解析,因此本身的处理管道并不能处理其它格式的文件。

v1.0.1版本后,进一步支持图像(.jpg及.png)、Word(.doc及.docx)、以及PPT(.ppt及.pptx)三类文件的解析。

1. MS-Office文件解析示例

对于 MS-Office 文件,需要通过 LibreOffice 将其转换成 PDF文件,再复用PDF的解析管道。(本地使用时,需要提前安装LibreOffice,并设置相应环境变量)

python示例脚本如下:

import os

from magic_pdf.data.data_reader_writer import FileBasedDataWriter, FileBasedDataReader
from magic_pdf.model.doc_analyze_by_custom_model import doc_analyze
from magic_pdf.data.read_api import read_local_office

# prepare env
local_image_dir, local_md_dir = "output/images""output"
image_dir = str(os.path.basename(local_image_dir))

os.makedirs(local_image_dir, exist_ok=True)

image_writer, md_writer = FileBasedDataWriter(local_image_dir), FileBasedDataWriter(
    local_md_dir
)

# proc
## Create Dataset Instance
input_file = "demo.pptx"     # replace with real ms-office file

input_file_name = input_file.split(".")[0]
ds = read_local_office(input_file)[0]

infer_result = ds.apply(doc_analyze, ocr=True)

## 处理管道
pipe_result = infer_result.pipe_ocr_mode(image_writer) 

pipe_result.dump_md(md_writer, f"{input_file_name}.md", image_dir)

### 获取内容列表(JSON格式)
content_list_content = pipe_result.get_content_list(image_dir)

### 保存内容列表到JSON文件
pipe_result.dump_content_list(md_writer, f"{input_file_name}_content_list.json", image_dir)

### 获取中间JSON格式数据
middle_json_content = pipe_result.get_middle_json()

### 保存中间JSON数据
pipe_result.dump_middle_json(md_writer, f'{input_file_name}_middle.json')

具体转换过程在magic_pdf\data\read_api.py文件的read_local_office方法。

考虑到Excel文件也属于MS-Office文件,因此,拿excel文件试了一下,发现也能正常跑通,只是会将整张表格内容变成一个html的格式,没有自动将每行内容区分开。

另外,拿这个脚本试了下.txt.md文件,发现也能正常使用,后面可以直接借此拓展RagflowPlus的文件支持形式。

2. 图片文件解析示例

对于图片文件,无需转换成pdf,原本的管道可兼容,示例python脚本如下:

import os

from magic_pdf.data.data_reader_writer import FileBasedDataWriter, FileBasedDataReader
from magic_pdf.model.doc_analyze_by_custom_model import doc_analyze
from magic_pdf.data.read_api import read_local_images

# prepare env
local_image_dir, local_md_dir = "output/images""output"
image_dir = str(os.path.basename(local_image_dir))

os.makedirs(local_image_dir, exist_ok=True)

image_writer, md_writer = FileBasedDataWriter(local_image_dir), FileBasedDataWriter(
    local_md_dir
)

# proc
## Create Dataset Instance
input_file = "demo.jpg"   # replace with real image file
input_file_name = input_file.split(".")[0]

ds = read_local_images(input_file)[0]

infer_result = ds.apply(doc_analyze, ocr=True)

## 处理管道
pipe_result = infer_result.pipe_ocr_mode(image_writer) 

pipe_result.dump_md(md_writer, f"{input_file_name}.md", image_dir)

### 获取内容列表(JSON格式)
content_list_content = pipe_result.get_content_list(image_dir)

### 保存内容列表到JSON文件
pipe_result.dump_content_list(md_writer, f"{input_file_name}_content_list.json", image_dir)

### 获取中间JSON格式数据
middle_json_content = pipe_result.get_middle_json()

### 保存中间JSON数据
pipe_result.dump_middle_json(md_writer, f'{input_file_name}_middle.json')

补充材料

查看源代码时,发现了MinerU实际上复用了很多PDF-Extract-Kit的代码。

MinerU更多的是工程上的整合,如需更细致地挖掘某个步骤,可参考同属opendatalabPDF-Extract-Kit这个仓库。

53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询

扫码登录
登录即表示您同意《53AI网站服务协议》
服务协议

欢迎您使用【53AI 官方网站】(以下简称“本网站”或“我们”)。本《会员服务协议》(以下简称“本协议”)是您(以下简称“会员”或“用户”)与【深圳市博思协创网络科技有限公司】之间关于注册、登录及使用本网站会员服务所订立的法律协议。

在您注册或登录前,请务必审慎阅读、充分理解各条款内容,特别是免除或限制责任的条款、知识产权条款、争议解决条款等。此类条款将以加粗形式提示您注意。 当您通过微信公众号授权、手机验证码验证或其他方式成功登录本网站时,即视为您已完全理解并同意接受本协议的全部内容。

一、 定义

本网站:指由【深圳市博思协创网络科技有限公司】运营的,域名为【53ai.com】的网站及相关移动端页面。

会员服务:指本网站向注册会员提供的知识库文章查阅、内容检索及其他相关增值服务。

知识库内容:指本网站发布的包括但不限于文字、图表、数据、研究报告、行业分析等数字化内容资源。

二、 账号注册与登录

登录方式:本网站支持以下登录方式,您可根据实际情况选择:

微信公众号授权登录:您同意将您的微信OpenID信息授权给本网站,用于创建或关联会员账号。

手机验证码登录:您需提供真实有效的手机号码,并通过短信验证码完成身份验证与登录/注册。

账号安全:您的账号仅限您本人使用,禁止赠与、借用、租用、转让或售卖。因您保管不善导致的账号被盗、密码泄露等损失,由您自行承担。

实名认证:根据相关法律法规要求,我们可能要求您在特定功能下完成实名认证。如您拒绝提供,可能无法使用部分或全部服务。

未成年人保护:若您未满18周岁,请在法定监护人的陪同下阅读本协议,并在征得监护人同意后使用本服务。

三、 服务内容与规范

知识库查阅权限:会员登录后,有权按照其会员等级对应的权限范围,在线浏览、检索本网站知识库中的相关文章及内容。

服务变更:我们有权根据业务发展需要,调整、变更或终止部分服务内容,并将以网站公告、公众号消息等方式提前通知。

禁止行为:您在使用服务时不得实施以下行为:

利用技术手段批量爬取、下载、转存知识库内容;

将知识库内容用于商业目的或未经授权地向第三方传播;

干扰本网站正常运行或侵犯其他用户合法权益;

发布违法违规信息或从事违反公序良俗的活动。

四、 知识产权声明

权利归属:本网站知识库中的排版设计、软件代码等内容的知识产权均归【公司全称】或原权利人所有,受《中华人民共和国著作权法》等法律保护。

有限许可:本网站授予会员一项非独占、不可转让、不可转授权的普通许可,仅限于个人学习、研究之目的在线查阅知识库内容。

侵权追责:未经书面许可,任何单位或个人不得以任何形式复制、转载、摘编、镜像、汇编或以其他方式使用上述内容。一经发现,我们保留追究其法律责任的权利。

五、 个人信息保护

我们重视对您个人信息的保护。关于我们如何收集、使用、存储和保护您的个人信息,请单独阅读 《隐私政策》。

您通过微信公众号授权或手机号验证所提供的信息,我们将严格按照《个人信息保护法》的规定处理,仅用于身份识别、服务提供及安全验证等必要用途。

您可以随时通过网站设置或联系客服行使查阅、更正、删除个人信息及撤回授权同意的权利。

六、 免责声明

内容准确性:知识库内容仅供参考,不构成专业建议。我们不对其完整性、准确性、时效性作任何明示或暗示的保证,您应自行判断并承担使用风险。

不可抗力:因自然灾害、政策法规变化、网络故障、第三方平台接口异常(如微信接口维护、运营商短信通道故障)等不可抗力导致的服务中断或延迟,我们不承担违约责任。

第三方链接:本网站可能包含指向第三方网站的链接,该等网站的内容和服务不受我们控制,请您自行甄别风险。

七、 违约责任

如您违反本协议约定,我们有权视情节采取警告、限制功能、暂停服务、注销账号等措施,并保留要求赔偿损失的权利。

如因您的违约行为导致我们遭受行政处罚、第三方索赔或商誉损失,您应承担全部赔偿责任(包括但不限于罚款、赔偿金、律师费、公证费等)。

八、 法律适用与争议解决

本协议的订立、执行和解释均适用中华人民共和国大陆地区法律。

因本协议产生的或与本协议有关的任何争议,双方应友好协商解决;协商不成的,任何一方均可向【公司所在地】有管辖权的人民法院提起诉讼。

九、 其他

本协议构成双方就本服务达成的完整协议,取代此前任何口头或书面约定。

本协议任一条款被认定为无效或不可执行的,不影响其他条款的效力。

我们对本协议享有最终解释权,并在法律允许的范围内保留随时修改的权利。修改后的协议一经公布即生效,继续使用服务即视为同意修订内容。


已查阅