📖 目录
- 一、RAG 为什么需要文档解析?
- 二、PyMuPDF 和 Unstructured 的定位区别
- 三、PyMuPDF 简介
- 四、PyMuPDF 基础用法
- 1. 提取 PDF 文本
- 2. 提取文本块
- 3. 提取字典结构
- 4. 把 PDF 页面渲染成图片
- 5. 提取 PDF 中的图片
- 6. 表格提取
- 五、PyMuPDF 在 RAG 中的推荐用法
- 1. 普通 PDF 文本知识库
- 2. 扫描件 PDF
- 3. 图片较多的 PDF
- 4. 表格较多的 PDF
- 六、Unstructured 简介
- 七、Unstructured 基础用法
- 1. 自动解析文档
- 2. 解析 PDF
- 3. 解析图片
- 八、Unstructured 的 Title 是怎么来的?
- 九、Unstructured Chunking 用法
- 1. 按标题切 Chunk
- 2. 为什么 Unstructured 的 chunking 和普通文本切分不同?
- 十、QA 格式文档怎么处理?
- QA 数据向量化问题还是答案?
- 十一、QA 里有图片怎么办?
- 十二、QA 里有表格怎么办?
- 十三、PyMuPDF 与 Unstructured 的组合方案
- 方案 1:Unstructured 主导
- 方案 2:PyMuPDF 主导
- 方案 3:混合方案
- 十四、RAG 入库数据结构推荐
- 十五、总结
一、RAG 为什么需要文档解析?
RAG 的完整流程通常是:
原始文档 -> 文档解析 -> 文本清洗 -> Chunk 切分 -> 向量化 -> 写入向量库 -> 检索 -> LLM 生成回答在这个流程里,文档解析非常关键。如果 PDF、Word、图片、表格在解析阶段就被处理坏了,后面的向量检索和问答效果都会下降。
常见问题包括:
- PDF 提取出来文字顺序错乱;
- 扫描件 PDF 没有可复制文本;
- 表格被拆成一堆无意义的行;
- 图片里的文字没有 OCR;
- 标题和正文没有结构;
- Chunk 切得太碎或太长;
- 检索结果没有页码、来源、章节信息。
PyMuPDF 和 Unstructured 都是 RAG 文档处理里常见的工具,但它们定位不同。
二、PyMuPDF 和 Unstructured 的定位区别
| 工具 | 核心定位 | 更适合做什么 |
|---|---|---|
| PyMuPDF | PDF 底层解析与页面处理库 | 提取文本、图片、坐标、页面渲染、表格检测 |
| Unstructured | 多格式文档解析与结构化预处理框架 | 把 PDF、Word、HTML、图片等解析成结构化 Element,并支持 chunk |
可以简单理解:
PyMuPDF 更像 PDF 手术刀。 Unstructured 更像 RAG 文档预处理流水线。如果你只处理 PDF,并且想要更细粒度控制页面、坐标、图片、表格,PyMuPDF 很合适。
如果你要处理多种格式,例如 PDF、DOCX、HTML、PPTX、图片,并希望直接拿到Title、NarrativeText、Table等结构化元素,Unstructured 更方便。
三、PyMuPDF 简介
PyMuPDF 是一个高性能 Python 文档处理库,底层基于 MuPDF。它可以处理 PDF、XPS、EPUB 等格式。RAG 中最常用的是 PDF 解析。
安装:
pipinstallpymupdf导入方式:
importfitz注意:PyMuPDF 的导入名是fitz。
四、PyMuPDF 基础用法
1. 提取 PDF 文本
importfitz doc=fitz.open("demo.pdf")forpage_index,pageinenumerate(doc):text=page.get_text()print(f"第{page_index+1}页")print(text)page.get_text()默认提取纯文本,适合普通可复制文本 PDF。
如果是扫描件 PDF,可能提取不到文字,需要先 OCR。
2. 提取文本块
importfitz doc=fitz.open("demo.pdf")page=doc[0]blocks=page.get_text("blocks")forblockinblocks:print(block)blocks会返回带坐标的文本块,常用于:
- 分析页面布局;
- 判断标题和正文;
- 按区域抽取内容;
- 保留页码和坐标 metadata。
3. 提取字典结构
importfitz doc=fitz.open("demo.pdf")page=doc[0]data=page.get_text("dict")forblockindata["blocks"]:ifblock["type"]==0:forlineinblock["lines"]:line_text="".join(span["text"]forspaninline["spans"])print(line_text)dict结构里有更详细的字体、字号、坐标信息。你可以根据字号、加粗、位置来粗略判断标题。
4. 把 PDF 页面渲染成图片
importfitz doc=fitz.open("demo.pdf")fori,pageinenumerate(doc):pix=page.get_pixmap()pix.save(f"page_{i+1}.png")如果想提高图片分辨率:
importfitz doc=fitz.open("demo.pdf")page=doc[0]matrix=fitz.Matrix(2,2)pix=page.get_pixmap(matrix=matrix)pix.save("page_1_2x.png")这在 OCR、视觉模型理解、图片问答中很常用。
5. 提取 PDF 中的图片
importfitz doc=fitz.open("demo.pdf")forpage_index,pageinenumerate(doc):images=page.get_images(full=True)forimage_index,imginenumerate(images):xref=img[0]base_image=doc.extract_image(xref)image_bytes=base_image["image"]image_ext=base_image["ext"]image_path=f"page_{page_index+1}_image_{image_index+1}.{image_ext}"withopen(image_path,"wb")asf:f.write(image_bytes)RAG 中图片一般不要直接向量化原图,而是:
图片 -> OCR / caption -> 文本描述参与向量化 原图路径 -> metadata 保存,用于回答时引用或展示6. 表格提取
PyMuPDF 新版本支持页面表格检测。典型用法类似:
importfitz doc=fitz.open("demo.pdf")page=doc[0]tables=page.find_tables()fortableintables:df=table.to_pandas()print(df)注意:
- 表格检测依赖 PDF 版面质量;
- 复杂跨页表格、无线框表格可能识别不稳定;
- 如果表格很重要,建议结合
pdfplumber、Camelot、Tabula 或 Unstructured 的表格能力做对比。
五、PyMuPDF 在 RAG 中的推荐用法
1. 普通 PDF 文本知识库
PyMuPDF 提取文本 -> 按页保存 page metadata -> 清洗页眉页脚 -> 按标题或段落切 chunk -> 向量化示例数据结构:
{"content":"这里是第 3 页中的一段正文...","metadata":{"source":"manual.pdf","page":3,"type":"text"}}2. 扫描件 PDF
PyMuPDF 渲染页面图片 -> OCR -> 得到文本 -> 切 chunk -> 向量化3. 图片较多的 PDF
PyMuPDF 提取图片 -> OCR / caption -> 图片说明进入 embedding_text -> 原图路径进入 metadata示例:
{"embedding_text":"登录页面截图,包含用户名、密码、验证码、登录按钮。","metadata":{"source":"manual.pdf","page":5,"image_path":"/assets/page_5_image_1.png","type":"image"}}4. 表格较多的 PDF
表格不要只当普通文本处理,建议保存三份:
Markdown 表格:给 LLM 阅读 CSV / JSON:用于精确查询和计算 表格摘要:参与向量检索六、Unstructured 简介
Unstructured 是一个面向 LLM / RAG 的文档预处理库。它可以把 PDF、HTML、Word、PPT、图片等文件解析成结构化元素。
安装:
pipinstallunstructured如果处理 PDF、图片和 OCR,通常需要额外依赖。实际项目中可以按官方文档安装对应 extras,例如 PDF、OCR、local inference 相关依赖。
Unstructured 的核心思想是:
先 partition 成 Elements 再基于 Elements 做 chunking常见 Element 类型:
| Element 类型 | 含义 |
|---|---|
Title | 标题 |
NarrativeText | 正文段落 |
ListItem | 列表项 |
Table | 表格 |
Image | 图片 |
Header | 页眉 |
Footer | 页脚 |
七、Unstructured 基础用法
1. 自动解析文档
fromunstructured.partition.autoimportpartition elements=partition(filename="demo.pdf")foreinelements:print(type(e).__name__,e.text[:80]ifhasattr(e,"text")else"")这会自动根据文件类型选择解析方式。
2. 解析 PDF
fromunstructured.partition.pdfimportpartition_pdf elements=partition_pdf(filename="demo.pdf",strategy="hi_res",infer_table_structure=True,)foreinelements:print(type(e).__name__,e.text[:100])常见参数:
| 参数 | 说明 |
|---|---|
strategy="fast" | 速度快,适合可复制文本 PDF |
strategy="hi_res" | 使用版面分析,适合复杂 PDF、扫描件、表格、标题识别 |
infer_table_structure=True | 尝试识别表格结构 |
3. 解析图片
fromunstructured.partition.imageimportpartition_image elements=partition_image(filename="page.png",strategy="hi_res",)foreinelements:print(type(e).__name__,e.text)图片解析一般依赖 OCR 和布局识别。
八、Unstructured 的 Title 是怎么来的?
Unstructured 不是在 chunk 阶段凭空判断标题,而是在 partition 阶段把文档解析成 Elements。
如果某段内容被识别为:
Title那么chunk_by_title才能把它当作章节边界。
流程如下:
原始文档 -> partition -> Title / NarrativeText / ListItem / Table 等 Elements -> chunk_by_title -> 按 Title 边界切 chunk九、Unstructured Chunking 用法
1. 按标题切 Chunk
fromunstructured.partition.pdfimportpartition_pdffromunstructured.chunking.titleimportchunk_by_title elements=partition_pdf(filename="demo.pdf",strategy="hi_res",infer_table_structure=True,)chunks=chunk_by_title(elements,max_characters=1200,new_after_n_chars=1000,combine_text_under_n_chars=200,)forchunkinchunks:print(type(chunk).__name__)print(chunk.text[:300])参数说明:
| 参数 | 作用 |
|---|---|
max_characters | 单个 chunk 最大字符数 |
new_after_n_chars | 超过这个长度后倾向开启新 chunk |
combine_text_under_n_chars | 太短的 section 尽量合并 |
2. 为什么 Unstructured 的 chunking 和普通文本切分不同?
普通 splitter 往往是:
纯文本 -> 按字符数 / token 数硬切Unstructured 更像:
文档 -> Element 结构 -> 根据标题、段落、表格等结构切优势是能保留文档结构,尤其适合说明书、政策文档、FAQ、操作手册。
十、QA 格式文档怎么处理?
推荐结构:
{"question":"访问中心站需要哪些条件?","answer":"中心站部署...","embedding_text":"问题:访问中心号。","metadata":{"source":"中心站说明文档.pdf","page":1,"section":"1"}}QA 数据向量化问题还是答案?
推荐:
用于检索:问题 + 同义问题 + 答案摘要 + 关键词 用于回答:完整答案不要只向量化答案,也不要只保存问题。
更稳的embedding_text:
问题:如何登录中心站? 同义问题:中心站怎么登录?登录中心站需要什么? 答案摘要:通过浏览器访问中心站网址,输入用户名、密码和手机验证码登录。 关键词:登录、网址、用户名、密码、手机验证码、验证码。十一、QA 里有图片怎么办?
图片不要直接塞进向量库。推荐流程:
图片 -> OCR -> caption -> OCR 文本和 caption 参与向量化 -> 原图路径保存到 metadata示例:
{"type":"image","path":"/assets/page_2_image_1.png","caption":"登录页面截图,包含用户名输入框、密码输入框、发送手机验证码按钮和登录按钮。","ocr_text":"用户名 密码 发送手机验证码 验证码 登录"}然后把这些信息加入embedding_text:
图片说明:登录页面截图,包含用户名、密码、验证码和登录按钮。 图片 OCR:用户名、密码、发送手机验证码、验证码、登录。这样用户问:
登录页面验证码按钮在哪里?也有机会检索到正确 QA。
十二、QA 里有表格怎么办?
表格建议不要只做 caption,也不要只把表格原文混进一大段文本。
推荐保存三种形态:
Markdown 表格:给 LLM 看。 CSV / JSON:用于精确查询和计算。 表格摘要:参与向量检索。例如:
| 项目 | 最低配置 | 推荐配置 | | --- | --- | --- | | CPU | i5 | i7 / Ryzen 7 | | 内存 | 8GB | 16GB | | 显卡 | 集成显卡 | GTX 1660 |表格摘要:
表格说明访问中心站的硬件配置要求。最低配置为 i5、8GB 内存、集成显卡;推荐配置为 i7 或 Ryzen 7、16GB 内存、GTX 1660。向量化文本可以写成:
问题:访问中心? 答案摘要:需要电脑,最低配置 i5、8GB 内存、集成显卡;推荐 i7/Ryzen 7、16GB 内存、GTX 1660。 表格关键词:CPU、内存、显卡、最低配置、推荐配置。如果用户问计算类问题:
推荐内存比最低内存多多少?更好的做法是:
检索到表格 -> 读取 CSV / JSON -> 用程序计算 -> LLM 解释结果不要完全依赖模型从自然语言表格里猜。
十三、PyMuPDF 与 Unstructured 的组合方案
实际项目中,两者可以组合使用。
方案 1:Unstructured 主导
适合多格式文档:
Unstructured partition -> Elements -> chunk_by_title -> 表格摘要 / 图片 OCR -> 向量化优点:
- 多格式统一;
- Title、Table、ListItem 等结构清晰;
- chunking 更贴近文档结构。
缺点:
- 依赖较多;
- 复杂 PDF 的效果和环境配置有关;
- 需要检查 Title、Table 是否识别正确。
方案 2:PyMuPDF 主导
适合 PDF 控制更细的场景:
PyMuPDF 按页解析 -> 提取文本 / 图片 / 坐标 / 表格 -> 自定义规则切 chunk -> 向量化优点:
- 速度快;
- PDF 级控制强;
- 方便保留页码、坐标、图片路径。
缺点:
- 标题、段落、表格结构需要自己处理;
- 多格式支持不如 Unstructured 统一。
方案 3:混合方案
推荐给生产项目:
先用 Unstructured 获取结构化 Elements 如果表格 / 图片 / 页码不满足要求 再用 PyMuPDF 做补充提取例如:
Unstructured 负责标题、正文、表格初步解析。 PyMuPDF 负责页面截图、图片抽取、坐标和页码补充。十四、RAG 入库数据结构推荐
无论使用 PyMuPDF 还是 Unstructured,最终建议统一成类似结构:
{"id":"manual_pdf_page_1_qa_1","embedding_text":"问题:访问中心答案摘要:需要硬件、软件、网络、账号条件。关键词:Windows10、Chrome、固定出口 IP、白名单、账号。","content":"完整答案正文...","metadata":{"source":"manual.pdf","page":1,"section":"1","type":"qa","question":"访问中心站","images":[{"path":"/assets/page_1_image_1.png","caption":"硬件条件示意图"}],"tables":[{"path":"/tables/page_1_table_1.csv","summary":"硬件配置要求表"}]}}向量库里主要存:
embedding_text metadata回答时再根据 metadata 取:
完整答案 Markdown 表格 图片说明 原始文件路径 页码来源十五、总结
| 场景 | 推荐工具 | 推荐处理方式 |
|---|---|---|
| 普通可复制 PDF | PyMuPDF / Unstructured | 提取文本,按页和标题切 chunk |
| 扫描件 PDF | PyMuPDF + OCR / Unstructured hi_res | 页面渲染或 OCR 解析 |
| 多格式文档 | Unstructured | partition 成 Elements,再 chunk |
| 标题结构明显的文档 | Unstructured | chunk_by_title |
| QA / FAQ 文档 | 自定义规则 + Unstructured | 按 QA 单元入库 |
| 图片多的文档 | PyMuPDF + OCR/caption | 图片说明参与向量化,原图保存路径 |
| 表格多的文档 | Unstructured / PyMuPDF / pdfplumber | Markdown + CSV/JSON + 表格摘要 |
| 需要精确页码和坐标 | PyMuPDF | 保存 page、bbox metadata |
PyMuPDF 和 Unstructured 在 RAG 中不是互相替代,而是互补关系:
PyMuPDF 适合精细控制 PDF:文本、图片、坐标、页面渲染、表格检测。 Unstructured 适合统一处理多格式文档:Title、NarrativeText、ListItem、Table 等结构化 Elements。