python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python PyPDF2提取PDF文本

Python使用PyPDF2实现快速提取PDF文本

作者:无心水

在日常办公和数据处理中,PDF文件几乎无处不在,本文将带你从零开始,用PyPDF2快速提取PDF文本,涵盖安装配置、核心操作、元数据获取、中文乱码避坑等全流程,希望对大家有所帮助

前言

在日常办公和数据处理中,PDF文件几乎无处不在。合同、报告、论文、发票……每天都有大量PDF文档需要处理。当我们需要从中提取文字信息时,手动复制粘贴不仅效率低下,而且容易出错。

Python的PyPDF2库,正是解决这一痛点的利器。它轻量、纯Python实现、无需安装额外依赖,是初学者入门PDF自动化处理的最佳选择。

本文将带你从零开始,用PyPDF2快速提取PDF文本,涵盖安装配置、核心操作、元数据获取、中文乱码避坑等全流程。全程附代码和图示,确保你读完就能上手写代码。

一、PyPDF2 是什么?适用场景与核心优缺点

1.1 适用场景

PyPDF2是一个纯Python的PDF处理库,能够在不借助任何外部软件的情况下,对PDF文件进行各种操作[reference:0]。它在以下场景中表现出色:

1.2 核心优缺点

维度评价说明
优点轻量易上手纯Python实现,一行pip安装,API简洁直观
优点零外部依赖无需安装Adobe Acrobat或其他PDF软件
优点基础功能全覆盖合并、拆分、提取、加密等一应俱全
缺点中文易乱码对中文字体编码支持较弱,这是最常见的坑
缺点文本提取能力有限对复杂排版、表格、扫描件基本失效
缺点大文件内存占用高将整个PDF加载到内存,几百页可能卡死

特别提醒:PyPDF2对中文PDF提取文本时容易产生乱码。官方原版PyPDF2已停止维护,推荐使用其现代分支 pypdf(API几乎完全兼容)[reference:1]。下文代码同时支持两种写法,你只需将 import PyPDF2 替换为 import pypdf 即可平滑迁移。

1.3 与其他PDF处理库的对比

为了让读者更清楚PyPDF2的定位,下图展示了主流Python PDF库的对比:

一句话选择建议:日常基础操作用PyPDF2/pypdf;提取中文或表格换pdfplumber;追求性能或处理大型PDF用PyMuPDF;扫描件PDF直接上OCR。

二、环境准备:pip安装 + 虚拟环境配置

2.1 基础安装

打开终端/命令提示符,执行以下命令:

pip install PyPDF2

如果你决定使用现代分支pypdf(强烈推荐),执行:

pip install pypdf

验证安装是否成功:

python -c "import PyPDF2; print(PyPDF2.__version__)"
# 输出类似:3.0.0

2.2 虚拟环境配置(团队项目推荐)

# 创建虚拟环境
python -m venv pdf_env
# 激活虚拟环境(Windows)
pdf_env\Scripts\activate
# 激活虚拟环境(Mac/Linux)
source pdf_env/bin/activate
# 在虚拟环境中安装
pip install PyPDF2
# 导出依赖清单
pip freeze > requirements.txt

2.3 安装常见问题

错误信息原因解决方案
No module named 'PyPDF2'未安装库执行 pip install PyPDF2
ImportError: cannot import name 'PdfFileReader'新版API变更改用 from PyPDF2 import PdfReader
pip install 超时网络问题换国内镜像:pip install PyPDF2 -i https://pypi.tuna.tsinghua.edu.cn/simple

三、核心操作:逐页读取PDF文本

3.1 初学者最易犯的错误

很多新手会这样写:

import PyPDF2
with open('example.pdf', 'rb') as file:
    reader = PyPDF2.PdfReader(file)
    print(reader)  # 输出:<PyPDF2._reader.PdfReader object at 0x...>

打印出来的只是一个内存地址,而不是PDF的内容[reference:2]。这是因为PdfReader对象代表的是整个PDF文档的结构,而不是文本本身。

3.2 逐页提取完整代码

import PyPDF2

