3. 文档解析与预处理:PDF/HTML/Word解析技术、OCR在金融文档中的应用、文档分块策略

好,咱们进入第三章。这一章聊的是RAG系统的“入口”——文档解析与预处理。

说实话,我在金融行业干了这么多年,最头疼的从来不是模型选型,而是数据进来那一步。你想想看,一个PDF文件,里面可能既有扫描件,又有表格,还有手写批注。模型再强,喂进去的是垃圾,吐出来的只能是更精致的垃圾。

所以这一章,我带你把这些“脏活累活”捋清楚。

3.1 PDF解析:金融文档的硬骨头

金融圈里,PDF是绝对的主角。招股书、年报、研报、合同……全是PDF。但PDF这玩意儿,说白了就是个“打印格式”,不是“数据结构”。

我常用的解析工具有这么几个:

  • PyMuPDF(fitz):轻量级,解析速度快。适合文本型PDF。我在处理招股书时经常用它,因为招股书大多是文字排版,没有太多复杂图片。
  • pdfplumber:对表格支持好。如果你要解析财报里的资产负债表,这个很顺手。
  • pdfminer.six:老牌工具,能精确提取文字位置信息。适合需要保留排版结构的场景。

举个例子,用PyMuPDF提取文本:

import fitz

doc = fitz.open("annual_report.pdf")
for page_num, page in enumerate(doc):
    text = page.get_text()
    print(f"--- Page {page_num + 1} ---")
    print(text)

嗯,这里要注意。很多金融PDF是“伪PDF”——文字其实是图片。这时候上面的代码就抓瞎了。怎么办?往下看。

3.2 OCR在金融文档中的应用

OCR,光学字符识别。说白了就是把图片里的文字“认”出来。

我在项目中遇到过最典型的场景:某券商发来的历史交易确认单,全是扫描件。文字模糊、印章重叠、表格线断断续续。当时我试了好几种方案。

目前金融场景下,我推荐两个方向:

  • Tesseract OCR:开源免费,适合清晰文档。配合中文语言包(chi_sim)效果还行。但遇到复杂版面,准确率会掉。
  • PaddleOCR:百度出品,对中文支持极好。我实测过,在金融票据、合同扫描件上,准确率比Tesseract高出一截。而且它自带版面分析,能区分标题、正文、表格。

用PaddleOCR做个简单示例:

from paddleocr import PaddleOCR

ocr = PaddleOCR(use_angle_cls=True, lang='ch')
result = ocr.ocr('scanned_contract.jpg', cls=True)

for line in result[0]:
    text = line[1][0]
    confidence = line[1][1]
    print(f"{text} (置信度: {confidence:.2f})")
避坑指南:我曾经在解析一份带有水印的PDF时,OCR把“机密”两个字识别成了正文内容,导致后续检索时频繁命中无关信息。后来我加了预处理步骤——先用OpenCV做二值化和去噪,再送OCR。效果好了很多。

3.3 HTML与Word解析

除了PDF,金融文档还有不少来自网页(比如财经新闻、监管公告)和Word文件(比如内部备忘录、尽调报告)。

HTML解析,我习惯用BeautifulSoup。但要注意,金融网站的结构经常变。我建议不要硬编码选择器,而是用更鲁棒的方式——比如按标题层级提取正文。

from bs4 import BeautifulSoup
import requests

url = "https://example.com/financial-news"
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')

# 提取所有段落文本
paragraphs = soup.find_all('p')
content = '\n'.join([p.get_text() for p in paragraphs])

Word解析,python-docx库是首选。它能处理.docx格式。但金融圈里还有大量老旧的.doc文件,这时候我一般先用LibreOffice转成.docx,再解析。

from docx import Document

doc = Document("internal_report.docx")
full_text = []
for para in doc.paragraphs:
    full_text.append(para.text)
print('\n'.join(full_text))

3.4 文档分块策略:RAG系统的关键一步

文档解析完了,接下来就是分块。这一步做不好,后面检索就是空中楼阁。

你想想看,如果一块太大,比如把整份年报塞进去,检索时模型根本找不到具体位置。如果一块太小,比如按句子切,上下文又断了。

我总结了几种分块策略,按场景选:

策略 适用场景 优点 缺点
固定长度分块 通用文档 实现简单,速度快 可能切断语义
语义分块 长文本、研报 保持段落完整性 依赖NLP模型,稍慢
递归分块 结构化文档 按标题层级切分 需要文档有清晰结构
滑动窗口分块 问答场景 上下文有重叠,信息不丢失 存储冗余大

我个人习惯用递归分块。比如一份招股书,先按章节切,再按段落切,最后按句子切。这样既能保留结构,又能控制块大小。

用LangChain的RecursiveCharacterTextSplitter实现:

from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,
    chunk_overlap=50,
    separators=["\n\n", "\n", "。", "!", "?", ",", " ", ""]
)

chunks = text_splitter.split_text(full_text)
print(f"共切分成 {len(chunks)} 个块")
我的经验:chunk_size设多少合适?我一般从256开始试,然后看检索效果。金融文档里,512到1024之间比较常见。太小了信息密度不够,太大了又容易跑偏。

3.5 本章知识体系

下面这张图,是我梳理的文档解析与预处理的核心流程。你可以把它当作一个检查清单:

文档解析与预处理核心流程 原始文档 文档类型判断 PDF解析 PyMuPDF / pdfplumber HTML解析 BeautifulSoup Word解析 python-docx 是否需要OCR? 扫描件 / 图片型PDF OCR识别 直接提取文本 文档分块 → 向量化

核心要点:文档解析不是一锤子买卖。我建议你在项目初期就建立一个“文档质量检查”环节——解析完成后,随机抽几份文档,人工核对提取结果。这个习惯帮我避免过很多次“数据污染”事故。

好了,这一章的内容就到这里。解析和预处理是RAG系统的地基,地基不稳,上层建筑再漂亮也没用。下一章我们会聊向量化与嵌入模型,到时候见。

专注资料整理