文档加载与解析:让RAG系统读懂你的文件

做RAG系统,第一步就是要把文档喂给机器。但机器不是人,它看不懂PDF里的排版,也读不懂Word里的表格。说白了,我们需要一个“翻译官”——把各种格式的文件,转成机器能理解的纯文本。

我个人习惯把这一步叫做“文档的预处理”。你想想看,如果这一步没做好,后面检索再牛,也是垃圾进垃圾出。我在项目中遇到过太多次了,因为PDF解析漏了一行关键数据,整个问答系统就答非所问。

常见文档格式:PDF、Word、HTML

现实世界里,文档格式五花八门。但RAG系统最常打交道的,就这三种:

  • PDF:最头疼的格式。它本质上是“打印出来的图片”,文字可能被切割、乱序。尤其是扫描件,简直就是噩梦。
  • Word:相对友好。结构清晰,有段落、标题、表格。但要注意,有些Word文档里嵌了图片,图片里的文字是读不出来的。
  • HTML:网页的骨架。标签嵌套复杂,但只要你懂点前端,用BeautifulSoup就能轻松提取正文。

嗯,这里要注意:不要以为所有PDF都能完美解析。我曾经处理过一份政府红头文件,PDF里用了特殊字体,结果解析出来全是乱码。后来我换了个工具才搞定。

文档解析工具:三剑客

工欲善其事,必先利其器。我常用的三把刀:

1. PyMuPDF(fitz)—— PDF解析首选

为什么推荐它?因为它快,而且能处理复杂的PDF布局。我习惯用它来提取文本、表格,甚至图片。

import fitz  # PyMuPDF

doc = fitz.open("report.pdf")
for page_num, page in enumerate(doc):
    text = page.get_text()
    print(f"第{page_num+1}页内容:\n{text}")

你看,代码就这么几行。但有个坑:get_text()默认是按阅读顺序提取的,如果PDF里有多栏布局,它可能会把两栏的文字混在一起。我曾经因为这个,把一份双栏论文解析得乱七八糟。后来我改用page.get_text("blocks"),按块提取,再手动排序,才解决了问题。

我的小技巧: 遇到复杂PDF,先打印出每个文本块的坐标,看看它们的位置关系。然后根据坐标排序,再拼接成正确的阅读顺序。

2. python-docx —— Word文档的瑞士军刀

Word文档相对规整。python-docx可以读取段落、表格、标题样式。我一般这样用:

from docx import Document

doc = Document("report.docx")
for para in doc.paragraphs:
    print(para.text, para.style.name)  # 文本内容和样式名

这里有个细节:Word里的表格,需要用doc.tables来遍历。我建议把表格转成Markdown格式,这样既保留了结构,又方便后续分块。

3. BeautifulSoup —— HTML解析利器

HTML文档,说白了就是一堆标签。BeautifulSoup能帮你快速定位到正文区域,去掉广告、导航栏这些噪音。

from bs4 import BeautifulSoup

with open("page.html", "r", encoding="utf-8") as f:
    soup = BeautifulSoup(f, "html.parser")

# 提取所有段落文本
for p in soup.find_all("p"):
    print(p.get_text())

嗯,这里要注意:有些网页的正文藏在<article><div class="content">里。我习惯先查看网页结构,再写选择器。别偷懒,直接find_all("p")可能会把页脚也抓进来。

文档分块策略:切得对,才能找得准

文档解析完了,得到一大段文本。但你不能直接把整篇文档扔给RAG系统。为什么?因为大模型的上下文窗口有限,而且检索时,粒度太粗会导致命中率低。

我总结了几种分块策略,你可以根据场景选:

策略 适用场景 优点 缺点
固定长度分块 通用场景,快速原型 实现简单,速度最快 可能切断语义完整的段落
段落分块 结构清晰的文档(如论文、报告) 保留语义完整性 段落长度不一,可能过长或过短
语义分块 需要高精度检索的场景 块内语义连贯,检索效果好 实现复杂,需要额外模型
递归分块 长文档,需要多级粒度 灵活,可适应不同查询 存储成本高,管理复杂

我个人最常用的是“段落分块+固定长度兜底”。具体做法是:先按段落切,如果某个段落太长(比如超过500字),再按句子或固定长度切。这样既保证了语义完整,又控制了块的大小。

避坑指南: 我曾经做过一个项目,把一份技术手册按固定512字符切块。结果一个完整的API说明被切成了两半,检索时永远找不到后半部分。后来我改用段落分块,问题就解决了。记住:分块策略一定要根据文档内容来定,不要一刀切。

知识体系总览

下面这张图,是我对本章知识体系的总结。你可以看到,从原始文档到可检索的文本块,中间经历了三个关键步骤:格式解析、文本提取、分块策略。每一步都有对应的工具和注意事项。

文档加载与解析知识体系 原始文档 PDF / Word / HTML 解析工具 PyMuPDF / python-docx / BS4 纯文本 提取 & 清洗 分块策略 固定长度 | 段落分块 | 语义分块 | 递归分块 可检索的文本块 图:文档加载与解析的完整流程

你看,整个流程其实不复杂。但每个环节都有细节要注意。我刚开始做RAG时,总想着一步到位,结果在解析这一步就栽了跟头。后来我养成了一个习惯:每处理一种新文档,先手动检查几页,看看解析效果。嗯,这个习惯帮我避免了很多线上事故。

核心要点总结:

  • PDF用PyMuPDF,注意多栏布局和扫描件
  • Word用python-docx,表格要单独处理
  • HTML用BeautifulSoup,先定位正文区域
  • 分块策略没有银弹,根据文档内容灵活选择
  • 永远先做小规模验证,再批量处理

好了,文档加载与解析就聊到这里。记住一句话:解析的质量,决定了RAG系统的上限。这一步花再多时间都值得。

专注资料整理