news 2026/7/4 2:55:17

文档处理实战:PDF和Word怎么变成高质量知识库

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
文档处理实战:PDF和Word怎么变成高质量知识库

文档处理实战:PDF和Word怎么变成高质量知识库

上篇我们用 PyMuPDF 三行代码解析了 PDF,看起来很简单对不对?

但那是"教科书级"的 PDF——纯文字、单栏、无表格。现实中你收到的 PDF 可能是这样的:扫描件歪七扭八、表格嵌套表格、双栏排版混着图片、甚至第 3 页和第 15 页方向都不一样。

文档处理是 RAG 的第一个大坑,也是最重要的坑。这篇我把踩过的坑全倒出来。

大家好,我是黒漂技术佬。


一、先给你泼盆冷水:真实文档长什么样

企业知识库的文档来源通常就这几个:

来源典型文件质量
产品/技术文档PDF(导出自 Word / Markdown)⭐⭐⭐⭐ 较好
制度/合规文件PDF(扫描件或打印后扫描)⭐⭐ 差
会议纪要/方案Word / 飞书文档⭐⭐⭐ 一般
培训材料PPT 导出 PDF⭐⭐ 差
历史归档各种格式混杂,甚至有 .wps⭐ 噩梦级

处理这些文档时,你会遇到:

  • 扫描件:每一页是一张图,文字是"画上去"的,需要 OCR
  • 表格:解析后变成「姓名部门职位张三技术部工程师」,糊成一坨
  • 双栏布局:解析器按行读,左边半句右边半句混着来
  • 页眉页脚/水印:每页都带"XX公司 · 内部机密 · 2024 V1.0",全被当成正文
  • 嵌入图片:架构图、流程图、截图,直接丢弃或变成乱码占位符

二、PDF 解析:三种武器,各有所长

武器 1:PyMuPDF(fitz)—— 通用首选

importfitz# PyMuPDFdoc=fitz.open("document.pdf")forpageindoc:text=page.get_text()# 提取文本images=page.get_images()# 提取图片引用tables=page.find_tables()# 检测表格(新版支持)

优点:速度快,对现代生成的 PDF 效果好,直接支持表格检测。
缺点:遇到扫描件就废,需要配合 OCR。

武器 2:pdfplumber —— 表格之王

importpdfplumberwithpdfplumber.open("document.pdf")aspdf:forpageinpdf.pages:text=page.extract_text()# 文本提取tables=page.extract_tables()# 表格提取(比PyMuPDF更稳)

pdfplumber 的表格提取远超 PyMuPDF。它通过分析 PDF 的线条和字符位置来推断表格结构,对于有边框线和无线条的表格都能处理。

这两者不是二选一的关系,而是配合使用

defextract_page(page_fitz,page_plumber):"""融合两个解析器的结果"""text=page_fitz.get_text()# 先从 PyMuPDF 拿文本tables=page_plumber.extract_tables()# 再从 pdfplumber 拿表格iftables:fortableintables:# 把表格转成 Markdown 格式,保留结构md_table=convert_table_to_markdown(table)text+="\n\n"+md_tablereturntext

武器 3:Tesseract OCR —— 扫描件的救星

对于扫描件,必须上 OCR(光学字符识别,Optical Character Recognition):

importfitzfromPILimportImageimportpytesseractimportiodefocr_scanned_pdf(file_path):"""处理扫描件 PDF,逐页 OCR"""doc=fitz.open(file_path)all_text=[]forpage_num,pageinenumerate(doc):# 把 PDF 页面渲染成图片(300 DPI 是 OCR 的最佳分辨率)pix=page.get_pixmap(dpi=300)img=Image.open(io.BytesIO(pix.tobytes("png")))# OCR 识别,中英文混合text=pytesseract.image_to_string(img,lang='chi_sim+eng')all_text.append(f"--- 第{page_num+1}页 ---\n{text}")return"\n\n".join(all_text)

关键参数解读

  • dpi=300:渲染分辨率。低于 200 的话小字糊成一片,OCR 准确率断崖式下降
  • lang='chi_sim+eng':中英文混合识别。需要提前安装中文语言包

OCR 不是万能药

