给 RAG 知识库准备 PDF 文本时,除了文字,还应保存页码、文本块编号和坐标。这样检索命中后能回到原页核对,遇到双栏错序、页眉混入或扫描页也能定位来源。PyMuPDF 的 blocks 提取可以提供这些字段,但坐标排序不等于理解文档的阅读顺序。
本文处理读者有权使用的本地 PDF,不执行模型调用。以下接口依据为 2026 年 10 月 1 日读取的官方文档;复杂 PDF 的抽取质量需逐页核对,不能把一份教学文件的结果推广到全部文档。

环境与输入范围
在独立 Python 环境安装 python -m pip install pymupdf==1.28.2。先拿一份小的、有可选择文字的 PDF,保存为 source.pdf。扫描页只有图片时,本例不会自动完成 OCR;加密文件、损坏页和复杂版式需要分别处理。
官方文字提取说明展示 page.get_text(“blocks”),并说明提取顺序可能受 PDF 的生成方式影响。本文使用 sort=True 形成坐标顺序,保留原始 block_no 供回查,不声称它能自动正确读取多栏论文或合并表格。
输出一个带出处的 JSONL
将代码保存为 pdf_blocks.py,执行 python pdf_blocks.py。它将当前文件版本的 SHA-256 与页号、块号放在同一记录中,并另外输出空文本页清单。
import hashlib
import json
from pathlib import Path
import pymupdf
source = Path("source.pdf")
digest = hashlib.sha256()
with source.open("rb") as stream:
for piece in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(piece)
source_sha = digest.hexdigest()
empty_pages = []
count = 0
with pymupdf.open(source) as doc, Path("pdf_blocks.jsonl").open(
"w", encoding="utf-8"
) as out:
pages = len(doc)
for page_index, page in enumerate(doc):
found = 0
for block in page.get_text("blocks", sort=True):
x0, y0, x1, y1, text, block_no, block_type = block
if block_type != 0 or not text.strip():
continue
record = {
"source_file": source.name,
"source_sha256": source_sha,
"page": page_index + 1,
"block_no": block_no,
"bbox": [round(v, 3) for v in (x0, y0, x1, y1)],
"text": text.strip(),
}
out.write(json.dumps(record, ensure_ascii=False) + "\n")
count += 1
found += 1
if not found:
empty_pages.append(page_index + 1)
summary = {"pages": pages, "text_blocks": count, "empty_pages": empty_pages}
Path("pdf_extract_summary.json").write_text(
json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8"
)
print(summary)
if not count:
raise ValueError("没有提取到文本;先检查文件或安排 OCR")
官方 TextPage 输出说明给出块元组中的四个坐标、文本、block_no 和 block_type;类型 0 是文本。代码跳过图像块,避免把图像描述误当正文。坐标用于在原页定位,不能单凭矩形推断标题等级、表格单元格或段落语义。
核对三个输出,而不是只看文件生成
- 打开 summary,页数应与 PDF 阅读器一致。empty_pages 非空时逐页确认:是有意空白页、扫描页,还是提取失败,不能直接把这些页当作没有知识。
- 任选三个文本块,按 page 打开原页,核对 bbox 对应位置以及文字、数字、单位和否定词。页码按 1 起算,方便与阅读器对照。
- 检查页首/页尾与双栏页面,确认没有把相邻列错误拼成一个条款。无法可靠提取的页先保留为待处理,不送进自动回答链。
JSONL 中的 source_sha256 标识当前 PDF 的字节版本。同一文件修改后摘要会变化,page/block_no 也可能重排;它们不是跨版本不变的业务 ID。
如何用于 RAG,而不丢掉原文关系
后续切片时保留 source_sha256、page、block_no 和必要的坐标范围。一个片段如果合并多个块,应保存全部组成块的引用;不要只留合并后的字符串和向量,让读者再也无法回查原页。
块边界也不一定是合适的检索片段:标题与正文可能分成不同块,表格可能逐行拆开。先用实际问答任务检查片段是否有完整条件,再决定合并方式。本文只交付文本与出处,不自动判定分段策略或生成向量。
常见失败与下一步
- 整页没有文本:对照 PDF 看是否是扫描图像,再选择 OCR,并保留 OCR 的来源和误识别记录。
- 顺序不对:利用坐标定位列与区域,对当前版式制定规则;不要把 sort=True 当作通用阅读理解。
- 表格数值混乱:按原页核对行列,必要时使用专门的表格提取及人工校验。
- 提取过程中报错:保存文件版本和失败页,不能把部分输出标成完整导入。
先对小 PDF 运行一次,回查几个关键条款及所有空文本页。文字与出处都能对应,再将输出交给知识库的切片和索引环节。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/31975.html