大家好,我是工藤学编程 🦉 | 一个正在努力学习的小博主,期待你的关注 |
---|---|
实战代码系列最新文章😉 | C++实现图书管理系统(Qt C++ GUI界面版) |
SpringBoot实战系列🐷 | 【SpringBoot实战系列】SpringBoot3.X 整合 MinIO 存储原生方案 |
分库分表 | 分库分表之实战-sharding-JDBC分库分表执行流程原理剖析 |
消息队列 | 深入浅出 RabbitMQ-RabbitMQ消息确认机制(ACK) |
AI大模型 | 零基础学AI大模型之RAG系统链路解析与Document Loaders多案例实战" |
前情摘要:
1、零基础学AI大模型之读懂AI大模型
2、零基础学AI大模型之从0到1调用大模型API
3、零基础学AI大模型之SpringAI
4、零基础学AI大模型之AI大模型常见概念
5、零基础学AI大模型之大模型私有化部署全指南
6、零基础学AI大模型之AI大模型可视化界面
7、零基础学AI大模型之LangChain
8、零基础学AI大模型之LangChain六大核心模块与大模型IO交互链路
9、零基础学AI大模型之Prompt提示词工程
10、零基础学AI大模型之LangChain-PromptTemplate
11、零基础学AI大模型之ChatModel聊天模型与ChatPromptTemplate实战
12、零基础学AI大模型之LangChain链
13、零基础学AI大模型之Stream流式输出实战
14、零基础学AI大模型之LangChain Output Parser
15、零基础学AI大模型之解析器PydanticOutputParser
16、零基础学AI大模型之大模型的"幻觉"
17、零基础学AI大模型之RAG技术
18、零基础学AI大模型之RAG系统链路解析与Document Loaders多案例实战
本文章目录
- [零基础学AI大模型之LangChain PyPDFLoader实战与PDF图片提取全解析](#零基础学AI大模型之LangChain PyPDFLoader实战与PDF图片提取全解析)
-
- [1. 前言:为什么需要PyPDFLoader?](#1. 前言:为什么需要PyPDFLoader?)
- [2. PyPDFLoader基础:安装与核心能力](#2. PyPDFLoader基础:安装与核心能力)
-
- [2.1 安装依赖库](#2.1 安装依赖库)
- [2.2 核心能力说明](#2.2 核心能力说明)
- [3. PyPDFLoader实战:3类核心场景](#3. PyPDFLoader实战:3类核心场景)
-
- [3.1 场景1:加载整个PDF并查看基础信息](#3.1 场景1:加载整个PDF并查看基础信息)
- [3.2 场景2:按需加载指定页码](#3.2 场景2:按需加载指定页码)
- [3.3 场景3:合并所有页面为单个文本](#3.3 场景3:合并所有页面为单个文本)
- [4. 常见问题与解决方案(避坑指南)](#4. 常见问题与解决方案(避坑指南))
-
- [4.1 问题1:PDF无法加载或内容为空](#4.1 问题1:PDF无法加载或内容为空)
- [4.2 问题2:文本分块不理想(如句子被截断)](#4.2 问题2:文本分块不理想(如句子被截断))
- [5. 进阶:PDF图片提取(含OCR实战)](#5. 进阶:PDF图片提取(含OCR实战))
-
- [5.1 工具介绍:RapidOCR-ONNXRuntime](#5.1 工具介绍:RapidOCR-ONNXRuntime)
- [5.2 实战:提取PDF中的图片文本](#5.2 实战:提取PDF中的图片文本)
- [6. 高级技巧:批量处理文件夹中的所有PDF](#6. 高级技巧:批量处理文件夹中的所有PDF)
- [7. 总结与下一步](#7. 总结与下一步)
-
- [7.1 本文核心收获](#7.1 本文核心收获)
零基础学AI大模型之LangChain PyPDFLoader实战与PDF图片提取全解析
在AI大模型学习系列中,我们已经掌握了LangChain的核心概念、Prompt工程、链(Chain)等基础能力。而在RAG(检索增强生成)系统中,"文档加载(Document Loading)"是数据输入的第一步------只有先把PDF、Word等外部文档准确提取成文本,才能后续构建向量数据库、实现精准检索。
本文作为RAG链路的关键实战篇,将聚焦LangChain中最常用的PDF加载器PyPDFLoader
,从基础使用到图片提取,再到问题排查,带你一站式掌握PDF文档处理能力。

1. 前言:为什么需要PyPDFLoader?
在RAG系统中,PDF是最常见的"外部知识库"格式(如技术文档、论文、报告等)。但PDF的文本存储结构特殊,直接读取会出现"乱码""分页丢失"等问题------LangChain的PyPDFLoader正是为解决这个问题而生。
它的核心价值在于:
- 自动按PDF页码拆分文档,返回
Document
对象列表(每个对象对应1页); - 保留元数据(如页码、文件路径),方便后续检索时定位"文本来源";
- 支持按需加载指定页码,避免大文件加载耗时;
- 可结合OCR工具提取扫描版PDF或图片中的文本,覆盖更多场景。
2. PyPDFLoader基础:安装与核心能力
2.1 安装依赖库
PyPDFLoader
依赖pypdf
库实现PDF解析,需先安装(建议指定版本避免兼容性问题):
bash
# 安装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
2.2 核心能力说明
能力 | 描述 | 适用场景 |
---|---|---|
按页拆分 | 将多页PDF拆分为单个Document 对象,每个对象包含page_content (文本)和metadata (元数据) |
需按页码追溯文本来源的场景(如"引用第5页的内容") |
元数据保留 | 自动记录source (文件路径)、page (页码,从1开始) |
RAG检索时显示"答案来自xxx文件第x页" |
按需加载 | 支持指定页码范围加载,无需加载整个文件 | 大文件(如1000页PDF)仅需提取部分页面 |
3. PyPDFLoader实战:3类核心场景
以下实战均基于"本地PDF文件"(路径示例:data/test.pdf
),建议先创建data
文件夹并放入测试PDF,避免路径错误。
3.1 场景1:加载整个PDF并查看基础信息
目标:加载完整PDF,查看总页数、第一页文本和元数据。
python
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文件的核心加载器,支持按页拆分、元数据保留...
3.2 场景2:按需加载指定页码
目标:加载PDF的"第2-4页"(注意:load()
方法的参数是索引,从0开始,即第2页对应索引1,第4页对应索引3)。
python
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]
,避免混淆!
3.3 场景3:合并所有页面为单个文本
目标:将所有页面的文本合并为一个字符串(适用于无需按页拆分的场景,如"生成PDF全文摘要")。
python
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指南"),可在合并前添加"去重逻辑",例如:
python
# 合并时去除每页开头的重复页眉(示例:页眉为"LangChain指南")
full_text = "\n\n".join([page.page_content.replace("LangChain指南", "") for page in pages])
4. 常见问题与解决方案(避坑指南)
在实际使用中,最常遇到"加载失败"和"分块不理想"两类问题,以下是针对性解决方案。
4.1 问题1:PDF无法加载或内容为空
常见原因与解决步骤
原因 | 现象 | 解决方案 |
---|---|---|
1. 文件是"扫描版PDF"(本质是图片集合) | 加载后page_content 为空字符串,或只有乱码 |
用OCR工具提取图片中的文本(见第5章) |
2. PDF文件加密(需密码解密) | 报错PdfReadError: File has not been decrypted |
1. 用Adobe Acrobat等工具手动解密; 2. 若需代码解密,可先用PyPDF2 库解密后再加载 |
3. 文件路径错误 | 报错FileNotFoundError: [Errno 2] No such file or directory |
1. 检查路径是否存在(用os.path.exists("data/test.pdf") 验证); 2. 优先使用绝对路径避免相对路径混淆 |
4. 文件损坏 | 报错PdfReadError: EOF marker not found |
重新下载或修复PDF文件(用Adobe Acrobat修复) |
加密PDF解密示例(用PyPDF2)
若PDF需密码,可先解密再传给PyPDFLoader:
python
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)}")
4.2 问题2:文本分块不理想(如句子被截断)
PyPDFLoader
仅负责"提取文本",若需将文本拆分为适合大模型输入的"小块"(如500字符/块),需结合RecursiveCharacterTextSplitter
优化分块策略。
解决方案:自定义分块参数
python
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", "。", "!", "?", "."]
。
5. 进阶:PDF图片提取(含OCR实战)
PyPDFLoader
默认仅提取"文本层"的内容,若PDF中包含"图片"(如截图、手写笔记、图表中的文字),需结合OCR工具提取图片中的文本。
这里推荐轻量级OCR工具rapidocr-onnxruntime
,支持中英混合识别,且无需复杂配置。
5.1 工具介绍:RapidOCR-ONNXRuntime
特性 | 说明 | 优势 |
---|---|---|
引擎 | 基于ONNX Runtime(跨平台推理引擎) | 速度快、资源占用低(比Tesseract快3-5倍) |
语言支持 | 中文、英文、日文、韩文等10+语言 | 适合处理中英混合的技术文档 |
模型体积 | 核心模型仅5-10MB | 无需下载大模型,安装即用 |
跨平台 | 支持Windows、Linux、macOS、移动端 | 开发环境无限制 |
与主流OCR工具对比
工具 | 引擎 | 速度 | 准确率 | 依赖项 | 适用场景 |
---|---|---|---|---|---|
RapidOCR-ONNXRuntime | ONNX Runtime | ⭐⭐⭐⭐ | ⭐⭐⭐ | 少(仅需onnxruntime) | 跨平台、轻量级部署、实时提取 |
Tesseract | 自研引擎 | ⭐⭐ | ⭐⭐ | 多(需安装Poppler、语言包) | 开源免费、简单文本识别 |
EasyOCR | PyTorch | ⭐⭐ | ⭐⭐⭐ | 多(需安装PyTorch、CUDA) | 复杂场景(如倾斜文本) |
Microsoft Read API | 云端引擎 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 无(需API密钥) | 企业级、高并发需求 |
5.2 实战:提取PDF中的图片文本
步骤1:安装依赖
bash
# 安装rapidocr-onnxruntime(首次安装会自动下载小模型,耗时约1-2分钟)
pip install rapidocr-onnxruntime -i https://pypi.tuna.tsinghua.edu.cn/simple
步骤2:代码实现(图片文本提取)
python
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
可能会用不同标记包裹图片文本,以实际输出为准)
6. 高级技巧:批量处理文件夹中的所有PDF
若需一次性加载"某个文件夹下的所有PDF"(如docs/
文件夹),可结合os
库遍历文件夹,实现批量加载。
python
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
文件); - 多线程加载:若文件夹中PDF数量多(如100+),可使用
concurrent.futures
多线程加载,提升效率:
python
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]
7. 总结与下一步
7.1 本文核心收获
- 基础能力 :掌握
PyPDFLoader
的安装、单文件加载、按需加载、全文合并; - 问题解决:能排查"加载失败""分块不理想"等常见问题;
- 进阶技能 :结合
RapidOCR-ONNXRuntime
提取PDF图片文本,实现批量PDF处理; - RAG衔接 :提取后的
Document
对象可直接传入文本分割器,为后续"向量数据库构建"做准备。
如果觉得本文有帮助,欢迎点赞+关注,后续会持续更新RAG系统的实战内容!
