在日常工作中,我们经常需要从PDF文档中提取表格数据。手动复制粘贴效率低下,且容易出错。本文将介绍如何使用Python编写程序,自动识别并提取PDF文档中的表格数据,并将其保存为CSV格式,方便后续分析和处理。我们将重点解决表格跨页、合并单元格等复杂情况,并提供详细的代码示例。
1. 准备工作
在开始之前,需要安装以下Python库:
- pdfplumber: 用于解析PDF文档,提取文本和表格。
- pandas: 用于处理表格数据,并导出为CSV格式。
可以使用pip命令安装:
pip install pdfplumber pandas
2. 代码实现
以下是一个示例代码,可以提取PDF文档中的所有表格,并将每个表格保存为一个CSV文件。
import pdfplumber
import pandas as pd
import os
def extract_tables_from_pdf(pdf_path, output_dir='tables'):
"""从PDF文档中提取表格数据,并保存为CSV文件。"
if not os.path.exists(output_dir):
os.makedirs(output_dir)
with pdfplumber.open(pdf_path) as pdf:
table_index = 1
for i, page in enumerate(pdf.pages):
tables = page.extract_tables()
if tables:
for table in tables:
# 将表格数据转换为DataFrame
df = pd.DataFrame(table[1:], columns=table[0]) if table[0] else pd.DataFrame(table)
# 清理DataFrame:移除空行、空列和特殊字符
df = df.dropna(axis=0, how='all') # 移除所有值均为 NaN 的行
df = df.dropna(axis=1, how='all') # 移除所有值均为 NaN 的列
# 移除DataFrame中的换行符(\n)、制表符(\t)和回车符(\r)
df = df.replace({r'[\n\t\r]': ''}, regex=True)
# 将DataFrame中的None值替换为空字符串
df = df.fillna('')
# 保存为CSV文件
output_path = os.path.join(output_dir, f'table_{table_index}_page_{i+1}.csv')
df.to_csv(output_path, index=False, encoding='utf-8-sig')
print(f'表格 {table_index} 从第 {i+1} 页提取成功,保存到:{output_path}')
table_index += 1
if __name__ == '__main__':
pdf_file = 'example.pdf' # 替换为你的PDF文件路径
extract_tables_from_pdf(pdf_file)
代码解释:
- 导入库: 导入
pdfplumber
和pandas
库。 extract_tables_from_pdf
函数:- 接收PDF文件路径和输出目录作为参数。
- 使用
pdfplumber.open()
打开PDF文档。 - 循环遍历每一页,使用
page.extract_tables()
提取表格。 - 将提取的表格数据转换为
pandas.DataFrame
。 - 将DataFrame保存为CSV文件,文件名包含表格索引和页码。
- 主程序:
- 指定PDF文件路径。
- 调用
extract_tables_from_pdf
函数,提取表格数据。
3. 处理复杂表格
3.1 表格跨页
pdfplumber
可以自动处理表格跨页的情况。当表格跨页时,page.extract_tables()
会将跨页的表格合并为一个表格。
3.2 合并单元格
pdfplumber
对合并单元格的支持有限。提取的表格数据中,合并的单元格可能会被拆分为多个单元格。为了解决这个问题,可以尝试以下方法:
- 手动调整: 提取表格后,手动编辑CSV文件,合并被拆分的单元格。这适用于表格数量较少的情况。
- 自定义解析: 如果PDF文档的格式比较规范,可以尝试自定义解析方法。例如,根据单元格的坐标和文本内容,判断是否为合并单元格,并进行合并。
以下是一个自定义解析合并单元格的示例代码:
import pdfplumber
import pandas as pd
import os
def extract_tables_with_merged_cells(pdf_path, output_dir='tables_merged'):
if not os.path.exists(output_dir):
os.makedirs(output_dir)
with pdfplumber.open(pdf_path) as pdf:
table_index = 1
for i, page in enumerate(pdf.pages):
# 使用`extract_table`方法,并自定义参数
table = page.extract_table({
'vertical_strategy': 'lines',
'horizontal_strategy': 'lines',
'explicit_vertical_lines': page.vertical_lines,
'explicit_horizontal_lines': page.horizontal_lines
})
if table:
# 将表格数据转换为DataFrame
df = pd.DataFrame(table[1:], columns=table[0]) if table[0] else pd.DataFrame(table)
# 清理DataFrame:移除空行、空列和特殊字符
df = df.dropna(axis=0, how='all') # 移除所有值均为 NaN 的行
df = df.dropna(axis=1, how='all') # 移除所有值均为 NaN 的列
# 移除DataFrame中的换行符(\n)、制表符(\t)和回车符(\r)
df = df.replace({r'[\n\t\r]': ''}, regex=True)
# 将DataFrame中的None值替换为空字符串
df = df.fillna('')
# 保存为CSV文件
output_path = os.path.join(output_dir, f'table_{table_index}_page_{i+1}.csv')
df.to_csv(output_path, index=False, encoding='utf-8-sig')
print(f'表格 {table_index} 从第 {i+1} 页提取成功,保存到:{output_path}')
table_index += 1
if __name__ == '__main__':
pdf_file = 'example.pdf' # 替换为你的PDF文件路径
extract_tables_with_merged_cells(pdf_file)
代码解释:
- 使用
extract_table
方法: 使用page.extract_table()
方法,而不是page.extract_tables()
方法。extract_table
方法允许自定义参数,更灵活地控制表格提取过程。 - 自定义参数: 通过
vertical_strategy
、horizontal_strategy
、explicit_vertical_lines
和explicit_horizontal_lines
等参数,指定表格的提取策略。这些参数可以帮助pdfplumber
更准确地识别表格的边界,从而更好地处理合并单元格。
注意: 自定义解析方法需要根据具体的PDF文档进行调整。不同的PDF文档可能需要不同的参数设置才能达到最佳效果。
4. 优化和改进
- 错误处理: 在代码中添加错误处理机制,例如,捕获文件不存在、PDF格式错误等异常,提高程序的健壮性。
- 进度显示: 当处理大型PDF文档时,可以添加进度显示,让用户了解程序的运行状态。
- 多线程处理: 对于包含大量表格的PDF文档,可以使用多线程技术,并行提取表格数据,提高程序的运行效率。
- 更强大的库: 尝试使用
camelot
或tabula-py
等更专业的PDF表格提取库,它们可能在处理复杂表格方面表现更好。 但这些库通常依赖于外部Java环境,配置可能相对复杂。 - OCR: 如果PDF是扫描件,需要先进行OCR识别,将图片转换为文本,才能提取表格数据。可以使用
pytesseract
库进行OCR识别。
5. 总结
本文介绍了如何使用Python自动提取PDF表格数据,并保存为CSV格式。我们提供了详细的代码示例,并讨论了如何处理表格跨页、合并单元格等复杂情况。通过学习本文,您可以快速构建自己的PDF表格提取工具,提高工作效率。
希望本文对您有所帮助!快去试试吧! 记住,实践是检验真理的唯一标准,多尝试不同的PDF文档,你就能掌握更多技巧!