def extract_text_from_pdf(pdf_path):
    """从PDF文件中提取所有文本并打印"""
    try:
        with open(pdf_path, 'rb') as file:
            reader = PyPDF2.PdfReader(file)
            print(f"文件共 {len(reader.pages)} 页\n")
            
            for page_num, page in enumerate(reader.pages):
                text = page.extract_text()
                print(f"--- 第 {page_num + 1} 页 ---")
                print(text if text else "[该页无文本或提取失败]")
                print()
                
    except FileNotFoundError:
        print(f"错误:文件 '{pdf_path}' 不存在")
    except Exception as e:
        print(f"处理PDF时发生错误:{e}")

# 使用示例
extract_text_from_pdf('example.pdf')

3.3 只提取指定页面

# 只提取第3页(索引从0开始,所以第3页的索引是2)
reader = PyPDF2.PdfReader('example.pdf')
third_page_text = reader.pages[2].extract_text()
print(third_page_text)

3.4 合并多个PDF文件

from PyPDF2 import PdfReader, PdfWriter

def merge_pdfs(pdf_list, output_path):
    writer = PdfWriter()
    
    for pdf_file in pdf_list:
        reader = PdfReader(pdf_file)
        for page in reader.pages:
            writer.add_page(page)
    
    with open(output_path, 'wb') as f:
        writer.write(f)
    
    print(f"合并完成!共 {len(writer.pages)} 页,保存至 {output_path}")

# 使用示例
merge_pdfs(['file1.pdf', 'file2.pdf', 'file3.pdf'], 'merged.pdf')

3.5 拆分PDF文件

from PyPDF2 import PdfReader, PdfWriter

def split_pdf(input_pdf, output_dir):
    reader = PdfReader(input_pdf)
    
    for i, page in enumerate(reader.pages):
        writer = PdfWriter()
        writer.add_page(page)
        
        output_path = f"{output_dir}/page_{i+1}.pdf"
        with open(output_path, 'wb') as f:
            writer.write(f)
    
    print(f"拆分完成!共 {len(reader.pages)} 个文件")

# 只拆分前5页
def split_first_n_pages(input_pdf, n, output_dir):
    reader = PdfReader(input_pdf)
    writer = PdfWriter()
    
    for page in reader.pages[:n]:
        writer.add_page(page)
    
    with open(f"{output_dir}/first_{n}_pages.pdf", 'wb') as f:
        writer.write(f)

四、进阶:获取PDF元信息

PDF文件通常包含元数据(Metadata),如作者、标题、创建工具、加密状态等。PyPDF2可以轻松读取这些信息。

4.1 获取完整元信息

from PyPDF2 import PdfReader

reader = PdfReader('example.pdf')
info = reader.metadata

print("=" * 40)
print("PDF 元信息")
print("=" * 40)
print(f"标题: {info.title}")
print(f"作者: {info.author}")
print(f"主题: {info.subject}")
print(f"创建者: info.creator}")
print(f"生产者: info.producer}")
print(f"页数: {len(reader.pages)}")
print(f"是否加密: {reader.is_encrypted}")
print("=" * 40)

4.2 处理加密PDF

from PyPDF2 import PdfReader

reader = PdfReader('encrypted.pdf')

if reader.is_encrypted:
    try:
        # 尝试解密
        reader.decrypt('your_password')
        print("解密成功!")
        
        # 现在可以正常提取文本
        text = reader.pages[0].extract_text()
        print(text)
    except Exception as e:
        print(f"解密失败:{e}")
else:
    print("文件未加密,直接读取")

重要提示:PyPDF2对加密PDF的支持有限,仅支持旧式加密算法(RC4),不支持AES-256等现代加密方式[reference:3]。遇到 NotImplementedError: only algorithm code 1 and 2 are supported 错误时,说明文件使用了不支持的加密算法,建议换用pikepdf处理[reference:4]。

五、避坑指南:中文乱码的根源与解决方案

这是初学者在使用PyPDF2时最常踩的坑,值得花一整节来讲清楚。

5.1 乱码现象长什么样?

# 原本应该是:"这是一份中文PDF文档"
# 实际输出:
"ä¸è¿½ä»½ä¸æPDFææ£"
# 或者输出:
"?????????"

5.2 中文乱码的根源(重点理解)

