Python实用工具:Camelot库——轻松提取PDF表格数据的完整指南

一、Camelot库核心概述

Camelot是一款专为从PDF文件中精确提取表格数据而生的Python库,它能将PDF里的表格转换为Pandas DataFrame或CSV、JSON等格式,极大降低了PDF表格数据处理的门槛。其工作原理是通过两种核心算法(Lattice和Stream)识别表格:Lattice适用于有清晰边框线的表格,通过检测线条来定位单元格;Stream适用于无边框表格,依靠文本的位置和间距来划分单元格。

该库的优点十分突出:提取精度高、支持自定义配置、输出格式灵活、完全免费开源;缺点则是对扫描版PDF(图片型PDF)无效,仅支持文本型PDF,且对复杂嵌套表格的处理能力有限。Camelot采用MIT License开源协议,允许开发者自由使用、修改和分发,无商业使用限制。

二、Camelot库安装步骤

Camelot的安装分为基础安装和依赖补充两个部分,因为它依赖于Ghostscript等第三方工具,不同操作系统的安装流程略有差异,以下是详细的安装指南。

2.1 安装Ghostscript依赖

Ghostscript是Camelot识别PDF表格的核心依赖,必须优先安装。

  1. Windows系统
    访问Ghostscript官方下载地址(https://www.ghostscript.com/releases/gsdnld.html),下载对应版本的安装包,按照安装向导完成安装。安装完成后,需要将Ghostscript的可执行文件路径添加到系统环境变量中,例如默认路径为C:\Program Files\gs\gs10.02.1\bin
  2. macOS系统
    使用Homebrew包管理器执行以下命令安装:
    bash brew install ghostscript
  3. Linux系统(Ubuntu/Debian)
    执行apt-get命令安装:
    bash sudo apt-get install ghostscript

2.2 安装Camelot库

完成Ghostscript安装后,通过pip命令即可安装Camelot库,建议使用Python3.6及以上版本:

pip install camelot-py[cv]

这里的[cv]表示安装包含OpenCV依赖的完整版,OpenCV有助于提升表格识别的准确率。安装完成后,可以在Python环境中执行以下代码验证是否安装成功:

import camelot
print(camelot.__version__)

如果代码能正常输出Camelot的版本号,说明安装成功。

三、Camelot库核心用法与代码实例

Camelot的核心操作流程是读取PDF文件→配置提取参数→提取表格→导出/处理数据,下面将详细讲解两种核心提取算法的使用方法,并结合代码实例进行演示。

3.1 核心概念:Lattice与Stream算法

在使用Camelot提取表格前,需要先明确PDF表格的类型,从而选择对应的算法:

  • Lattice算法:默认算法,适用于有明确边框线的表格,例如Excel导出的PDF表格、财务报表等。该算法通过检测表格的竖线和横线来确定单元格的边界,提取精度极高。
  • Stream算法:适用于无边框线的表格,例如纯文本排版的表格、网页导出的无框PDF表格等。该算法通过分析文本块的位置、间距和对齐方式,来推断表格的结构。

3.2 基础用法:提取单页PDF表格

首先准备一个测试用的PDF文件(例如test_table.pdf),该文件的第1页包含一个有边框的表格。下面的代码将演示如何使用Lattice算法提取该表格。

3.2.1 代码实例:Lattice算法提取有边框表格

import camelot

# 读取PDF文件,指定提取第1页的表格,使用Lattice算法
tables = camelot.read_pdf(
    'test_table.pdf',  # PDF文件路径
    pages='1',         # 指定提取的页码,支持多页如'1,3,5'或范围'1-5'
    flavor='lattice'   # 指定提取算法为lattice
)

# 打印提取到的表格数量
print(f"提取到的表格数量:{len(tables)}")

# 查看第一个表格的基本信息
print("第一个表格的基本信息:")
print(tables[0].parsing_report)  # 输出解析报告,包含精度、页数等信息

# 将表格转换为Pandas DataFrame
df = tables[0].df
print("\n表格数据(DataFrame格式):")
print(df)

# 将表格导出为CSV文件
tables[0].to_csv('extracted_table.csv')
print("\n表格已导出为extracted_table.csv")

# 将表格导出为JSON文件
tables[0].to_json('extracted_table.json')
print("表格已导出为extracted_table.json")

3.2.2 代码说明

  • camelot.read_pdf()是核心函数,用于读取PDF并提取表格,返回一个TableList对象,包含所有提取到的表格。
  • pages参数用于指定提取的页码,支持单页、多页和页码范围,例如pages='1-3'表示提取第1到3页的表格。
  • flavor参数指定算法类型,lattice为默认值,适用于有边框表格。
  • tables[0].parsing_report会输出解析报告,包含accuracy(提取精度)、whitespace(空白占比)、page(页码)等信息,精度越高说明提取效果越好。
  • tables[0].df将表格转换为Pandas DataFrame,方便后续的数据清洗和分析。
  • to_csv()to_json()方法可以将表格导出为对应的文件格式,便于分享和存储。

3.2.3 代码实例:Stream算法提取无边框表格

如果需要提取的PDF表格没有边框线,就需要使用stream算法,同时可以通过table_regions参数指定表格所在的区域,提升提取精度。

import camelot

# 读取PDF文件,使用Stream算法提取无边框表格
tables = camelot.read_pdf(
    'test_no_border_table.pdf',
    pages='1',
    flavor='stream',
    table_regions=['20, 700, 500, 300']  # 指定表格的坐标区域:x1, y1, x2, y2
)

# 输出解析报告
print("解析报告:")
print(tables[0].parsing_report)

# 查看表格数据
df = tables[0].df
print("\n无边框表格数据:")
print(df)

# 导出为Excel文件(需要安装openpyxl库)
tables[0].to_excel('extracted_no_border_table.xlsx', index=False)
print("\n无边框表格已导出为extracted_no_border_table.xlsx")

3.2.4 代码说明

  • flavor='stream'指定使用Stream算法,适用于无边框表格。
  • table_regions参数的作用是限定表格的提取区域,坐标格式为[x1, y1, x2, y2],其中(x1, y1)是区域的左上角坐标,(x2, y2)是右下角坐标。该参数可以避免PDF中的其他文本干扰表格提取,大幅提升准确率。
  • to_excel()方法可以将表格导出为Excel文件,使用前需要安装openpyxl库,执行pip install openpyxl即可。

3.3 高级用法:自定义提取参数

Camelot提供了丰富的自定义参数,用于处理复杂的PDF表格,例如合并单元格、调整列间距、过滤空白行等。下面的代码将演示如何使用这些参数优化提取效果。

3.3.1 代码实例:处理合并单元格与空白行

import camelot

# 读取包含合并单元格的PDF表格
tables = camelot.read_pdf(
    'test_merge_cells.pdf',
    pages='1',
    flavor='lattice',
    strip_text='\n',  # 去除单元格内的换行符
    suppress_stdout=True  # 抑制控制台输出的冗余信息
)

# 查看原始提取的表格数据
print("原始提取数据(含合并单元格):")
print(tables[0].df)

# 处理合并单元格:通过DataFrame的fillna方法填充合并单元格的内容
df = tables[0].df
# 向前填充空值,适用于垂直合并的单元格
df = df.fillna(method='ffill', axis=0)
# 向左填充空值,适用于水平合并的单元格
df = df.fillna(method='ffill', axis=1)

print("\n处理合并单元格后的数据:")
print(df)

# 过滤空白行:删除所有元素均为空的行
df = df.dropna(how='all')
print("\n过滤空白行后的数据:")
print(df)

3.3.2 代码说明

  • strip_text='\n'参数用于去除单元格内的换行符,使文本内容更整洁。
  • suppress_stdout=True参数可以抑制Camelot在控制台输出的冗余日志信息,让输出更简洁。
  • 合并单元格在提取后会表现为NaN值,通过Pandas的fillna()方法,使用ffill(向前填充)策略,可以快速填充合并单元格的内容,还原表格的真实结构。
  • dropna(how='all')方法用于删除所有元素均为空的行,适用于清理包含大量空白行的表格数据。

3.3.3 代码实例:提取多页PDF中的所有表格

如果PDF文件包含多个页面,且每个页面都有表格,可以通过pages='all'参数提取所有页面的表格,并批量导出。

import camelot
import os

# 创建输出目录
output_dir = 'multi_page_tables'
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# 提取多页PDF中的所有表格
tables = camelot.read_pdf(
    'multi_page_test.pdf',
    pages='all',
    flavor='lattice'
)

print(f"共提取到 {len(tables)} 个表格")

# 批量导出所有表格为CSV文件
for i, table in enumerate(tables):
    table.to_csv(os.path.join(output_dir, f'table_{i+1}.csv'))
    print(f"表格 {i+1} 已导出到 {output_dir}/table_{i+1}.csv")

3.3.4 代码说明

  • pages='all'参数表示提取PDF文件中所有页面的表格,无需手动指定页码。
  • 通过enumerate()遍历TableList对象中的每个表格,批量导出为CSV文件,并按序号命名,方便管理。
  • 使用os.makedirs()创建输出目录,避免因目录不存在导致的导出失败。

四、实际应用案例:PDF财务报表数据提取与分析

下面将结合一个实际的应用场景——提取PDF格式的财务报表中的利润表数据,并进行简单的数据分析,演示Camelot库在实际工作中的使用价值。

4.1 案例背景

假设我们有一份名为2024_financial_report.pdf的PDF文件,其中第3页是公司的利润表,表格为有边框格式,包含“项目”“2023年”“2024年”三列数据。我们需要提取该表格数据,并分析2024年相较于2023年的营收变化情况。

4.2 代码实例:数据提取与分析

import camelot
import pandas as pd
import matplotlib.pyplot as plt

# 步骤1:提取PDF中的利润表数据
tables = camelot.read_pdf(
    '2024_financial_report.pdf',
    pages='3',
    flavor='lattice',
    strip_text='\n'
)

# 步骤2:转换为DataFrame并清洗数据
profit_df = tables[0].df
# 设置列名:假设表格第一行是表头
profit_df.columns = profit_df.iloc[0]
profit_df = profit_df.drop(0, axis=0)
# 重置索引
profit_df = profit_df.reset_index(drop=True)
# 过滤掉空行
profit_df = profit_df.dropna(how='all')

print("清洗后的利润表数据:")
print(profit_df)

# 步骤3:数据类型转换(将金额列转换为数值类型)
# 假设金额列名为“2023年”和“2024年”
profit_df['2023年'] = pd.to_numeric(profit_df['2023年'], errors='coerce')
profit_df['2024年'] = pd.to_numeric(profit_df['2024年'], errors='coerce')

# 步骤4:分析营收变化——筛选“营业收入”行
revenue_row = profit_df[profit_df['项目'].str.contains('营业收入', na=False)]
if not revenue_row.empty:
    revenue_2023 = revenue_row['2023年'].values[0]
    revenue_2024 = revenue_row['2024年'].values[0]
    revenue_growth = (revenue_2024 - revenue_2023) / revenue_2023 * 100

    print(f"\n2023年营业收入:{revenue_2023:.2f} 万元")
    print(f"2024年营业收入:{revenue_2024:.2f} 万元")
    print(f"营业收入增长率:{revenue_growth:.2f}%")

    # 步骤5:可视化营收变化
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 解决中文显示问题
    plt.figure(figsize=(8, 5))
    years = ['2023年', '2024年']
    revenues = [revenue_2023, revenue_2024]
    plt.bar(years, revenues, color=['#3498db', '#e74c3c'])
    plt.title('2023-2024年营业收入对比')
    plt.ylabel('营业收入(万元)')
    for i, v in enumerate(revenues):
        plt.text(i, v + 100, f'{v:.2f}', ha='center')
    plt.savefig('revenue_comparison.png', dpi=300, bbox_inches='tight')
    plt.show()
else:
    print("\n未找到营业收入相关数据")

4.3 案例说明

  1. 数据提取:使用Lattice算法提取PDF第3页的利润表数据,通过strip_text='\n'清理单元格内的换行符。
  2. 数据清洗:将表格的第一行设为列名,删除表头行和空行,确保数据结构整洁。
  3. 类型转换:将金额列从字符串类型转换为数值类型,以便进行数学计算,errors='coerce'参数可以将无法转换的值设为NaN
  4. 数据分析:通过筛选包含“营业收入”的行,计算2024年相较于2023年的营收增长率。
  5. 数据可视化:使用Matplotlib绘制柱状图,直观展示两年的营业收入对比情况,并解决中文显示问题。

这个案例充分体现了Camelot库在实际工作中的价值——从PDF中快速提取结构化数据,结合Pandas和Matplotlib完成数据分析与可视化,大大提升了工作效率。

五、Camelot库常见问题与解决方案

在使用Camelot的过程中,可能会遇到一些常见问题,下面列出了这些问题的解决方案:

  1. 问题1:提取到的表格为空或不完整
    • 解决方案:检查PDF是否为文本型PDF(扫描版PDF无法提取);使用table_regions参数指定表格区域;尝试切换latticestream算法;调整edge_tol参数(边缘容差)或row_tol参数(行容差)。
  2. 问题2:报错“Ghostscript not found”
    • 解决方案:确认Ghostscript已正确安装,并将其路径添加到系统环境变量中;重启Python环境后重试。
  3. 问题3:合并单元格处理不彻底
    • 解决方案:提取数据后,使用Pandas的fillna()方法手动填充NaN值;对于复杂的合并单元格,可以结合df.replace()方法进行处理。
  4. 问题4:多页PDF提取效率低
    • 解决方案:避免使用pages='all'提取不必要的页面,手动指定需要提取的页码;关闭suppress_stdout=False查看详细日志,定位耗时较长的页面。

六、相关资源链接

  • Pypi地址:https://pypi.org/project/camelot-py
  • Github地址:https://github.com/camelot-dev/camelot
  • 官方文档地址:https://camelot-py.readthedocs.io/en/master/

关注我,每天分享一个实用的Python自动化工具。