📕 PDF 文档处理 - 完整指南
本文档是 PDF 文档处理技能的完整技术参考,包含所有工作流程、代码示例和最佳实践。
📋 概述
PDF 文档处理技能提供全面的 PDF 操作解决方案,支持:
- 文本和表格提取 - 从 PDF 中提取结构化数据
- PDF 创建 - 程序化生成专业文档
- 文档操作 - 合并、拆分、旋转、加密
- 表单处理 - 填写可填充表单和添加注释
- OCR 识别 - 处理扫描的 PDF 文档
🔧 核心工具库
Python 库
1. pypdf - 基本 PDF 操作
用途: 读取、合并、拆分、旋转、加密 PDF
安装: pip install pypdf
基本用法:
from pypdf import PdfReader, PdfWriter
# 读取 PDF
reader = PdfReader("document.pdf")
print(f"总页数: {len(reader.pages)}")
# 提取文本
text = ""
for page in reader.pages:
text += page.extract_text()
2. pdfplumber - 高级数据提取
用途: 精确提取文本和表格,保留布局
安装: pip install pdfplumber
特点:
- 保留文本布局和位置信息
- 精确的表格检测和提取
- 支持提取图像位置信息
3. reportlab - PDF 创建
用途: 程序化生成 PDF 文档
安装: pip install reportlab
特点:
- 支持复杂布局和样式
- 可添加图表、表格、图片
- 多页文档生成
4. pytesseract + pdf2image - OCR
用途: 识别扫描的 PDF 文档
安装:
pip install pytesseract pdf2image
# 还需要安装 Tesseract OCR 引擎
📊 工作流程详解
工作流 1: 数据提取
适用场景: 从 PDF 报告、发票、表格中提取结构化数据
步骤 1: 读取 PDF
import pdfplumber
with pdfplumber.open("report.pdf") as pdf:
print(f"文档有 {len(pdf.pages)} 页")
步骤 2: 提取文本(保留布局)
with pdfplumber.open("document.pdf") as pdf:
for i, page in enumerate(pdf.pages):
text = page.extract_text()
print(f"=== 第 {i+1} 页 ===")
print(text)
步骤 3: 提取表格
import pandas as pd
with pdfplumber.open("invoice.pdf") as pdf:
all_tables = []
for page in pdf.pages:
tables = page.extract_tables()
for table in tables:
if table: # 确保表格不为空
# 假设第一行是表头
df = pd.DataFrame(table[1:], columns=table[0])
all_tables.append(df)
# 合并所有表格
if all_tables:
result = pd.concat(all_tables, ignore_index=True)
result.to_excel("extracted_data.xlsx", index=False)
步骤 4: 高级表格提取(处理跨页表格)
with pdfplumber.open("multi_page_table.pdf") as pdf:
tables_settings = {
"vertical_strategy": "lines",
"horizontal_strategy": "lines",
}
all_rows = []
for page in pdf.pages:
table = page.extract_table(table_settings=tables_settings)
if table:
all_rows.extend(table)
df = pd.DataFrame(all_rows[1:], columns=all_rows[0])
df.to_csv("output.csv", index=False)
工作流 2: PDF 文档操作
2.1 合并 PDF
基础合并:
from pypdf import PdfWriter
writer = PdfWriter()
# 方法 1: 使用 append
for pdf_file in ["doc1.pdf", "doc2.pdf", "doc3.pdf"]:
writer.append(pdf_file)
writer.write("merged.pdf")
高级合并(选择特定页面):
from pypdf import PdfReader, PdfWriter
writer = PdfWriter()
# 从 doc1.pdf 添加第 1-3 页
reader1 = PdfReader("doc1.pdf")
for page_num in range(0, 3): # 页面索引从 0 开始
writer.add_page(reader1.pages[page_num])
# 从 doc2.pdf 添加第 5 页
reader2 = PdfReader("doc2.pdf")
writer.add_page(reader2.pages[4])
with open("custom_merge.pdf", "wb") as output:
writer.write(output)
2.2 拆分 PDF
拆分为单页文件:
from pypdf import PdfReader, PdfWriter
reader = PdfReader("input.pdf")
for i, page in enumerate(reader.pages):
writer = PdfWriter()
writer.add_page(page)
with open(f"page_{i+1}.pdf", "wb") as output:
writer.write(output)
按范围拆分:
reader = PdfReader("document.pdf")
# 提取第 1-5 页
writer_part1 = PdfWriter()
for i in range(0, 5):
writer_part1.add_page(reader.pages[i])
writer_part1.write("pages_1_5.pdf")
# 提取第 6-10 页
writer_part2 = PdfWriter()
for i in range(5, 10):
writer_part2.add_page(reader.pages[i])
writer_part2.write("pages_6_10.pdf")
2.3 旋转页面
from pypdf import PdfReader, PdfWriter
reader = PdfReader("input.pdf")
writer = PdfWriter()
# 旋转所有页面 90 度
for page in reader.pages:
page.rotate(90) # 顺时针旋转 90 度
writer.add_page(page)
with open("rotated.pdf", "wb") as output:
writer.write(output)
旋转特定页面:
reader = PdfReader("input.pdf")
writer = PdfWriter()
for i, page in enumerate(reader.pages):
# 只旋转第 1 和第 3 页
if i in [0, 2]:
page.rotate(180)
writer.add_page(page)
writer.write("selective_rotate.pdf")
2.4 提取元数据
from pypdf import PdfReader
reader = PdfReader("document.pdf")
meta = reader.metadata
print(f"标题: {meta.title}")
print(f"作者: {meta.author}")
print(f"主题: {meta.subject}")
print(f"创建者: {meta.creator}")
print(f"创建日期: {meta.creation_date}")
print(f"修改日期: {meta.modification_date}")
2.5 添加水印
from pypdf import PdfReader, PdfWriter
# 创建或加载水印 PDF(单页)
watermark = PdfReader("watermark.pdf").pages[0]
# 应用到所有页面
reader = PdfReader("document.pdf")
writer = PdfWriter()
for page in reader.pages:
page.merge_page(watermark)
writer.add_page(page)
with open("watermarked.pdf", "wb") as output:
writer.write(output)
2.6 密码保护
from pypdf import PdfReader, PdfWriter
reader = PdfReader("input.pdf")
writer = PdfWriter()
for page in reader.pages:
writer.add_page(page)
# 添加密码(用户密码和所有者密码)
writer.encrypt(
user_password="user_pass",
owner_password="owner_pass",
permissions_flag=0b0100 # 允许打印
)
with open("encrypted.pdf", "wb") as output:
writer.write(output)
解密 PDF:
reader = PdfReader("encrypted.pdf")
if reader.is_encrypted:
reader.decrypt("user_pass")
writer = PdfWriter()
for page in reader.pages:
writer.add_page(page)
writer.write("decrypted.pdf")
工作流 3: PDF 创建
3.1 基础 PDF 创建
from reportlab.lib.pagesizes import letter, A4
from reportlab.pdfgen import canvas
c = canvas.Canvas("hello.pdf", pagesize=letter)
width, height = letter
# 添加文本
c.drawString(100, height - 100, "Hello World!")
c.drawString(100, height - 120, "这是使用 reportlab 创建的 PDF")
# 添加线条
c.line(100, height - 140, 400, height - 140)
# 添加矩形
c.rect(100, height - 200, 200, 50)
# 保存
c.save()
3.2 多页文档
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import inch
doc = SimpleDocTemplate("report.pdf", pagesize=letter)
styles = getSampleStyleSheet()
story = []
# 第一页
title = Paragraph("销售报告", styles['Title'])
story.append(title)
story.append(Spacer(1, 0.2*inch))
intro = Paragraph("本报告总结了第一季度的销售数据...", styles['Normal'])
story.append(intro)
story.append(PageBreak())
# 第二页
story.append(Paragraph("详细数据分析", styles['Heading1']))
story.append(Spacer(1, 0.1*inch))
body = Paragraph("详细的销售数据分析内容..." * 50, styles['Normal'])
story.append(body)
# 构建 PDF
doc.build(story)
3.3 添加表格
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
from reportlab.lib import colors
doc = SimpleDocTemplate("table.pdf", pagesize=letter)
elements = []
# 表格数据
data = [
['产品', '数量', '单价', '总价'],
['产品A', '100', '¥50', '¥5000'],
['产品B', '150', '¥80', '¥12000'],
['产品C', '200', '¥30', '¥6000'],
['总计', '', '', '¥23000']
]
# 创建表格
table = Table(data)
# 设置样式
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, 0), 14),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('BACKGROUND', (0, 1), (-1, -2), colors.beige),
('BACKGROUND', (0, -1), (-1, -1), colors.lightblue),
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
elements.append(table)
doc.build(elements)
3.4 添加图片
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Image, Paragraph
from reportlab.lib.styles import getSampleStyleSheet
doc = SimpleDocTemplate("image.pdf", pagesize=letter)
styles = getSampleStyleSheet()
story = []
# 添加标题
story.append(Paragraph("图片示例", styles['Title']))
# 添加图片(自动缩放)
img = Image("chart.png", width=400, height=300)
story.append(img)
doc.build(story)
工作流 4: 表单处理
4.1 填写可填充表单
from pypdf import PdfReader, PdfWriter
reader = PdfReader("form_template.pdf")
writer = PdfWriter()
# 读取表单字段
page = reader.pages[0]
fields = reader.get_fields()
print("可用字段:")
for field_name in fields:
print(f" - {field_name}")
# 填写表单
writer.append(reader)
writer.update_page_form_field_values(
writer.pages[0],
{
"Name": "张三",
"Email": "zhangsan@example.com",
"Phone": "138-0000-0000",
"Address": "北京市朝阳区"
}
)
with open("filled_form.pdf", "wb") as output:
writer.write(output)
4.2 批量填写表单
import pandas as pd
from pypdf import PdfReader, PdfWriter
# 从 Excel 读取数据
data = pd.read_excel("applicants.xlsx")
template = PdfReader("application_form.pdf")
for index, row in data.iterrows():
writer = PdfWriter()
writer.append(template)
# 填写字段
writer.update_page_form_field_values(
writer.pages[0],
{
"Name": row["姓名"],
"ID": row["身份证号"],
"Department": row["部门"],
"Date": row["日期"].strftime("%Y-%m-%d")
}
)
output_file = f"filled_{row['姓名']}.pdf"
with open(output_file, "wb") as f:
writer.write(f)
print(f"已生成: {output_file}")
4.3 提取表单字段信息
from pypdf import PdfReader
reader = PdfReader("form.pdf")
fields = reader.get_fields()
print("表单字段详情:")
for field_name, field_info in fields.items():
print(f"\n字段名: {field_name}")
print(f" 类型: {field_info.get('/FT')}")
print(f" 值: {field_info.get('/V')}")
print(f" 默认值: {field_info.get('/DV')}")
工作流 5: OCR 扫描文档
处理扫描的 PDF(图片格式),转换为可搜索文本。
安装依赖
# Python 库
pip install pytesseract pdf2image
# 系统依赖(Ubuntu/Debian)
sudo apt-get install tesseract-ocr tesseract-ocr-chi-sim poppler-utils
# macOS
brew install tesseract tesseract-lang poppler
OCR 处理
import pytesseract
from pdf2image import convert_from_path
# 转换 PDF 为图片
images = convert_from_path('scanned.pdf', dpi=300)
# OCR 每一页
full_text = ""
for i, image in enumerate(images):
print(f"正在处理第 {i+1} 页...")
text = pytesseract.image_to_string(image, lang='chi_sim+eng')
full_text += f"\n=== 第 {i+1} 页 ===\n"
full_text += text
# 保存结果
with open("ocr_output.txt", "w", encoding="utf-8") as f:
f.write(full_text)
创建可搜索的 PDF
from pdf2image import convert_from_path
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
import pytesseract
images = convert_from_path('scanned.pdf')
c = canvas.Canvas("searchable.pdf", pagesize=letter)
for image in images:
# OCR 获取文本
text = pytesseract.image_to_string(image)
# 在背景绘制原始图片
# 在前景添加透明文本(用于搜索)
# 这里简化示例,实际需要更复杂的布局
c.drawString(0, 0, text)
c.showPage()
c.save()
🔧 命令行工具
qpdf - 通用 PDF 工具
安装: sudo apt-get install qpdf 或 brew install qpdf
合并 PDF:
qpdf --empty --pages file1.pdf file2.pdf -- merged.pdf
拆分 PDF:
# 提取第 1-5 页
qpdf input.pdf --pages . 1-5 -- pages1-5.pdf
# 提取第 6-10 页
qpdf input.pdf --pages . 6-10 -- pages6-10.pdf
旋转页面:
# 旋转第 1 页 90 度
qpdf input.pdf output.pdf --rotate=+90:1
# 旋转所有页面
qpdf input.pdf output.pdf --rotate=+90:1-z
解密 PDF:
qpdf --password=mypassword --decrypt encrypted.pdf decrypted.pdf
pdftotext - 文本提取
安装: sudo apt-get install poppler-utils
基本提取:
pdftotext input.pdf output.txt
保留布局:
pdftotext -layout input.pdf output.txt
提取特定页面:
# 提取第 1-5 页
pdftotext -f 1 -l 5 input.pdf output.txt
pdfimages - 图片提取
# 提取所有图片为 JPEG
pdfimages -j input.pdf output_prefix
# 结果: output_prefix-000.jpg, output_prefix-001.jpg, ...
pdftk - PDF 工具包(如果可用)
# 合并
pdftk file1.pdf file2.pdf cat output merged.pdf
# 拆分(burst 生成单页文件)
pdftk input.pdf burst
# 旋转
pdftk input.pdf rotate 1east output rotated.pdf
📋 快速参考表
| 任务 | 最佳工具 | 命令/代码 |
|---|---|---|
| 提取文本 | pdfplumber | page.extract_text() |
| 提取表格 | pdfplumber | page.extract_tables() |
| 合并 PDF | pypdf | writer.append(pdf) |
| 拆分 PDF | pypdf | 逐页提取 |
| 创建 PDF | reportlab | Canvas 或 Platypus |
| 填写表单 | pypdf | update_page_form_field_values() |
| OCR 识别 | pytesseract | image_to_string() |
| 旋转页面 | pypdf | page.rotate(90) |
| 加密 PDF | pypdf | writer.encrypt() |
| 添加水印 | pypdf | page.merge_page(watermark) |
| 命令行合并 | qpdf | qpdf --empty --pages ... |
| 命令行提取文本 | pdftotext | pdftotext -layout |
🎯 最佳实践
1. 性能优化
处理大文件:
# ❌ 不好:一次性读取所有页面
with pdfplumber.open("large.pdf") as pdf:
all_text = "".join([p.extract_text() for p in pdf.pages])
# ✅ 好:逐页处理
with pdfplumber.open("large.pdf") as pdf:
for page in pdf.pages:
text = page.extract_text()
process(text) # 处理后立即释放内存
批量处理:
import concurrent.futures
def process_pdf(pdf_file):
# 处理单个 PDF
pass
pdf_files = ["file1.pdf", "file2.pdf", ...]
# 并行处理
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
results = executor.map(process_pdf, pdf_files)
2. 错误处理
from pypdf import PdfReader
from pypdf.errors import PdfReadError
try:
reader = PdfReader("document.pdf")
if reader.is_encrypted:
reader.decrypt("password")
for page in reader.pages:
text = page.extract_text()
except PdfReadError as e:
print(f"PDF 读取错误: {e}")
except Exception as e:
print(f"未知错误: {e}")
3. 表格提取优化
import pdfplumber
# 自定义表格检测设置
table_settings = {
"vertical_strategy": "lines", # 使用线条检测垂直边界
"horizontal_strategy": "lines", # 使用线条检测水平边界
"min_words_vertical": 3, # 最小垂直词数
"min_words_horizontal": 3, # 最小水平词数
}
with pdfplumber.open("complex_table.pdf") as pdf:
table = pdf.pages[0].extract_table(table_settings=table_settings)
4. 内存管理
# 处理完后显式关闭
reader = PdfReader("file.pdf")
# ... 处理 ...
reader.stream.close()
# 或使用 with 语句(推荐)
with pdfplumber.open("file.pdf") as pdf:
# 自动关闭
pass
⚠️ 常见问题
问题 1: 表格提取不准确
原因: PDF 中的表格可能是图片或布局复杂
解决方案:
- 调整
table_settings参数 - 对于图片表格,使用 OCR
- 考虑手动指定表格区域
# 手动指定表格区域
bbox = (x0, y0, x1, y1) # 左上角和右下角坐标
table = page.crop(bbox).extract_table()
问题 2: 中文乱码
原因: 字体嵌入问题或编码不支持
解决方案:
# 使用 pdfplumber(通常更好地处理中文)
with pdfplumber.open("chinese.pdf") as pdf:
text = pdf.pages[0].extract_text()
# 确保使用 UTF-8 保存
with open("output.txt", "w", encoding="utf-8") as f:
f.write(text)
问题 3: 扫描 PDF 无法提取文本
原因: 扫描的 PDF 是图片,没有文本层
解决方案: 使用 OCR(见工作流 5)
问题 4: 表单字段名称未知
解决方案: 先提取字段列表
reader = PdfReader("form.pdf")
fields = reader.get_fields()
for name in fields:
print(name)
🔗 相关资源
官方文档
进阶主题
- PDF/A 归档格式 - 长期保存
- PDF 表单高级功能 - 下拉框、单选按钮
- PDF 数字签名 - 使用 pyHanko
- PDF 优化和压缩 - 减小文件大小
📝 总结
PDF 文档处理技能提供了完整的 PDF 操作解决方案:
✅ 数据提取 - pdfplumber 精确提取文本和表格
✅ 文档操作 - pypdf 合并、拆分、加密
✅ PDF 创建 - reportlab 生成专业文档
✅ 表单处理 - 批量填写和数据收集
✅ OCR 识别 - 处理扫描文档
根据具体任务选择合适的工具,遵循最佳实践,可以高效处理各种 PDF 操作需求。