乱码的根本原因并不复杂。下图清晰解释了为什么PyPDF2会提取出乱码:

简单来说:PDF中的中文字符存储的是一串“编号”,需要借助“编码映射表”才能翻译成正确的中文。PyPDF2恰恰缺少这个翻译能力,遇到非标准编码的中文字体就直接“放弃治疗”,输出占位符[reference:5]。

具体来说,乱码的根源有以下几种:

5.3 如何判断PDF是否“可提取”?

在执行代码之前,建议先手动测试一下PDF的文字是否可选:

from PyPDF2 import PdfReader

def check_pdf_extractability(pdf_path):
    reader = PdfReader(pdf_path)
    
    print(f"文件名: {pdf_path}")
    print(f"页数: {len(reader.pages)}")
    print(f"是否加密: {reader.is_encrypted}")
    
    # 尝试提取第一页的前200个字符
    sample = reader.pages[0].extract_text()[:200]
    print(f"第一页预览: {sample}")
    
    # 检查生产者信息
    producer = reader.metadata.get('/Producer', '未知')
    print(f"生产者: {producer}")
    
    # 初步判断
    if any(ord(c) > 127 for c in sample if c):
        print(">>> 可能包含非ASCII字符(中文),提取结果可能存在乱码风险")
    else:
        print(">>> 仅包含ASCII字符,提取相对可靠")

check_pdf_extractability('test.pdf')

5.4 中文乱码的临时解决方案

虽然PyPDF2无法完美解决中文乱码,但在某些场景下可以通过以下方式缓解:

方案1:使用pdfplumber替代(推荐)

# pip install pdfplumber
import pdfplumber

with pdfplumber.open('chinese_doc.pdf') as pdf:
    for page in pdf.pages:
        text = page.extract_text()
        print(text)

pdfplumber基于PDFMiner,对中文字体支持更稳定,API也非常相似[reference:7]。

方案2:使用pypdf(现代分支)加容错参数

from pypdf import PdfReader

# 添加strict=False忽略部分解析警告
reader = PdfReader('chinese_doc.pdf', strict=False)
text = reader.pages[0].extract_text()

方案3:处理扫描件PDF(图片型PDF)

# 图片型PDF需要先转换为图片,再用OCR识别
# pip install pdf2image pytesseract pillow
from pdf2image import convert_from_path
import pytesseract

images = convert_from_path('scanned.pdf')
text = pytesseract.image_to_string(images[0], lang='chi_sim')
print(text)

方案4:编码后处理

# 尝试手动修复编码(仅适用于特定场景)
def fix_encoding(broken_text):
    # 尝试不同的编码方式
    for encoding in ['gbk', 'gb2312', 'big5', 'utf-8']:
        try:
            return broken_text.encode('latin1').decode(encoding)
        except:
            continue
    return broken_text

text = page.extract_text()
fixed_text = fix_encoding(text)

5.5 避坑总结

问题类型表现解决方案
文字型PDF输出乱码(如䏿‡)换用pdfplumber
扫描件PDF提取为空或全是乱码走OCR路线
加密PDF报NotImplementedError换pikepdf解密
大文件内存暴涨/卡死分批处理或换pypdf

六、完整可运行代码:批量处理文件夹内所有PDF

以下是一个生产环境可直接使用的脚本,一键提取文件夹内所有PDF的文本内容,并保存为独立的TXT文件。

import os
import sys
from pathlib import Path
from PyPDF2 import PdfReader

def extract_text_from_pdf(pdf_path):
    """
    从单个PDF文件提取文本
    返回: (是否成功, 文本内容)
    """
    try:
        reader = PdfReader(pdf_path)
        full_text = []
        
        for page_num, page in enumerate(reader.pages, 1):
            text = page.extract_text()
            if text:
                full_text.append(f"[第{page_num}页]\n{text}\n")
            else:
                full_text.append(f"[第{page_num}页]\n[无文本或提取失败]\n")
        
        return True, "\n".join(full_text)
    
    except Exception as e:
        return False, f"处理失败:{str(e)}"

