手动复制100份合同的关键条款需要多久?答案是至少半天。而用Python,只需要3秒。
上周五下午,财务部的小王又被领导叫去整理PDF合同。整整一摞合同需要提取金额、日期和双方信息。她叹了口气,开始打开第一个PDF,选中文本,复制,粘贴到Excel...
而此时,技术部的小张已经完成了本周的工作,正在悠闲地喝着咖啡。区别在哪里?小张花了10分钟写了个Python脚本,剩下的工作都由计算机自动完成了。
今天,我就带你掌握这个技能——用Python批量处理PDF合同,让你告别复制粘贴的重复劳动。
为什么选择Python处理PDF?
PDF文件因其格式固定、不易篡改的特性,在商务场景中被广泛使用。但这也带来了数据处理的难题:当需要从合同、报表、发票等PDF文件中提取结构化数据时,手动复制粘贴效率低下且易出错 。
Python的pdfplumber库为我们提供了高效的解决方案。它不仅能提取文本,还能精准解析表格结构,甚至处理扫描件(需配合OCR技术)。
准备工作:安装必要的库
在开始之前,我们需要安装两个Python库:
```bash
pip install pdfplumber openpyxl
```
· pdfplumber:负责从PDF中提取文本和表格数据
· openpyxl:帮助我们创建和编辑Excel文件,方便保存提取的数据
第一步:从PDF中提取文本
最简单的需求是从PDF中提取所有文本内容。让我们从一个基础示例开始:
```python
import pdfplumber
# 打开PDF文件
with pdfplumber.open("合同示例.pdf") as pdf:
# 获取第一页
first_page = pdf.pages[0]
# 提取文本
text = first_page.extract_text()
print(text)
```
这段代码会输出PDF第一页的全部文本内容。但实际合同处理中,我们通常只需要特定的关键信息,比如合同金额、签订日期、双方名称等。
第二步:提取关键信息(实战!)
假设我们需要从以下合同文本中提取关键字段:
```
合同编号:HT-2023-001
签订日期:2023年5月15日
甲方:北京科技有限公司
乙方:上海信息技术有限公司
合同金额:人民币壹佰贰拾万元整(¥1,200,000)
付款方式:合同签订后3个工作日内支付30%
```
我们可以使用正则表达式来提取这些信息:
```python
import pdfplumber
import re
def extract_contract_info(pdf_path):
info = {}
with pdfplumber.open(pdf_path) as pdf:
# 提取所有文本
full_text = ""
for page in pdf.pages:
full_text += page.extract_text() + "\n"
# 使用正则表达式提取关键信息
# 提取合同编号
contract_no_match = re.search(r'合同编号[::]\s*([^\n]+)', full_text)
if contract_no_match:
info['合同编号'] = contract_no_match.group(1).strip()
# 提取签订日期
date_match = re.search(r'签订日期[::]\s*([^\n]+)', full_text)
if date_match:
info['签订日期'] = date_match.group(1).strip()
# 提取甲方
party_a_match = re.search(r'甲方[::]\s*([^\n]+)', full_text)
if party_a_match:
info['甲方'] = party_a_match.group(1).strip()
# 提取乙方
party_b_match = re.search(r'乙方[::]\s*([^\n]+)', full_text)
if party_b_match:
info['乙方'] = party_b_match.group(1).strip()
# 提取金额(支持数字格式)
amount_match = re.search(r'金额[::]\s*([^\n]+?)(?:\s|$)', full_text)
if amount_match:
info['合同金额'] = amount_match.group(1).strip()
return info
# 使用示例
result = extract_contract_info("销售合同.pdf")
for key, value in result.items():
print(f"{key}: {value}")
```
第三步:批量处理合同文件夹
真正的效率提升在于批量处理。现在,让我们编写一个脚本,自动处理整个文件夹中的所有PDF合同,并将结果保存到Excel文件中:
```python
import pdfplumber
import re
import os
from openpyxl import Workbook
def batch_process_contracts(folder_path, output_excel):
# 创建Excel工作簿
wb = Workbook()
ws = wb.active
ws.title = "合同信息汇总"
# 添加表头
headers = ['文件名', '合同编号', '签订日期', '甲方', '乙方', '合同金额']
for col, header in enumerate(headers, 1):
ws.cell(row=1, column=col, value=header)
# 获取文件夹中所有PDF文件
pdf_files = [f for f in os.listdir(folder_path) if f.lower().endswith('.pdf')]
# 处理每个PDF文件
row_num = 2
for pdf_file in pdf_files:
pdf_path = os.path.join(folder_path, pdf_file)
print(f"正在处理: {pdf_file}")
try:
# 提取合同信息
info = extract_contract_info(pdf_path)
# 写入Excel
ws.cell(row=row_num, column=1, value=pdf_file)
ws.cell(row=row_num, column=2, value=info.get('合同编号', ''))
ws.cell(row=row_num, column=3, value=info.get('签订日期', ''))
ws.cell(row=row_num, column=4, value=info.get('甲方', ''))
ws.cell(row=row_num, column=5, value=info.get('乙方', ''))
ws.cell(row=row_num, column=6, value=info.get('合同金额', ''))
row_num += 1
except Exception as e:
print(f"处理 {pdf_file} 时出错: {e}")
# 保存Excel文件
wb.save(output_excel)
print(f"处理完成!结果已保存至: {output_excel}")
print(f"共处理 {len(pdf_files)} 个文件,成功写入 {row_num-2} 个")
# 使用示例
batch_process_contracts("F:\\合同文件夹", "合同信息汇总.xlsx")
```
第四步:处理PDF中的表格数据
很多合同包含付款计划、产品清单等表格数据。pdfplumber在提取表格方面表现出色:
```python
import pdfplumber
import csv
def extract_tables_from_pdf(pdf_path, output_csv):
all_tables = []
with pdfplumber.open(pdf_path) as pdf:
for page_num, page in enumerate(pdf.pages, 1):
# 提取页面中的表格
tables = page.extract_tables()
for table_num, table in enumerate(tables, 1):
if table: # 确保表格不为空
all_tables.append({
'page': page_num,
'table': table_num,
'data': table
})
# 将第一个表格保存为CSV(可根据需求调整)
if all_tables:
with open(output_csv, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
for row in all_tables[0]['data']:
writer.writerow(row)
print(f"表格已提取至: {output_csv}")
print(f"共发现 {len(all_tables)} 个表格")
return all_tables
# 使用示例
tables = extract_tables_from_pdf("付款计划.pdf", "付款计划.csv")
```
性能优化与异常处理
在处理大量PDF文件时,我们需要考虑性能和稳定性:
```python
import pdfplumber
from multiprocessing import Pool
import logging
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def process_single_pdf(pdf_path):
"""处理单个PDF文件的函数,用于多进程"""
try:
with pdfplumber.open(pdf_path) as pdf:
# 这里添加你的处理逻辑
text = ""
for page in pdf.pages:
text += page.extract_text() or "" # 处理extract_text返回None的情况
# 模拟信息提取
info = {
'file': os.path.basename(pdf_path),
'pages': len(pdf.pages),
'text_length': len(text)
}
return info
except Exception as e:
logging.error(f"处理 {pdf_path} 失败: {e}")
return None
def batch_process_multiprocessing(folder_path):
"""使用多进程批量处理PDF文件"""
pdf_files = [os.path.join(folder_path, f) for f in os.listdir(folder_path)
if f.lower().endswith('.pdf')]
# 使用4个进程并行处理
with Pool(4) as pool:
results = pool.map(process_single_pdf, pdf_files)
# 过滤掉失败的处理结果
successful = [r for r in results if r]
logging.info(f"成功处理 {len(successful)}/{len(pdf_files)} 个文件")
return successful
# 使用示例
results = batch_process_multiprocessing("F:\\合同文件夹")
```
常见问题与解决方案
问题1:表格提取错位
原因:PDF中表格线不清晰或存在合并单元格。
解决方案:调整snap_tolerance参数
```python
table = page.extract_table({
"vertical_strategy": "lines",
"horizontal_strategy": "lines",
"snap_tolerance": 5
})
```
问题2:中文乱码
原因:PDF内置字体缺失或编码问题。
解决方案:确保系统安装了中文字体,或使用char_tolerance参数
问题3:处理扫描件PDF
限制:pdfplumber无法直接解析扫描件(图像格式)
替代方案:结合pytesseract进行OCR识别
总结与展望
通过以上代码,你已经掌握了用Python批量处理PDF合同的基本技能。从单文件提取到批量处理,从文本识别到表格解析,这些工具能帮你节省大量时间,让你从繁琐的复制粘贴中解放出来。
下一期预告:我们将深入探讨如何处理更复杂的合同场景——当合同包含复杂的页眉页脚、跨页表格,甚至是扫描图像时,该如何应对?敬请期待《Python处理PDF高级篇:当合同不再“标准”时》。
如果你在实践过程中遇到问题,欢迎在评论区留言,我们一起探讨解决方案。