Python操作PDF:别再手动复制粘贴了,20行代码搞定合并拆分
上周老板扔给我一个需求:把10份合同PDF合并成一个文件,还要给每页加上公司水印。
我问行政小妹之前怎么做的,她说手动用Adobe Acrobat一页页复制。
10个文件。手动。每次还要对齐。
我打开编辑器,写了不到20行代码,3秒跑完。她看我的眼神像看魔术师。
今天就把这套方法讲透,你也能做到。
为什么选 PyPDF2 和 pdfplumber?
Python 操作 PDF 的库有好几个,我直接说结论:根据需求选,别只认一个。
- •
PyPDF2:老牌库,擅长合并、拆分、旋转、加密等页面操作,轻量但文本提取较弱 - •
pdfplumber:文本提取神器,能精确定位文字、提取表格,适合数据抓取场景 - •
reportlab:生成PDF的王者,能从零创建带格式的文档,但读取能力一般 - •
pymupdf (fitz):全能型选手,读写、渲染、OCR都行,但安装稍复杂
新手建议先装两个:
pip install PyPDF2 pdfplumber
装完就能用,不需要额外配置。
先搞懂 PDF 的"术语"
操作 PDF 之前,得先知道代码里的东西对应文件里的哪个位置:
| | |
|---|
PdfReader | | |
PdfWriter | | |
PageObject | | |
Page.extract_text() | | |
Page.merge_page() | | |
读写基础:5 分钟上手
读取 PDF 信息
from PyPDF2 import PdfReaderreader = PdfReader('合同.pdf')print(f'页数:{len(reader.pages)}')print(f'标题:{reader.metadata.title}')first_page = reader.pages[0]text = first_page.extract_text()print(text[:500])
提取所有页的文字
from PyPDF2 import PdfReaderreader = PdfReader('报告.pdf')all_text = []for i, page in enumerate(reader.pages): text = page.extract_text() all_text.append(f'--- 第 {i+1} 页 ---\n{text}')with open('报告全文.txt', 'w', encoding='utf-8') as f: f.write('\n'.join(all_text))print('提取完成!')
extract_text() 这个方法很关键。不加它,你拿到的是 PageObject 对象,还得再处理才能拿到文字。直接调用省一半代码。
表格提取:用 pdfplumber 精准抓取
PyPDF2 提取文字还行,但表格数据经常乱成一锅粥。
这时候换 pdfplumber,它能识别表格结构:
import pdfplumberwith pdfplumber.open('财务报表.pdf') as pdf: first_page = pdf.pages[0] tables = first_page.extract_tables() for row in tables[0]: print(row) # ['项目', '金额', '占比'] # ['工资', '50000', '50%']
extract_tables() 返回的是二维列表,每个子列表是一行。直接就是结构化数据,省得自己解析。
合并 PDF:最常用的操作
from PyPDF2 import PdfMergermerger = PdfMerger()merger.append('1月报告.pdf')merger.append('2月报告.pdf', pages=[0, 2])merger.append('3月报告.pdf', pages=(0, 5))merger.write('Q1报告汇总.pdf')merger.close()print('合并完成!')
封装成函数,合并整个文件夹:
from pathlib import Pathdef merge_pdfs_in_folder(folder, output='合并结果.pdf'): merger = PdfMerger() pdf_files = sorted(Path(folder).glob('*.pdf')) for pdf in pdf_files: if pdf.name == output: continue merger.append(str(pdf)) merger.write(output) merger.close() print(f'合并了 {len(pdf_files)} 个文件')merge_pdfs_in_folder('./合同文件夹/')
拆分 PDF:按页提取
把 100 页的报告拆成每 10 页一个文件:
from PyPDF2 import PdfReader, PdfWriterreader = PdfReader('年度报告.pdf')total_pages = len(reader.pages)for start in range(0, total_pages, 10): writer = PdfWriter() end = min(start + 10, total_pages) for page_num in range(start, end): writer.add_page(reader.pages[page_num]) filename = f'报告_第{start+1}-{end}页.pdf' with open(filename, 'wb') as f: writer.write(f) print(f'已生成: {filename}')
踩坑提醒:PdfWriter 每次循环都要新建一个,不然页面会累加。
添加水印:保护文档
from PyPDF2 import PdfReader, PdfWriterdef add_watermark(input_pdf, watermark_pdf, output_pdf): reader = PdfReader(input_pdf) watermark_reader = PdfReader(watermark_pdf) watermark_page = watermark_reader.pages[0] writer = PdfWriter() for page in reader.pages: page.merge_page(watermark_page) writer.add_page(page) with open(output_pdf, 'wb') as f: writer.write(f) print(f'水印添加完成: {output_pdf}')add_watermark('合同.pdf', '水印.pdf', '合同_已水印.pdf')
注意:水印 PDF 必须是透明背景的单页 PDF,不然会把原内容盖住。
加密解密:保护敏感信息
from PyPDF2 import PdfReader, PdfWriterreader = PdfReader('薪资表.pdf')writer = PdfWriter()for page in reader.pages: writer.add_page(page)writer.encrypt('123456')with open('薪资表_加密.pdf', 'wb') as f: writer.write(f)print('加密完成!密码是: 123456')
解密:
reader = PdfReader('薪资表_加密.pdf')if reader.is_encrypted: reader.decrypt('123456')text = reader.pages[0].extract_text()print(text)
完整实战:批量处理合同
回到开头的需求——合并合同并加水印:
from PyPDF2 import PdfMerger, PdfReader, PdfWriterfrom pathlib import Pathdef process_contracts(folder, watermark_pdf, output='合同汇总.pdf'): # 1. 合并所有合同 merger = PdfMerger() pdf_files = sorted(Path(folder).glob('*.pdf')) for pdf in pdf_files: if pdf.name == output or pdf.name == watermark_pdf: continue merger.append(str(pdf)) merged_file = 'temp_merged.pdf' merger.write(merged_file) merger.close() # 2. 添加水印 reader = PdfReader(merged_file) watermark = PdfReader(watermark_pdf).pages[0] writer = PdfWriter() for page in reader.pages: page.merge_page(watermark) writer.add_page(page) # 3. 保存最终文件 with open(output, 'wb') as f: writer.write(f) Path(merged_file).unlink() print(f'处理完成,共 {len(pdf_files)-1} 份合同')process_contracts('./合同/', '水印.pdf')
写在最后
Python 操作 PDF 这件事,会了就是生产力翻倍,不会就是加班到头秃。