def batch_extract_pdfs(input_folder, output_folder, extensions=None):
    """
    批量提取文件夹内所有PDF的文本
    
    参数:
        input_folder: 存放PDF文件的文件夹路径
        output_folder: 输出TXT文件的文件夹路径
        extensions: 文件扩展名列表,默认为['.pdf']
    """
    if extensions is None:
        extensions = ['.pdf', '.PDF']
    
    # 创建输出文件夹
    Path(output_folder).mkdir(parents=True, exist_ok=True)
    
    # 收集所有PDF文件
    pdf_files = []
    for ext in extensions:
        pdf_files.extend(Path(input_folder).glob(f"*{ext}"))
    
    if not pdf_files:
        print(f"在文件夹 '{input_folder}' 中未找到PDF文件")
        return
    
    print(f"共找到 {len(pdf_files)} 个PDF文件,开始处理...\n")
    
    success_count = 0
    fail_count = 0
    
    for pdf_file in pdf_files:
        print(f"正在处理: {pdf_file.name} ...")
        
        success, content = extract_text_from_pdf(str(pdf_file))
        
        output_path = Path(output_folder) / f"{pdf_file.stem}.txt"
        
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(content)
        
        if success:
            success_count += 1
            print(f"  ✓ 成功,已保存至: {output_path.name}")
        else:
            fail_count += 1
            print(f"  ✗ 失败: {content[:100]}")
    
    print(f"\n{'='*50}")
    print(f"处理完成!成功: {success_count},失败: {fail_count}")
    print(f"输出目录: {output_folder}")
    print(f"{'='*50}")

def extract_with_progress(input_folder, output_folder, show_preview=True):
    """
    带进度预览的批量提取功能
    """
    pdf_files = list(Path(input_folder).glob("*.pdf")) + list(Path(input_folder).glob("*.PDF"))
    total = len(pdf_files)
    
    for idx, pdf_file in enumerate(pdf_files, 1):
        print(f"[{idx}/{total}] 处理: {pdf_file.name}")
        
        success, content = extract_text_from_pdf(str(pdf_file))
        
        if show_preview and success:
            preview = content[:200].replace('\n', ' ')
            print(f"  预览: {preview}...")
        
        output_path = Path(output_folder) / f"{pdf_file.stem}.txt"
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(content)
        
        print(f"  ✓ 已保存\n")

if __name__ == "__main__":
    # 方式1:直接运行
    INPUT_DIR = "./pdfs"      # 存放PDF的文件夹
    OUTPUT_DIR = "./output"   # 输出TXT的文件夹
    
    # 方式2:命令行传参
    if len(sys.argv) >= 3:
        INPUT_DIR = sys.argv[1]
        OUTPUT_DIR = sys.argv[2]
    
    batch_extract_pdfs(INPUT_DIR, OUTPUT_DIR)
    
    # 如果需要更详细的处理进度,使用以下函数
    # extract_with_progress(INPUT_DIR, OUTPUT_DIR, show_preview=True)

七、总结与延伸建议

7.1 本文核心要点回顾

知识点关键内容
安装配置pip install PyPDF2,推荐同时安装pdfplumber作为备选
文本提取三步走:PdfReaderpages.extract_text()
合并/拆分使用PdfWriter添加/写入页面
元数据reader.metadata获取标题、作者等信息
中文乱码根源在于字体编码映射缺失,推荐pdfplumber替代

7.2 PyPDF2 vs pypdf 版本选择建议

版本维护状态推荐度适用场景
PyPDF2 1.x~2.x已停止不推荐
PyPDF2 3.x最终版⭐⭐短期使用
pypdf活跃更新⭐⭐⭐⭐⭐强烈推荐

只需将 import PyPDF2 改为 import pypdffrom PyPDF2 import xxx 改为 from pypdf import xxx,代码几乎无需其他改动即可平滑迁移。

7.3 进阶学习方向

一点提醒:任何工具都有其边界。PyPDF2最适合处理西文、无加密、文本型PDF的轻量级操作。遇到中文乱码、扫描件、大文件等场景,及时切换工具往往比“硬扛”更高效。

到此这篇关于Python使用PyPDF2实现快速提取PDF文本的文章就介绍到这了,更多相关Python PyPDF2提取PDF文本内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文