别对 OCR 抱太高期望。以下场景 OCR 基本歇菜:

  • 手写体(尤其是医生的字,懂的都懂)
  • 印章遮盖的文字
  • 严重倾斜的页面(需要先做透视校正)
  • 低分辨率的老旧档案扫描件

实战经验:扫描件先整体做预处理——灰度化、二值化、去噪——能提升 OCR 准确率 10%~20%。


三、Word 文档处理:比 PDF 简单,但也有坑

fromdocximportDocumentdefparse_docx(file_path):doc=Document(file_path)content=[]forparaindoc.paragraphs:# 根据段落样式判断层级ifpara.style.name.startswith('Heading'):level=int(para.style.name.split()[-1])# Heading 1 → 1prefix='#'*level+' '# 转成 Markdown 标题content.append(f"{prefix}{para.text}")else:content.append(para.text)# 处理表格fortableindoc.tables:md_table=docx_table_to_markdown(table)content.append(md_table)return"\n\n".join(content)

Word 的主要坑在嵌入对象:Visio 图、Excel 内嵌表、OLE 对象——这些 python-docx 解不出来,需要额外处理。


四、文本分块:最被低估的技术活

分块看似简单——切一刀就行。但实际上,chunk 的大小和策略直接影响检索质量。

分块的黄金法则

chunk 太大(1000+ 字)→ 语义太杂,检索命中率低 chunk 太小(100 字以下)→ 缺少上下文,LLM 看不懂 chunk 刚好(300~500 字)→ 黄金区间

三种分块策略对比

fromlangchain_text_splittersimport(RecursiveCharacterTextSplitter,MarkdownHeaderTextSplitter,SemanticChunker,)# 策略1:递归字符分块(最常用,适合绝大多数场景)splitter_recursive=RecursiveCharacterTextSplitter(chunk_size=500,chunk_overlap=100,separators=["\n\n","\n","。",".","!","?",";",";"," ",""])# 策略2:Markdown 按标题分块(适合结构化文档)headers_to_split_on=[("#","h1"),("##","h2"),("###","h3"),]splitter_md=MarkdownHeaderTextSplitter(headers_to_split_on)# 这样每个 chunk 自动带上 "h1: 第三章", "h2: 3.1 安全要求" 等元数据# 策略3:语义分块(需要额外的 Embedding 开销,适合高质量要求场景)splitter_semantic=SemanticChunker(embeddings=my_embeddings)# 它会用 Embedding 模型计算句子间的语义相似度,# 在语义"跳变"的地方切分

企业级分块的额外要求

只切块不够,每个 chunk 必须带上元数据(Metadata)

