RAG 文档解析实战:PyMuPDF 与 Unstructured 的使用

📖 目录

  • [一、RAG 为什么需要文档解析?](#一、RAG 为什么需要文档解析?)
  • [二、PyMuPDF 和 Unstructured 的定位区别](#二、PyMuPDF 和 Unstructured 的定位区别)
  • [三、PyMuPDF 简介](#三、PyMuPDF 简介)
  • [四、PyMuPDF 基础用法](#四、PyMuPDF 基础用法)
    • [1. 提取 PDF 文本](#1. 提取 PDF 文本)
    • [2. 提取文本块](#2. 提取文本块)
    • [3. 提取字典结构](#3. 提取字典结构)
    • [4. 把 PDF 页面渲染成图片](#4. 把 PDF 页面渲染成图片)
    • [5. 提取 PDF 中的图片](#5. 提取 PDF 中的图片)
    • [6. 表格提取](#6. 表格提取)
  • [五、PyMuPDF 在 RAG 中的推荐用法](#五、PyMuPDF 在 RAG 中的推荐用法)
    • [1. 普通 PDF 文本知识库](#1. 普通 PDF 文本知识库)
    • [2. 扫描件 PDF](#2. 扫描件 PDF)
    • [3. 图片较多的 PDF](#3. 图片较多的 PDF)
    • [4. 表格较多的 PDF](#4. 表格较多的 PDF)
  • [六、Unstructured 简介](#六、Unstructured 简介)
  • [七、Unstructured 基础用法](#七、Unstructured 基础用法)
    • [1. 自动解析文档](#1. 自动解析文档)
    • [2. 解析 PDF](#2. 解析 PDF)
    • [3. 解析图片](#3. 解析图片)
  • [八、Unstructured 的 Title 是怎么来的?](#八、Unstructured 的 Title 是怎么来的?)
  • [九、Unstructured Chunking 用法](#九、Unstructured Chunking 用法)
    • [1. 按标题切 Chunk](#1. 按标题切 Chunk)
    • [2. 为什么 Unstructured 的 chunking 和普通文本切分不同?](#2. 为什么 Unstructured 的 chunking 和普通文本切分不同?)
  • [十、QA 格式文档怎么处理?](#十、QA 格式文档怎么处理?)
    • [QA 数据向量化问题还是答案?](#QA 数据向量化问题还是答案?)
  • [十一、QA 里有图片怎么办?](#十一、QA 里有图片怎么办?)
  • [十二、QA 里有表格怎么办?](#十二、QA 里有表格怎么办?)
  • [十三、PyMuPDF 与 Unstructured 的组合方案](#十三、PyMuPDF 与 Unstructured 的组合方案)
    • [方案 1:Unstructured 主导](#方案 1:Unstructured 主导)
    • [方案 2:PyMuPDF 主导](#方案 2:PyMuPDF 主导)
    • [方案 3:混合方案](#方案 3:混合方案)
  • [十四、RAG 入库数据结构推荐](#十四、RAG 入库数据结构推荐)
  • 十五、总结

一、RAG 为什么需要文档解析?

RAG 的完整流程通常是:

text 复制代码
原始文档
  -> 文档解析
  -> 文本清洗
  -> Chunk 切分
  -> 向量化
  -> 写入向量库
  -> 检索
  -> LLM 生成回答

在这个流程里,文档解析非常关键。如果 PDF、Word、图片、表格在解析阶段就被处理坏了,后面的向量检索和问答效果都会下降。

常见问题包括:

  • PDF 提取出来文字顺序错乱;
  • 扫描件 PDF 没有可复制文本;
  • 表格被拆成一堆无意义的行;
  • 图片里的文字没有 OCR;
  • 标题和正文没有结构;
  • Chunk 切得太碎或太长;
  • 检索结果没有页码、来源、章节信息。

PyMuPDF 和 Unstructured 都是 RAG 文档处理里常见的工具,但它们定位不同。

二、PyMuPDF 和 Unstructured 的定位区别

工具 核心定位 更适合做什么
PyMuPDF PDF 底层解析与页面处理库 提取文本、图片、坐标、页面渲染、表格检测
Unstructured 多格式文档解析与结构化预处理框架 把 PDF、Word、HTML、图片等解析成结构化 Element,并支持 chunk

可以简单理解:

text 复制代码
PyMuPDF 更像 PDF 手术刀。
Unstructured 更像 RAG 文档预处理流水线。

如果你只处理 PDF,并且想要更细粒度控制页面、坐标、图片、表格,PyMuPDF 很合适。

如果你要处理多种格式,例如 PDF、DOCX、HTML、PPTX、图片,并希望直接拿到 TitleNarrativeTextTable 等结构化元素,Unstructured 更方便。

三、PyMuPDF 简介

PyMuPDF 是一个高性能 Python 文档处理库,底层基于 MuPDF。它可以处理 PDF、XPS、EPUB 等格式。RAG 中最常用的是 PDF 解析。

安装:

bash 复制代码
pip install pymupdf

导入方式:

python 复制代码
import fitz

注意:PyMuPDF 的导入名是 fitz

四、PyMuPDF 基础用法

1. 提取 PDF 文本

python 复制代码
import fitz

doc = fitz.open("demo.pdf")

for page_index, page in enumerate(doc):
    text = page.get_text()
    print(f"第 {page_index + 1} 页")
    print(text)

page.get_text() 默认提取纯文本,适合普通可复制文本 PDF。

如果是扫描件 PDF,可能提取不到文字,需要先 OCR。

2. 提取文本块

python 复制代码
import fitz

doc = fitz.open("demo.pdf")
page = doc[0]

blocks = page.get_text("blocks")

for block in blocks:
    print(block)

blocks 会返回带坐标的文本块,常用于:

  • 分析页面布局;
  • 判断标题和正文;
  • 按区域抽取内容;
  • 保留页码和坐标 metadata。

3. 提取字典结构

python 复制代码
import fitz

doc = fitz.open("demo.pdf")
page = doc[0]

data = page.get_text("dict")

for block in data["blocks"]:
    if block["type"] == 0:
        for line in block["lines"]:
            line_text = "".join(span["text"] for span in line["spans"])
            print(line_text)

dict 结构里有更详细的字体、字号、坐标信息。你可以根据字号、加粗、位置来粗略判断标题。

4. 把 PDF 页面渲染成图片

python 复制代码
import fitz

doc = fitz.open("demo.pdf")

for i, page in enumerate(doc):
    pix = page.get_pixmap()
    pix.save(f"page_{i + 1}.png")

如果想提高图片分辨率:

python 复制代码
import fitz

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 中的图片

python 复制代码
import fitz

doc = fitz.open("demo.pdf")

for page_index, page in enumerate(doc):
    images = page.get_images(full=True)

    for image_index, img in enumerate(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}"
        with open(image_path, "wb") as f:
            f.write(image_bytes)

RAG 中图片一般不要直接向量化原图,而是:

text 复制代码
图片 -> OCR / caption -> 文本描述参与向量化
原图路径 -> metadata 保存,用于回答时引用或展示

6. 表格提取

PyMuPDF 新版本支持页面表格检测。典型用法类似:

python 复制代码
import fitz

doc = fitz.open("demo.pdf")
page = doc[0]

tables = page.find_tables()

for table in tables:
    df = table.to_pandas()
    print(df)

注意:

  • 表格检测依赖 PDF 版面质量;
  • 复杂跨页表格、无线框表格可能识别不稳定;
  • 如果表格很重要,建议结合 pdfplumber、Camelot、Tabula 或 Unstructured 的表格能力做对比。

五、PyMuPDF 在 RAG 中的推荐用法

1. 普通 PDF 文本知识库

text 复制代码
PyMuPDF 提取文本
  -> 按页保存 page metadata
  -> 清洗页眉页脚
  -> 按标题或段落切 chunk
  -> 向量化

示例数据结构:

json 复制代码
{
  "content": "这里是第 3 页中的一段正文...",
  "metadata": {
    "source": "manual.pdf",
    "page": 3,
    "type": "text"
  }
}

2. 扫描件 PDF

text 复制代码
PyMuPDF 渲染页面图片
  -> OCR
  -> 得到文本
  -> 切 chunk
  -> 向量化

3. 图片较多的 PDF

text 复制代码
PyMuPDF 提取图片
  -> OCR / caption
  -> 图片说明进入 embedding_text
  -> 原图路径进入 metadata

示例:

json 复制代码
{
  "embedding_text": "登录页面截图,包含用户名、密码、验证码、登录按钮。",
  "metadata": {
    "source": "manual.pdf",
    "page": 5,
    "image_path": "/assets/page_5_image_1.png",
    "type": "image"
  }
}

4. 表格较多的 PDF

表格不要只当普通文本处理,建议保存三份:

text 复制代码
Markdown 表格:给 LLM 阅读
CSV / JSON:用于精确查询和计算
表格摘要:参与向量检索

六、Unstructured 简介

Unstructured 是一个面向 LLM / RAG 的文档预处理库。它可以把 PDF、HTML、Word、PPT、图片等文件解析成结构化元素。

安装:

bash 复制代码
pip install unstructured

如果处理 PDF、图片和 OCR,通常需要额外依赖。实际项目中可以按官方文档安装对应 extras,例如 PDF、OCR、local inference 相关依赖。

Unstructured 的核心思想是:

text 复制代码
先 partition 成 Elements
再基于 Elements 做 chunking

常见 Element 类型:

Element 类型 含义
Title 标题
NarrativeText 正文段落
ListItem 列表项
Table 表格
Image 图片
Header 页眉
Footer 页脚

七、Unstructured 基础用法

1. 自动解析文档

python 复制代码
from unstructured.partition.auto import partition

elements = partition(filename="demo.pdf")

for e in elements:
    print(type(e).__name__, e.text[:80] if hasattr(e, "text") else "")

这会自动根据文件类型选择解析方式。

2. 解析 PDF

python 复制代码
from unstructured.partition.pdf import partition_pdf

elements = partition_pdf(
    filename="demo.pdf",
    strategy="hi_res",
    infer_table_structure=True,
)

for e in elements:
    print(type(e).__name__, e.text[:100])

常见参数:

参数 说明
strategy="fast" 速度快,适合可复制文本 PDF
strategy="hi_res" 使用版面分析,适合复杂 PDF、扫描件、表格、标题识别
infer_table_structure=True 尝试识别表格结构

3. 解析图片

python 复制代码
from unstructured.partition.image import partition_image

elements = partition_image(
    filename="page.png",
    strategy="hi_res",
)

for e in elements:
    print(type(e).__name__, e.text)

图片解析一般依赖 OCR 和布局识别。

八、Unstructured 的 Title 是怎么来的?

Unstructured 不是在 chunk 阶段凭空判断标题,而是在 partition 阶段把文档解析成 Elements。

如果某段内容被识别为:

python 复制代码
Title

那么 chunk_by_title 才能把它当作章节边界。

流程如下:

text 复制代码
原始文档
  -> partition
  -> Title / NarrativeText / ListItem / Table 等 Elements
  -> chunk_by_title
  -> 按 Title 边界切 chunk

九、Unstructured Chunking 用法

1. 按标题切 Chunk

python 复制代码
from unstructured.partition.pdf import partition_pdf
from unstructured.chunking.title import chunk_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,
)

for chunk in chunks:
    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 往往是:

text 复制代码
纯文本 -> 按字符数 / token 数硬切

Unstructured 更像:

text 复制代码
文档 -> Element 结构 -> 根据标题、段落、表格等结构切

优势是能保留文档结构,尤其适合说明书、政策文档、FAQ、操作手册。

十、QA 格式文档怎么处理?

推荐结构:

json 复制代码
{
  "question": "访问中心站需要哪些条件?",
  "answer": "中心站部署...",
  "embedding_text": "问题:访问中心号。",
  "metadata": {
    "source": "中心站说明文档.pdf",
    "page": 1,
    "section": "1"
  }
}

QA 数据向量化问题还是答案?

推荐:

text 复制代码
用于检索:问题 + 同义问题 + 答案摘要 + 关键词
用于回答:完整答案

不要只向量化答案,也不要只保存问题。

更稳的 embedding_text

text 复制代码
问题:如何登录中心站?
同义问题:中心站怎么登录?登录中心站需要什么?
答案摘要:通过浏览器访问中心站网址,输入用户名、密码和手机验证码登录。
关键词:登录、网址、用户名、密码、手机验证码、验证码。

十一、QA 里有图片怎么办?

图片不要直接塞进向量库。推荐流程:

text 复制代码
图片
  -> OCR
  -> caption
  -> OCR 文本和 caption 参与向量化
  -> 原图路径保存到 metadata

示例:

json 复制代码
{
  "type": "image",
  "path": "/assets/page_2_image_1.png",
  "caption": "登录页面截图,包含用户名输入框、密码输入框、发送手机验证码按钮和登录按钮。",
  "ocr_text": "用户名 密码 发送手机验证码 验证码 登录"
}

然后把这些信息加入 embedding_text

text 复制代码
图片说明:登录页面截图,包含用户名、密码、验证码和登录按钮。
图片 OCR:用户名、密码、发送手机验证码、验证码、登录。

这样用户问:

text 复制代码
登录页面验证码按钮在哪里?

也有机会检索到正确 QA。

十二、QA 里有表格怎么办?

表格建议不要只做 caption,也不要只把表格原文混进一大段文本。

推荐保存三种形态:

text 复制代码
Markdown 表格:给 LLM 看。
CSV / JSON:用于精确查询和计算。
表格摘要:参与向量检索。

例如:

markdown 复制代码
| 项目 | 最低配置 | 推荐配置 |
| --- | --- | --- |
| CPU | i5 | i7 / Ryzen 7 |
| 内存 | 8GB | 16GB |
| 显卡 | 集成显卡 | GTX 1660 |

表格摘要:

text 复制代码
表格说明访问中心站的硬件配置要求。最低配置为 i5、8GB 内存、集成显卡;推荐配置为 i7 或 Ryzen 7、16GB 内存、GTX 1660。

向量化文本可以写成:

text 复制代码
问题:访问中心?
答案摘要:需要电脑,最低配置 i5、8GB 内存、集成显卡;推荐 i7/Ryzen 7、16GB 内存、GTX 1660。
表格关键词:CPU、内存、显卡、最低配置、推荐配置。

如果用户问计算类问题:

text 复制代码
推荐内存比最低内存多多少?

更好的做法是:

text 复制代码
检索到表格
  -> 读取 CSV / JSON
  -> 用程序计算
  -> LLM 解释结果

不要完全依赖模型从自然语言表格里猜。

十三、PyMuPDF 与 Unstructured 的组合方案

实际项目中,两者可以组合使用。

方案 1:Unstructured 主导

适合多格式文档:

text 复制代码
Unstructured partition
  -> Elements
  -> chunk_by_title
  -> 表格摘要 / 图片 OCR
  -> 向量化

优点:

  • 多格式统一;
  • Title、Table、ListItem 等结构清晰;
  • chunking 更贴近文档结构。

缺点:

  • 依赖较多;
  • 复杂 PDF 的效果和环境配置有关;
  • 需要检查 Title、Table 是否识别正确。

方案 2:PyMuPDF 主导

适合 PDF 控制更细的场景:

text 复制代码
PyMuPDF 按页解析
  -> 提取文本 / 图片 / 坐标 / 表格
  -> 自定义规则切 chunk
  -> 向量化

优点:

  • 速度快;
  • PDF 级控制强;
  • 方便保留页码、坐标、图片路径。

缺点:

  • 标题、段落、表格结构需要自己处理;
  • 多格式支持不如 Unstructured 统一。

方案 3:混合方案

推荐给生产项目:

text 复制代码
先用 Unstructured 获取结构化 Elements
如果表格 / 图片 / 页码不满足要求
再用 PyMuPDF 做补充提取

例如:

text 复制代码
Unstructured 负责标题、正文、表格初步解析。
PyMuPDF 负责页面截图、图片抽取、坐标和页码补充。

十四、RAG 入库数据结构推荐

无论使用 PyMuPDF 还是 Unstructured,最终建议统一成类似结构:

json 复制代码
{
  "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": "硬件配置要求表"
      }
    ]
  }
}

向量库里主要存:

text 复制代码
embedding_text
metadata

回答时再根据 metadata 取:

text 复制代码
完整答案
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 中不是互相替代,而是互补关系:

text 复制代码
PyMuPDF 适合精细控制 PDF:文本、图片、坐标、页面渲染、表格检测。
Unstructured 适合统一处理多格式文档:Title、NarrativeText、ListItem、Table 等结构化 Elements。