LangChain PyPDFLoader 实战与 PDF 图片提取解析
LangChain 中 PyPDFLoader 的使用,涵盖安装、基础加载(按页、指定页码、全文合并)、常见问题排查(加密、乱码、分块)及进阶功能(OCR 图片文本提取、批量处理)。通过实战代码展示如何结合 RapidOCR 提取扫描版 PDF 内容,并优化文本分块策略以适应大模型输入,为构建向量数据库提供数据支持。

LangChain 中 PyPDFLoader 的使用,涵盖安装、基础加载(按页、指定页码、全文合并)、常见问题排查(加密、乱码、分块)及进阶功能(OCR 图片文本提取、批量处理)。通过实战代码展示如何结合 RapidOCR 提取扫描版 PDF 内容,并优化文本分块策略以适应大模型输入,为构建向量数据库提供数据支持。

在 AI 大模型学习系列中,我们已经掌握了 LangChain 的核心概念、Prompt 工程、链(Chain)等基础能力。而在 RAG(检索增强生成)系统中,'文档加载(Document Loading)'是数据输入的第一步——只有先把 PDF、Word 等外部文档准确提取成文本,才能后续构建向量数据库、实现精准检索。
本文作为 RAG 链路的关键实战篇,将聚焦 LangChain 中最常用的 PDF 加载器 PyPDFLoader,从基础使用到图片提取,再到问题排查,带你一站式掌握 PDF 文档处理能力。
在 RAG 系统中,PDF 是最常见的'外部知识库'格式(如技术文档、论文、报告等)。但 PDF 的文本存储结构特殊,直接读取会出现'乱码''分页丢失'等问题——LangChain 的 PyPDFLoader 正是为解决这个问题而生。
它的核心价值在于:
Document 对象列表(每个对象对应 1 页);PyPDFLoader 依赖 pypdf 库实现 PDF 解析,需先安装(建议指定版本避免兼容性问题):
# 安装 pypdf(推荐 3.0.0+ 版本)
pip install pypdf>=3.0.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
# 安装 LangChain 社区版(包含 PyPDFLoader)
pip install langchain-community -i https://pypi.tuna.tsinghua.edu.cn/simple
| 能力 | 描述 | 适用场景 |
|---|---|---|
| 按页拆分 | 将多页 PDF 拆分为单个 Document 对象,每个对象包含 page_content(文本)和 metadata(元数据) | 需按页码追溯文本来源的场景(如'引用第 5 页的内容') |
| 元数据保留 | 自动记录 source(文件路径)、page(页码,从 1 开始) | RAG 检索时显示'答案来自 xxx 文件第 x 页' |
| 按需加载 | 支持指定页码范围加载,无需加载整个文件 | 大文件(如 1000 页 PDF)仅需提取部分页面 |
以下实战均基于'本地 PDF 文件'(路径示例:data/test.pdf),建议先创建 data 文件夹并放入测试 PDF,避免路径错误。
目标:加载完整 PDF,查看总页数、第一页文本和元数据。
from langchain_community.document_loaders import PyPDFLoader
# 1. 初始化加载器(传入 PDF 文件路径,支持相对路径/绝对路径)
# 相对路径:相对于当前代码文件的路径(如 data/test.pdf)
# 绝对路径:如 "C:/docs/test.pdf"(Windows)或 "/home/user/docs/test.pdf"(Linux)
loader = PyPDFLoader("data/test.pdf")
# 2. 加载所有页面(返回 Document 对象列表)
pages = loader.load()
# 3. 查看基础信息
print(f"PDF 总页数:{len(pages)}") # 输出总页数
print(f"\n第一页元数据:{pages[0].metadata}") # 元数据(source、page 等)
print(f"\n第一页前 200 字符预览:\n{pages[0].page_content[:200]}...") # 文本预览
输出示例:
PDF 总页数:10
第一页元数据:{'source': 'data/test.pdf', 'page': 1}
第一页前 200 字符预览:
LangChain PyPDFLoader 实战指南
1. 概述
PyPDFLoader 是 LangChain 社区版中用于解析 PDF 文件的核心加载器,支持按页拆分、元数据保留...
目标:加载 PDF 的'第 2-4 页'(注意:load() 方法的参数是索引,从 0 开始,即第 2 页对应索引 1,第 4 页对应索引 3)。
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("data/test.pdf")
# 加载第 2-4 页(索引 1、2、3)
target_pages = loader.load([1, 2, 3])
# 验证结果
for idx, page in enumerate(target_pages):
print(f"第{idx+2}页元数据:{page.metadata}") # 页码应为 2、3、4
print(f"第{idx+2}页前 100 字符:{page.page_content[:100]}...\n")
关键注意点:
loader.load() 的参数是'索引列表',而非'页码列表'——比如要加载第 5-7 页,需传入 [4, 5, 6],避免混淆!
目标:将所有页面的文本合并为一个字符串(适用于无需按页拆分的场景,如'生成 PDF 全文摘要')。
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("data/test.pdf")
pages = loader.load()
# 合并所有页面文本(用两个换行符分隔页面,避免文本粘连)
full_text = "\n\n".join([page.page_content for page in pages])
# 查看合并结果
print(f"合并后全文总字符数:{len(full_text)}")
print(f"\n全文前 500 字符预览:\n{full_text[:500]}...")
实用技巧:
如果 PDF 页面间有重复内容(如页眉'LangChain 指南'),可在合并前添加'去重逻辑',例如:
# 合并时去除每页开头的重复页眉(示例:页眉为"LangChain 指南")
full_text = "\n\n".join([page.page_content.replace("LangChain 指南", "") for page in pages])
在实际使用中,最常遇到'加载失败'和'分块不理想'两类问题,以下是针对性解决方案。
| 原因 | 现象 | 解决方案 |
|---|---|---|
| 1. 文件是'扫描版 PDF'(本质是图片集合) | 加载后 page_content 为空字符串,或只有乱码 | 用 OCR 工具提取图片中的文本(见第 5 章) |
| 2. PDF 文件加密(需密码解密) | 报错 PdfReadError: File has not been decrypted | 1. 用 Adobe Acrobat 等工具手动解密; |
PyPDF2 库解密后再加载 |
| 3. 文件路径错误 | 报错 FileNotFoundError: [Errno 2] No such file or directory | 1. 检查路径是否存在(用 os.path.exists("data/test.pdf") 验证);PdfReadError: EOF marker not found | 重新下载或修复 PDF 文件(用 Adobe Acrobat 修复) |若 PDF 需密码,可先解密再传给 PyPDFLoader:
from PyPDF2 import PdfReader, PdfWriter
from langchain_community.document_loaders import PyPDFLoader
import os
# 1. 解密 PDF 并保存为临时文件
def decrypt_pdf(input_path, output_path, password):
reader = PdfReader(input_path)
if reader.is_encrypted:
reader.decrypt(password) # 传入 PDF 密码
# 保存解密后的文件
writer = PdfWriter()
for page in reader.pages:
writer.add_page(page)
with open(output_path, "wb") as f:
writer.write(f)
print(f"解密后的 PDF 已保存至:{output_path}")
# 2. 解密并加载
decrypt_pdf(
input_path="data/encrypted_test.pdf", # 加密 PDF 路径
output_path="data/decrypted_test.pdf", # 解密后保存路径
password="123456" # PDF 密码
)
# 3. 用 PyPDFLoader 加载解密后的文件
loader = PyPDFLoader("data/decrypted_test.pdf")
pages = loader.load()
print(f"解密后 PDF 总页数:{len(pages)}")
PyPDFLoader 仅负责'提取文本',若需将文本拆分为适合大模型输入的'小块'(如 500 字符/块),需结合 RecursiveCharacterTextSplitter 优化分块策略。
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 1. 加载 PDF
loader = PyPDFLoader("data/test.pdf")
pages = loader.load()
# 2. 初始化文本分割器(核心参数说明)
text_splitter = RecursiveCharacterTextSplitter(
separators=["\n\n", "\n", "."], # 优先按段落(\n\n)、再按换行(\n)、最后按句号(.)分割
chunk_size=500, # 每个块的最大字符数(根据大模型上下文窗口调整,如 GPT-3.5 用 500-1000)
chunk_overlap=50, # 块之间的重叠字符数(避免上下文丢失,如前块结尾 50 字符与后块开头重叠)
length_function=len # 字符数计算方式(默认 len,即按字符数)
)
# 3. 执行分块
split_docs = text_splitter.split_documents(pages)
# 4. 查看分块结果
print(f"原始页面数:{len(pages)}")
print(f"分块后总块数:{len(split_docs)}")
print(f"\n第一个块内容:\n{split_docs[0].page_content}")
print(f"\n第一个块元数据(含页码):{split_docs[0].metadata}")
参数调整建议:
chunk_size=1000,chunk_overlap=100;chunk_size=300,chunk_overlap=30;separators 中添加'。''!''?',如 separators=["\n\n", "\n", "。", "!", "?", "."]。PyPDFLoader 默认仅提取'文本层'的内容,若 PDF 中包含'图片'(如截图、手写笔记、图表中的文字),需结合OCR 工具提取图片中的文本。
这里推荐轻量级 OCR 工具 rapidocr-onnxruntime,支持中英混合识别,且无需复杂配置。
| 特性 | 说明 | 优势 |
|---|---|---|
| 引擎 | 基于 ONNX Runtime(跨平台推理引擎) | 速度快、资源占用低(比 Tesseract 快 3-5 倍) |
| 语言支持 | 中文、英文、日文、韩文等 10+ 语言 | 适合处理中英混合的技术文档 |
| 模型体积 | 核心模型仅 5-10MB | 无需下载大模型,安装即用 |
| 跨平台 | 支持 Windows、Linux、macOS、移动端 | 开发环境无限制 |
| 工具 | 引擎 | 速度 | 准确率 | 依赖项 | 适用场景 |
|---|---|---|---|---|---|
| RapidOCR-ONNXRuntime | ONNX Runtime | ⭐⭐⭐⭐ | ⭐⭐⭐ | 少(仅需 onnxruntime) | 跨平台、轻量级部署、实时提取 |
| Tesseract | 自研引擎 | ⭐⭐ | ⭐⭐ | 多(需安装 Poppler、语言包) | 开源免费、简单文本识别 |
| EasyOCR | PyTorch | ⭐⭐ | ⭐⭐⭐ | 多(需安装 PyTorch、CUDA) | 复杂场景(如倾斜文本) |
| Microsoft Read API | 云端引擎 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 无(需 API 密钥) | 企业级、高并发需求 |
# 安装 rapidocr-onnxruntime(首次安装会自动下载小模型,耗时约 1-2 分钟)
pip install rapidocr-onnxruntime -i https://pypi.tuna.tsinghua.edu.cn/simple
from langchain_community.document_loaders import PyPDFLoader
# 关键:初始化时设置 extract_images=True,启用图片提取
loader = PyPDFLoader("data/pdf-img.pdf", extract_images=True)
# 加载页面(图片中的文本会自动嵌入到 page_content 中)
pages = loader.load()
# 查看结果(图片中的文本会跟在该页原有文本后)
print(f"总页数:{len(pages)}")
print(f"\n包含图片的页面内容(前 500 字符):\n{pages[0].page_content[:500]}...")
效果说明:
若 PDF 第 1 页包含一张'写有'LangChain RAG'的图片',则 pages[0].page_content 会包含:
【原有文本】... [图片文本:LangChain RAG] ...
(不同版本的 PyPDFLoader 可能会用不同标记包裹图片文本,以实际输出为准)
若需一次性加载'某个文件夹下的所有 PDF'(如 docs/ 文件夹),可结合 os 库遍历文件夹,实现批量加载。
from langchain_community.document_loaders import PyPDFLoader
import os
# 目标文件夹路径
pdf_folder = "docs/"
# 存储所有 PDF 的页面
all_pages = []
# 遍历文件夹中的所有文件
for filename in os.listdir(pdf_folder):
# 仅处理后缀为 .pdf 的文件
if filename.lower().endswith(".pdf"):
# 拼接完整文件路径
pdf_path = os.path.join(pdf_folder, filename)
try:
# 加载当前 PDF
loader = PyPDFLoader(pdf_path)
pages = loader.load()
all_pages.extend(pages)
print(f"成功加载:{filename}({len(pages)}页)")
except Exception as e:
# 捕获异常,避免单个文件错误导致整个批量任务失败
print(f"加载{filename}失败:{str(e)}")
# 查看批量加载结果
print(f"\n批量加载完成:共加载{len(all_pages)}页 PDF")
# 合并所有文本(可选)
full_text = "\n\n".join([page.page_content for page in all_pages])
print(f"所有 PDF 合并后总字符数:{len(full_text)}")
实用优化:
if filename.startswith('.'): continue(避免 macOS 下的 .DS_Store 文件);concurrent.futures 多线程加载,提升效率:from concurrent.futures import ThreadPoolExecutor
def load_single_pdf(pdf_path):
"""单个 PDF 加载函数(供多线程调用)"""
try:
loader = PyPDFLoader(pdf_path)
pages = loader.load()
print(f"成功加载:{os.path.basename(pdf_path)}({len(pages)}页)")
return pages
except Exception as e:
print(f"加载{os.path.basename(pdf_path)}失败:{str(e)}")
return []
# 多线程批量加载(设置最大线程数为 4,避免资源占用过高)
with ThreadPoolExecutor(max_workers=4) as executor:
# 获取所有 PDF 路径
pdf_paths = [os.path.join(pdf_folder, f) for f in os.listdir(pdf_folder) if f.lower().endswith(".pdf")]
# 批量执行
results = executor.map(load_single_pdf, pdf_paths)
# 合并结果
all_pages = [page for result in results for page in result]
PyPDFLoader 的安装、单文件加载、按需加载、全文合并;RapidOCR-ONNXRuntime 提取 PDF 图片文本,实现批量 PDF 处理;Document 对象可直接传入文本分割器,为后续'向量数据库构建'做准备。
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online