内容参考于:图灵AI大模型全栈
PDF是文档中最难处理的,因为PDF并不是一个结构化的内容,PDF它的内容杂乱无序,如下图PDF中的表格,这种的就需要识别它,表格中或许还存在图片,单纯的读取只能得到表格中的文字,并没有什么实际的价值,还有图片数据,单纯的读取只能知道这里有一个图片,这就有很多麻烦事,现在也没有比较好的处理方式,只能在现有的功能上做优化
处理PDF首先就要转换,把PDF转成JSON或者Markdown(md的文档),JSON就不用说了它本身就是结构化的,而Markdown它本身也是存在结构,如下图是Markdown文档,它是存在标题的,我们就可以把这些标题看做成结构化,它就是天生用来进行分割的
MinerU
它是上海人工智能实验室 OpenDataLab 团队开源的轻量化多模态智能文档解析工具,主打把杂乱 PDF、图片、Office 文档解析为适合大模型、RAG 知识库使用的干净结构化数据
在PDF中还会存在公式MinerU识别的也是很好,PDF存在的分页(表格中有10条数据,前5条在第5页,后5条在第6页)MinerU会把这种跨页面的进行合并,MinerU它是免费的
开源项目地址:https://github.com/opendatalab/MinerU
在线体验(限速/限量):https://mineru.net/
它有三种方式
| 使用方式 | 难度 | 速度 | 表格识别 | 公式 LaTeX | 批量处理 | 适合人群 |
|---|---|---|---|---|---|---|
| 在线网页版 | ★☆☆ | 较慢 | 部分 | 部分 | 不方便 | 临时体验、少量文件 |
| 一键桌面客户端 | ★★☆ | 快 | 优秀 | 优秀 | 支持 | 大多数普通用户 |
| 本地命令行/Python(通过代码来使用MinerU) | ★★★ | 最快 | 完全可控 | 完全可控 | 极强 | 开发者 / 批量需求 |
通过代码使用MinerU,下方有在线使用和本地客户端使用,我们肯定是要常用代码处理的,所以把代码使用的方式放到了最前面
下载Python库
pip install -U "mineru[all]" pip install hf_xet下载模型,需要下载特定的PDF解析模型,这个模型是MinerU自己的,如果不下载,它会使用在线的模型,在线的会比较慢
from modelscope import snapshot_download snapshot_download('OpenDataLab/PDF-Extract-Kit-1.0', local_dir=r'D:\LLM\Local_model\PDF-Extract-Kit-1.0')模型配置文件,mineru.json文件,pipeline的值是模型在本地的目录
{ "models-dir": { "pipeline": "D:/LLM/Local_model/PDF-Extract-Kit-1.0" } }如下图红框,mineru.json文件的位置,位置就是放到要运行MinerU代码的py文件的同目录(这个目录是我们代码中设置的环境变量决定的,看到代码就知道了),文件名固定mineru.json
注意如果不在py同目录创建mineru.json文件的话,它默认会在下图红框的目录中,也就是我们电脑的用户目录下
代码生成的文件,有图片、原始PDF、Markdown文件(md文件)、json
如下图红框图片会包含表格数据
然后MD文件它生成的存在问题,如下图红框,它的目录都是同级,都是H2级别,都是同级就不好处理,这个问题MinerU它不带,需要我们自己写处理文档数据的代码,这个处理的代码需要根据业务来,可能会写很多处理方式
如下图红框,老版本的MinerU会把表格写成html代码,下图红框是使用的新版MinerU生成的,可以正常显示表格样式
如下图红框,一个井号(#)表示一级标题,两个井号(#)表示二级标题,我们就可以通过拆分井号来得到章节信息
如下图红框,文档中有第一节、第二节。。。的文字,然后它的MinerU处理的PDF的章节都是二级的,我们就可以通过第一节、第二节。。。的文字来进一划分得到一级标题
如下图红框,可以通过它们来得到二级标题
如下图红框的内容,它应该是纯文本,但是被MinerU处理成了标题,所以它也要被处理
代码处理完后的效果:标题都正常了
上图处理的代码
完整代码:自定义处理md文档的代码可以告诉aimd文档的格式或者直接把md文件给ai,然后让ai写自定义处理
import os import re from pathlib import Path # 设置我们本地模型的目录 LOCAL_MODEL_DIR = r"D:\huanjing\ai模型\LLM\Local_model\PDF-Extract-Kit-1.0" # 设置mineru.json目录 LOCAL_MINERU_CONFIG = os.path.join(os.path.dirname(__file__), "mineru.json") # 设置MINERU_MODEL_SOURCE环境变量,local表示优先加载本地模型,默认remote启动时自动联网下载模型 os.environ["MINERU_MODEL_SOURCE"] = "local" # 设置MINERU_TOOLS_CONFIG_JSON环境变量,它的作用是告诉MinerU这个mineru.json文件位置在什么地方 os.environ["MINERU_TOOLS_CONFIG_JSON"] = LOCAL_MINERU_CONFIG from mineru.cli.common import do_parse from mineru.data.data_reader_writer import FileBasedDataWriter # ===== PDF目录 ===== pdf_path = "./data_file/2020-03-17__厦门灿坤实业股份有限公司__200512__闽灿坤__2019年__年度报告.pdf" # 读取文档 with open(pdf_path, "rb") as f: pdf_bytes = f.read() # ===== 文件解析完存放目录 ===== output_dir = "output" os.makedirs(output_dir, exist_ok=True) # ===== 处理的文件名,是一个数组,也必须是数组,可以传递多个 ===== pdf_file_names = [os.path.basename(pdf_path)] # 文件对应的内容,如果传递多个顺序要对应好 pdf_bytes_list = [pdf_bytes] # 自动语言识别 p_lang_list = [""] # ===== 设置图片数据存放位置 ===== img_writer = FileBasedDataWriter( os.path.join(output_dir, "images") ) # 自定义md文件处理 def fix_markdown_headings(md_path): lines = Path(md_path).read_text(encoding="utf-8").splitlines() new_lines = [] for line in lines: stripped = line.strip() # 如果不存在# 就添加成纯文本并且结束本次循环 if not stripped.startswith("#"): new_lines.append(line) continue # lstrip是把左边,也就是开头处的 # 给删除,strip是把前后,也就是开头和结尾处的 空格、换行 \n、制表符 \t 都会删掉 title = stripped.lstrip("#").strip() # 首先匹配第xxx节,我们当前处理的文档是有第一节到第六节,这里文章中有截图写,这里相当于一级标题 if re.match(r"^第[一二三四五六七八九十]+节", title): new_lines.append(f"# {title}") # 匹配一、二、三、四、五、六、七、八、九、十来得到二级标题 elif re.match(r"^[一二三四五六七八九十]+、", title): new_lines.append(f"## {title}") # 通过a-z和A-Z来得到三级标题 elif re.match(r"^(\d+|[a-zA-Z])、", title): new_lines.append(f"### {title}") # 通过 \d 表示的是任意数字,可以用来得到四级标题 elif re.match(r"^(\d+)", title): new_lines.append(f"#### {title}") # 通过是否以 "√", "□", "单位:"开头来得到文本,之前它们被MinerU处理成了标题 elif title.startswith(("√", "□", "单位:")): new_lines.append(title) else: # 文本内容 new_lines.append(line) # 写出文件 Path(md_path).write_text("\n".join(new_lines), encoding="utf-8") def fix_output_markdown_headings(output_dir): # 获取后缀为.md的文件,也就是只处理md文件 for md_path in Path(output_dir).rglob("*.md"): fix_markdown_headings(md_path) if __name__ == '__main__': # ===== 调用MinerU解析PDF文件 ===== # do_parse( # pdf_file_names=pdf_file_names, # pdf_bytes_list=pdf_bytes_list, # p_lang_list=p_lang_list, # output_dir=output_dir, # img_writer=img_writer, # parse_method="auto" # ) # 处理md文件 fix_output_markdown_headings(output_dir)
在线使用
打开https://mineru.net/ 链接点击下图红框
在下图红框上传一个PDF文件
效果图:可以看到它把跨页的表格数据,识别完后合并成了一个,注意它识别的并不是百分百的好
如下图红框,这个页脚识别的就不好,还有一些图片识别的也会不好,MinerU在同类中是比较好用的
本地客户端使用
打开https://mineru.net/链接,鼠标移动到下图红框位置,就会出现下图蓝框的内容,根据自己的电脑系统在下图蓝框中选择对应的操作系统
如下图客户端是这样的,跟网页版一样
把文件拖到下图红框,也就是通过下图红框进行上传,注意它是不需要联网的,它已经把需要的模型下载到本地了
如下图红框,PDF解析完后,它多了一个打开文件夹的按钮(鼠标移动上去才会看到)
这个文件夹中有图片数据、json数据、Markdown数据、原PDF数据
它的图片数据,下图红框都是一些表格图片,就是说它把表格进行了截图,后续如果要使用就通过图片识别的方式来查看表格