关于 pdf-processing
面向 Python 的 PDF 处理技能,可生成、提取和修改 PDF 文档。它帮助开发者为 OCR、表格提取、表单填写、合并和水印等任务选择库并验证处理流程。
主要功能
- 01使用 ReportLab 生成 PDF
- 02使用 pdfplumber 提取文本和表格
- 03使用 pypdf 合并、拆分和编辑元数据
- 04对扫描文档执行 OCR
- 05填写表单并添加水印
- 06验证输出并处理 PDF 边缘情况
适用场景
- 01创建格式化 PDF 报告
- 02从 PDF 提取文本或表格
- 03通过 OCR 处理扫描文档
- 04合并文件或填写 PDF 表单
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ pixel-process-ug/ superkit-agents --skill pdf-processing安装后在 Claude Code 中输入/pdf-processing确认已加载。
详细介绍
PDF 处理
概述
生成、操作 PDF 文档并从中提取数据。本技能涵盖 Python PDF 生态系统:用于合并、拆分和处理元数据的 pypdf,用于文本和表格提取的 pdfplumber,用于生成的 reportlab,用于 OCR 的 pytesseract,以及表单填写、水印添加和复杂文档组装策略。
只要需要通过代码创建、解析、转换或合并 PDF,就应用本技能。
多阶段流程
阶段 1:需求分析
- 确定操作类型(生成、提取、操作)
- 识别输入 PDF 的特征(扫描件、数字版、表单)
- 定义输出要求(格式、质量、大小)
- 规划数据处理流程(从源数据到 PDF,或从 PDF 到数据)
- 评估数据量和性能要求
停止 — 在明确操作类型和输入特征之前,不要选择库。
阶段 2:实现
- 为任务选择合适的库(参见决策表)
- 实现核心处理逻辑
- 处理边缘情况(文件损坏、PDF 已加密、内容混杂)
- 添加错误处理和验证
- 优化文件大小和处理速度
停止 — 不要跳过对已加密、旋转或扫描版 PDF 的边缘情况处理。
阶段 3:验证
- 确认输出在多个 PDF 查看器中都能正确渲染
- 在适用情况下,检查文本是否可选中(而非栅格化)
- 验证提取数据的准确性
- 使用边缘情况 PDF 进行测试(大型、已加密、扫描版)
- 验证可访问性(必要时使用带标签的 PDF)
库选择决策表
| 任务 | 库 | 原因 | 替代方案 |
|---|---|---|---|
| 文本提取 | pdfplumber | 准确度最高,可处理布局 | pypdf(更简单,但准确度较低) |
| 表格提取 | pdfplumber | 可解析结构化表格 | camelot(专用表格工具) |
| PDF 生成 | reportlab | 完全可控,品质专业 | weasyprint(HTML 转 PDF) |
| 合并 / 拆分 | pypdf | 简单、可靠、快速 | — |
| 表单填写 | pypdf | 可读取并填写 AcroForms | pdfrw(替代 API) |
| 元数据读写 | pypdf | 读写 PDF 属性 | — |
| OCR(扫描文档) | pytesseract + pdf2image | 提取扫描文档中的文本 | EasyOCR(深度学习) |
| 添加水印 | pypdf + reportlab | 叠加页面 | — |
| HTML 转 PDF | weasyprint | 基于 CSS 的布局,适合服务器环境 | playwright(浏览器渲染) |
使用 ReportLab 生成 PDF
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import HexColor
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table,
TableStyle, Image, PageBreak
)
from reportlab.lib import colors
def generate_report(output_path, data):
doc = SimpleDocTemplate(
output_path,
pagesize=A4,
topMargin=2.5*cm,
bottomMargin=2.5*cm,
leftMargin=2.5*cm,
rightMargin=2.5*cm,
)
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(
name='CustomTitle',
parent=styles['Title'],
fontSize=24,
textColor=HexColor('#2F5496'),
spaceAfter=20,
))
story = []
# Title
story.append(Paragraph(data['title'], styles['CustomTitle']))
story.append(Spacer(1, 12))
# Body text
story.append(Paragraph(data['body'], styles['Normal']))
story.append(Spacer(1, 20))
# Table
table_data = [['Name', 'Value', 'Status']]
for row in data['rows']:
table_data.append([row['name'], row['value'], row['status']])
table = Table(table_data, colWidths=[6*cm, 4*cm, 4*cm])
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), HexColor('#2F5496')),
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, 0), 11),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, HexColor('#F0F4FA')]),
('TOPPADDING', (0, 0), (-1, -1), 8),
('BOTTOMPADDING', (0, 0), (-1, -1), 8),
]))
story.append(table)
doc.build(story)
自定义页面模板(页眉/页脚)
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate
from datetime import datetime
def add_header_footer(canvas, doc):
canvas.saveState()
# Header
canvas.setFont('Helvetica', 9)
canvas.setFillColor(HexColor('#888888'))
canvas.drawString(2.5*cm, A4[1] - 1.5*cm, 'Company Name — Confidential')
canvas.drawRightString(A4[0] - 2.5*cm, A4[1] - 1.5*cm, f'Page {doc.page}')
# Footer
canvas.drawCentredString(A4[0]/2, 1.5*cm, f'Generated on {datetime.now():%Y-%m-%d}')
canvas.restoreState()
doc = BaseDocTemplate(output_path, pagesize=A4)
frame = Frame(2.5*cm, 2.5*cm, A4[0]-5*cm, A4[1]-5*cm)
doc.addPageTemplates([PageTemplate(id='main', frames=[frame], onPage=add_header_footer)])
文本和表格提取
pdfplumber
import pdfplumber
with pdfplumber.open('document.pdf') as pdf:
# Extract text from all pages
full_text = ''
for page in pdf.pages:
full_text += page.extract_text() + '\n'
# Extract tables
for page in pdf.pages:
tables = page.extract_tables()
for table in tables:
for row in table:
print(row)
# Extract text from specific area
page = pdf.pages[0]
bbox = (50, 100, 400, 300) # (x0, top, x1, bottom)
cropped = page.within_bbox(bbox)
text = cropped.extract_text()
表格提取设置
table_settings = {
"vertical_strategy": "lines", # or "text", "explicit"
"horizontal_strategy": "lines",
"snap_tolerance": 3,
"join_tolerance": 3,
"edge_min_length": 3,
"min_words_vertical": 3,
"min_words_horizontal": 1,
}
tables = page.extract_tables(table_settings)
表单填写
from pypdf import PdfReader, PdfWriter
reader = PdfReader('form.pdf')
writer = PdfWriter()
writer.append(reader)
# Fill form fields
writer.update_page_form_field_values(
writer.pages[0],
{
'full_name': 'Alice Johnson',
'email': '[email protected]',
'date': '2025-03-15',
'agree_terms': '/Yes', # Checkbox
},
auto_regenerate=False,
)
with open('filled_form.pdf', 'wb') as f:
writer.write(f)
OCR(扫描版 PDF)
from pdf2image import convert_from_path
import pytesseract
def ocr_pdf(pdf_path, language='eng'):
images = convert_from_path(pdf_path, dpi=300)
full_text = ''
for i, image in enumerate(images):
text = pytesseract.image_to_string(image, lang=language)
full_text += f'\n--- Page {i+1} ---\n{text}'
return full_text
# For better accuracy with specific layouts:
def ocr_with_config(image):
custom_config = r'--oem 3 --psm 6' # LSTM engine, assume uniform block
return pytesseract.image_to_string(image, config=custom_config)
合并和拆分
from pypdf import PdfReader, PdfWriter
# Merge multiple PDFs
def merge_pdfs(input_paths, output_path):
writer = PdfWriter()
for path in input_paths:
reader = PdfReader(path)
for page in reader.pages:
writer.add_page(page)
with open(output_path, 'wb') as f:
writer.write(f)
# Split PDF by page ranges
def split_pdf(input_path, ranges, output_dir):
reader = PdfReader(input_path)
for i, (start, end) in enumerate(ranges):
writer = PdfWriter()
for page_num in range(start - 1, min(end, len(reader.pages))):
writer.add_page(reader.pages[page_num])
with open(f'{output_dir}/part_{i+1}.pdf', 'wb') as f:
writer.write(f)
# Extract specific pages
def extract_pages(input_path, page_numbers, output_path):
reader = PdfReader(input_path)
writer = PdfWriter()
for num in page_numbers:
writer.add_page(reader.pages[num - 1])
with open(output_path, 'wb') as f:
writer.write(f)
添加水印
from pypdf import PdfReader, PdfWriter
from reportlab.pdfgen import canvas as rl_canvas
from reportlab.lib.pagesizes import A4
from io import BytesIO
def create_watermark(text, opacity=0.1):
buffer = BytesIO()
c = rl_canvas.Canvas(buffer, pagesize=A4)
c.setFillAlpha(opacity)
c.setFont('Helvetica-Bold', 60)
c.setFillColorRGB(0.5, 0.5, 0.5)
c.translate(A4[0]/2, A4[1]/2)
c.rotate(45)
c.drawCentredString(0, 0, text)
c.save()
buffer.seek(0)
return PdfReader(buffer)
def apply_watermark(input_path, output_path, watermark_text):
watermark = create_watermark(watermark_text)
reader = PdfReader(input_path)
writer = PdfWriter()
for page in reader.pages:
page.merge_page(watermark.pages[0])
writer.add_page(page)
with open(output_path, 'wb') as f:
writer.write(f)
元数据处理
from pypdf import PdfReader, PdfWriter
# Read metadata
reader = PdfReader('document.pdf')
info = reader.metadata
print(f'Title: {info.title}')
print(f'Author: {info.author}')
print(f'Pages: {len(reader.pages)}')
# Write metadata
writer = PdfWriter()
writer.append(reader)
writer.add_metadata({
'/Title': 'Updated Title',
'/Author': 'Author Name',
'/Subject': 'Document Subject',
'/Creator': 'My Application',
})
with open('updated.pdf', 'wb') as f:
writer.write(f)
反模式 / 常见错误
| 反模式 | 失败原因 | 建议做法 |
|---|---|---|
| 对数字版(基于文本的)PDF 执行 OCR | 文本已可提取时,OCR 速度慢且准确度低 | 先检查能否提取文本,仅在结果为空时执行 OCR |
| 未处理已加密的 PDF | 导致崩溃或静默失败 | 检测加密状态,提示输入密码或妥善跳过 |
| 将整个大型 PDF 加载到内存 | 导致服务器内存耗尽 | 按页流式处理或分块处理 |
| 忽略页面旋转元数据 | 提取的文本会变得乱码 | 提取前读取并应用旋转信息 |
| 硬编码页面尺寸 | 无法处理非 A4 文档 | 从源 PDF 读取尺寸 |
| 未关闭文件句柄 | 长时间运行的进程会发生资源泄漏 | 使用上下文管理器(with 语句) |
| 生成后未在多个查看器中测试 | 不同查看器的渲染效果有差异 | 在 Adobe Reader、Preview 和 Chrome 中测试 |
| 提取表格时未调整设置 | 列对齐效果差,单元格可能合并 | 针对不同文档类型调整 table_settings |
反合理化防护
- 不要在尝试直接提取文本之前使用 OCR——先检查 PDF 类型。
- 不要跳过加密检测——即使“大多数 PDF 都未加密”,也要明确处理。
- 不要假设页面尺寸为 A4——从源文档中读取尺寸。
- 不要只在一个 PDF 查看器中测试——Adobe、Preview 和 Chrome 的渲染效果各不相同。
- 不要在处理大型 PDF 时忽略内存占用——采用节省内存的模式(流式处理、分块)。
集成点
| 技能 | 关联方式 |
|---|---|
docx-processing | DOCX 转 PDF 流程,或格式选择 |
xlsx-processing | Excel 中的数据填充 PDF 报告表格 |
email-composer | 将生成的 PDF 附加到专业邮件中 |
content-research-writer | 将研究结果排版为 PDF 白皮书 |
file-organizer | 输出文件命名和目录结构规范 |
deployment | 服务器/CI 环境中的 PDF 生成流程 |
技能类型
灵活——根据具体的 PDF 任务选择合适的库和方法。生成时使用 ReportLab,提取时使用 pdfplumber,处理时使用 pypdf。可按需组合。
评论
支持 Markdown