defchunk_with_metadata(document,file_name,file_path):chunks=splitter.split_documents([document])fori,chunkinenumerate(chunks):chunk.metadata.update({"source":file_name,# 文件名:员工手册.pdf"file_path":file_path,# 完整路径"chunk_index":i,# 块序号"page_number":chunk.metadata.get("page",1),"doc_type":"policy",# 文档类型:制度/技术/产品"department":"HR",# 归属部门"updated_at":"2024-03-15",# 更新时间})returnchunks

这些元数据在检索时可以用于过滤——比如"只看技术部的文档"、“只看 2024 年更新的内容”。没有元数据,RAG 就是个瞎子。


五、文档清洗流水线:一把梭

把我以上说的串成一个完整的流水线:

classDocumentPipeline:"""企业文档处理流水线"""defprocess(self,file_path:str)->list:ext=file_path.suffix.lower()# Step 1: 解析ifext=='.pdf':raw_text=self._parse_pdf(file_path)elifext=='.docx':raw_text=self._parse_docx(file_path)elifext=='.md':raw_text=file_path.read_text(encoding='utf-8')else:raiseValueError(f"unsupported format:{ext}")# Step 2: 清洗clean_text=self._clean(raw_text)# Step 3: 分块chunks=self._split(clean_text)# Step 4: 注入元数据enriched=self._add_metadata(chunks,file_path)returnenricheddef_clean(self,text:str)->str:"""清洗文本噪音"""importre# 去掉多余空行(连续 3+ 个换行 → 2 个)text=re.sub(r'\n{3,}','\n\n',text)# 去掉页眉页脚常见的页码标记text=re.sub(r'^\d+\s*/\s*\d+$','',text,flags=re.MULTILINE)# 合并被换行打断的句子(中文段落内不应有单换行)# 这个需要根据实际情况调整正则text=re.sub(r'(?<=[\u4e00-\u9fff])\n(?=[\u4e00-\u9fff])','',text)returntext.strip()

那个中文换行合并的正则是关键

很多 PDF 提取出来的文本长这样:

本系统采用微服务架构设计, 将不同业务模块拆分为独立的 服务单元,通过API网关进行 统一调度。

这在中文里是一个完整段落,但提取出来被硬换行了。正则(?<=[\u4e00-\u9fff])\n(?=[\u4e00-\u9fff])的意思是:如果换行符前后都是中文字符,就把这个换行干掉,连成一句话。


六、实操建议(踩坑总结)

  1. 先做文档盘点:把你要入库的所有文档列出来,标注格式、页数、质量。扫一眼就知道坑在哪。
  2. 不要追求 100% 解析完美:表格乱一点、排版歪一点,只要 80% 的信息能提取出来,RAG 就能用。追求 100% 的 ROI 极低。
  3. 扫描件该放弃就放弃:如果一份扫描件占比不到 5%,且 OCR 质量实在太差,宁可手动录入关键内容到 Markdown,也不要在 OCR 参数上调一整天。
  4. chunk_size 不是拍脑袋的:拿你的典型文档做实验,同一组问题,不同 chunk_size 下的检索 mAP 对比。我的数据:中文技术文档,512 字 + 64 字 overlap综合最优。
  5. 元数据就是钱:花 10% 的额外时间给每个 chunk 打上完善的元数据,检索效果能提升 30% 以上。投入产出比极高。

💬 你的文档里有什么奇葩格式?遇到过什么解析难题?评论区发张截图(打码敏感信息),我帮你看看怎么解!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/4 2:54:55

经过统计发现:目前评价系统100%都是首评

我统计了20发表评论的视频&#xff0c;发现&#xff1a;评价的全都是首次评论的视频&#xff0c;一个评论>100的都没有。似乎2个你只能选择一个&#xff1a;要么评论>100 <1200,要么你就只能要首评价&#xff0c;我没得选。我开始实验&#xff1a;选择哪些评价大的看数…

作者头像 李华
网站建设 2026/7/4 2:54:08

2026年湖南优选企业TOP10榜单:哪些行业新星将引领未来?

摘要本文将为您揭晓2026年湖南优选企业TOP10榜单&#xff0c;涵盖科技、制造、农业等多个领域。通过对比分析这些企业的核心优势和适用场景&#xff0c;帮助您了解哪些行业新星将在未来引领市场。总评结论在2026年的湖南优选企业TOP10榜单中&#xff0c;云坤数智凭借其在AI生成…

作者头像 李华
网站建设 2026/7/4 2:54:02

三菱PLC与伺服电机FB功能块控制实战

1. 项目概述在工业自动化流水线项目中&#xff0c;伺服电机的精准控制一直是核心难点。三菱MR-JE-C系列伺服电机配合Q系列PLC的FB功能块方案&#xff0c;为我们提供了一套高效可靠的解决方案。这套组合在包装、装配、检测等多种流水线场景中都有出色表现&#xff0c;特别适合需…

作者头像 李华
网站建设 2026/7/4 2:53:31

C++ 虚继承对象内存布局

C 虚继承对象内存布局 一、先区分&#xff1a;普通继承 vs 虚继承 1. 普通公有继承&#xff08;非虚&#xff09; struct Base { int a; }; struct Derived : Base { int b; };布局&#xff1a;Base部分 Derived自身成员 Derived 对象内存&#xff1a; [ int a ] // Base [ in…

作者头像 李华
网站建设 2026/7/4 2:52:52

2026最新2款AI编程工具基础版免费平替之选权威实测合集

一、开篇&#xff1a;双工具长期实测&#xff0c;直观梳理核心差异这次对比的起因很偶然&#xff1a;5 款 AI 编程工具都在同一周发布了大版本更新&#xff0c;我趁机做了一次同条件下的横评。 我是一名从外包转自研的后端开发者&#xff0c;2026年6月负责代号POINT-MALL12积分…

作者头像 李华