这是《LLM 应用开发 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 RAG 的质量上限常常在解析阶段就确定了。PDF、Word、HTML、扫描件和表格解析错误,会在后续切块、向量化、检索和生成中被放大。本章讲解析目标、工具选择和质量评估。
7.1 解析目标
文档解析不是只提取字符,而是尽量还原阅读结构:
标题层级
段落
列表
表格
代码块
图片 / OCR
页眉页脚
脚注
文档元数据
理想输出应保留顺序和结构:
{
"doc_id": "policy-2026-01",
"blocks": [
{"type": "heading", "level": 2, "text": "退款政策"},
{"type": "paragraph", "text": "标准订单可在签收后 7 天内申请退款。"},
{"type": "table", "headers": ["商品", "时限"], "rows": [["食品", "不支持"]]}
]
}
7.2 文档类型挑战
| 类型 | 挑战 | 处理 |
|---|---|---|
| 双栏、表格、扫描、顺序错乱 | 版面分析 + OCR | |
| Word | 批注、样式、嵌套表格 | 结构化解析库 |
| HTML | 导航、广告、正文噪声 | 正文抽取 |
| Markdown | 相对链接、代码块 | 解析 AST |
| Excel | 合并单元格、多 sheet | 表格专用流程 |
| 扫描件 | 图片、倾斜、OCR 错误 | 预处理 + OCR 校验 |
| 邮件 | 引用链、附件 | 分段解析 |
7.3 PDF 解析
Python 示例:
from pypdf import PdfReader
def read_pdf(path: str):
reader = PdfReader(path)
pages = []
for i, page in enumerate(reader.pages, start=1):
text = page.extract_text() or ""
pages.append({"page": i, "text": text.strip()})
return pages
pypdf 适合文本层完整的简单 PDF。遇到双栏论文、复杂表格或扫描件,应使用版面分析或 OCR 工具。
7.4 Word 解析
from docx import Document
def read_docx(path: str):
doc = Document(path)
blocks = []
for p in doc.paragraphs:
if not p.text.strip():
continue
blocks.append({
"type": "paragraph",
"style": p.style.name,
"text": p.text.strip(),
})
for table in doc.tables:
rows = [[cell.text.strip() for cell in row.cells] for row in table.rows]
blocks.append({"type": "table", "rows": rows})
return blocks
注意 Word 的“标题”依赖样式定义,不同模板可能不一致。
7.5 HTML 正文抽取
import requests
from bs4 import BeautifulSoup
def read_html(url: str):
html = requests.get(url, timeout=10).text
soup = BeautifulSoup(html, "html.parser")
for tag in soup(["script", "style", "nav", "footer"]):
tag.decompose()
title = soup.title.get_text(strip=True) if soup.title else ""
text = "\n".join(
p.get_text(" ", strip=True)
for p in soup.find_all(["h1", "h2", "h3", "p", "li"])
if p.get_text(strip=True)
)
return {"title": title, "text": text}
抓取外部网页前要遵守站点协议、版权和频率限制。
7.6 OCR 流程
图片/扫描 PDF
-> 去斜 / 二值化 / 降噪
-> 版面检测
-> OCR
-> 置信度过滤
-> 表格还原
-> 人工抽检
OCR 错误示例:
原文:订单号 A10001
OCR:订単号 A1OOOI
处理策略:
- 保留 OCR 置信度;
- 关键字段用正则或字典校验;
- 合同号、金额、日期进入人工复核;
- 原图坐标保留,便于定位;
- 不同语言选择合适模型;
- 评估字符错误率。
7.7 表格解析
不要把表格直接压成无分隔文本。
推荐序列化:
表标题:商品发货时限
列:商品 | 时限 | 备注
行 1:食品 | 不支持 | 生鲜除外
行 2:数码 | 7天 | 需保留包装
复杂表格处理:
- 识别表头;
- 还原合并单元格;
- 拆分跨页表;
- 保持行列关系;
- 单元格为空时显式标记;
- 将表格和上下文说明放在同一 chunk 或可互相关联。
7.8 元数据设计
| 字段 | 示例 |
|---|---|
| doc_id | policy-2026-01 |
| source | /docs/refund.pdf |
| version | v3 |
| language | zh-CN |
| effective_time | 2026-01-01 |
| owner | 客服团队 |
| acl | cs_agent |
| page | 12 |
| checksum | sha256 |
元数据会影响检索过滤、权限、新鲜度和去重,应尽早设计。
7.9 解析质量评估
| 指标 | 说明 |
|---|---|
| 提取成功率 | 解析未崩溃比例 |
| 字符准确率 | OCR 质量抽样 |
| 结构准确率 | 标题表格是否正确 |
| 乱码率 | 不可读字符 |
| 空文档率 | 提取为空 |
| 平均处理时间 | 吞吐 |
| 人工修正率 | 需人工修复 |
建立样例集:
普通文档 60%
复杂表格 15%
扫描件 10%
多语言 10%
坏文件 5%
7.10 常见故障
| 现象 | 原因 | 处理 |
|---|---|---|
| 双栏 PDF 顺序错 | 未做版面分析 | 按栏切分 |
| 表格变成一行 | 文本提取 | 表格解析器 |
| 中文乱码 | 字体编码 | OCR 或替换解析器 |
| 页眉重复 | 未清理 | 按页过滤 |
| 标题丢失 | 样式异常 | 字号和版面启发 |
| 扫描件为空 | 无文本层 | OCR |
| 旧版本被检索 | 元数据缺失 | 版本与生效时间 |
本章小结
文档解析决定 RAG 的数据质量。要根据格式选择解析策略,保留标题、表格、页码、权限和版本等元数据,对 OCR 和复杂版面建立抽样评估。解析层做好后,切块和检索的问题会明显减少。
思考题
- 为什么 PDF 提取文本不等于完成文档解析?
- 表格序列化时应保留哪些信息?
- 哪些元数据会直接影响检索正确性?
- OCR 低置信度字段应如何处理?
- 如何为一个企业文档系统设计解析抽样集?