在金融、财务分析场景中,我们经常需要从上市公司财报 PDF 中提取关键财务指标(如净利润、资产负债率、现金流净额等),手动提取效率极低且易出错。本文将分享一套完整的 Python 解决方案,实现批量读取 PDF 财报→提取指定财务指标→数据清洗→计算派生指标→导出 Excel 的全流程自动化。

一、实现思路

1.环境准备:安装 PDF 解析、数据处理、Excel 导出相关依赖包;

2.路径配置:定位 PDF 财报存放目录,过滤非 PDF 文件;

3.指标定义:梳理需提取的资产负债表、现金流量表、利润表核心指标;

4.核心提取:编写函数逐页扫描 PDF,定位指标并清洗数值;

5.批量处理:遍历所有 PDF 文件和指标,汇总提取结果;

6.派生计算:基于基础指标计算派生指标(如资产负债率);

7.结果导出:将结构化数据保存为 Excel,方便后续分析。

二、环境搭建

首先安装所需依赖包

pip install PyPDF2 pandas openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple
  • PyPDF2:用于读取 PDF 文件并提取文本;
  • pandas:用于数据结构化处理和派生指标计算;
  • openpyxl:用于将 DataFrame 导出为 Excel 文件。

三、完整代码实现

3.1 导入核心包

import PyPDF2
import os
import pandas as pd
import re  

3.2 基础路径配置

pwd = os.getcwd()
PDF_pth = pwd + '\\' + 'PDFfile'  
PDFname = os.listdir(PDF_pth)     

3.3 定义需提取的财务指标

# 1. 资产负债表核心指标
field1 = ['货币资金','应收账款','存货','交易性金融资产',
          '在建工程','资产总计','应付账款','预收款项','负债合计',
          '合同负债','短期借款','未分配利润','所有者权益合计']

# 2. 现金流量表核心指标
field2 = ['现金及现金等价物净增加额','经营活动产生的现金流量净额','销售商品、提供劳务收到的现金',
          '投资活动产生的现金流量净额','投资支付的现金','收回投资收到的现金',
          '取得借款收到的现金','偿还债务支付的现金','筹资活动产生的现金流量净额']

# 3. 利润表核心指标(兼容不同财报的指标命名差异)
field3 = ['归属于母公司所有者的净利润','归属于母公司股东的净利润(净亏损以“—”号填列)',
          '归属于母公司股东的净利润(净亏损以“-”号填列)','归属于母公司股东的净利润',
          '营业外收入','营业总收入','营业成本','销售费用','管理费用',
          '财务费用','研发费用','税金及附加','营业总成本','营业利润(亏损以“-”号填列)',
          '营业利润(亏损以“-”号填列)','利润总额(亏损总额以“-”号填列)',
          '利润总额(亏损总额以“-”号填列)','资产减值损失(损失以“-”号填列)',
          '资产减值损失(损失以“-”号填列)','信用减值损失(损失以“-”号填列)',
          '信用减值损失(损失以“-”号填列)','营业收入增长率']

# 合并所有指标并去重(避免重复提取)
all_fields = list(set(field1 + field2 + field3))

3.4 核心提取函数

def extract_financial_indicator(pdf_path, indicator):
    """
    从单个PDF文件中提取指定财务指标的数值
    :param pdf_path: PDF文件完整路径
    :param indicator: 需提取的财务指标名称
    :return: 清洗后的指标数值(字符串/None)
    """
    try:
        with open(pdf_path, 'rb') as file:
            reader = PyPDF2.PdfReader(file)
            num_pages = len(reader.pages)
            
            # 逐页扫描PDF文本
            for page_num in range(num_pages):
                page_text = reader.pages[page_num].extract_text()
                # 统一符号(全角减号转半角)、统一大小写,提升匹配容错率
                page_text = page_text.replace('-', '-').replace('—', '-').lower()
                indicator_lower = indicator.lower().replace('-', '-').replace('—', '-')
                
                # 定位指标位置
                indicator_index = page_text.find(indicator_lower)
                if indicator_index != -1:
                    # 提取指标后50个字符(覆盖数值区域)
                    value_str = page_text[indicator_index + len(indicator_lower): indicator_index + 50]
                    # 数据清洗:仅保留数字、小数点、负号
                    value_str = re.sub(r'[^\d\-.]', '', value_str)
                    
                    # 校验数值有效性
                    if value_str:
                        # 截取合理长度(避免冗余字符)
                        clean_value = value_str[:6] if value_str[0] == '-' else value_str[:5]
                        # 验证是否为有效数值
                        try:
                            float(clean_value)
                            return clean_value
                        except ValueError:
                            continue
            return None  # 未找到指标
    except Exception as e:
        print(f"读取{pdf_path}时出错:{e}")
        return None

3.5 批量提取所有指标

result_dict = {
    'PDF文件名': [],
    '财务指标': [],
    '提取数值': []
}

for pdf_name in PDFname:
    pdf_full_path = os.path.join(PDF_pth, pdf_name)  
    print(f"正在处理文件:{pdf_name}")
    
    for indicator in all_fields:
        indicator_value = extract_financial_indicator(pdf_full_path, indicator)
        if indicator_value:
            result_dict['PDF文件名'].append(pdf_name)
            result_dict['财务指标'].append(indicator)
            result_dict['提取数值'].append(indicator_value)

result_df = pd.DataFrame(result_dict)

控制台显示内容

正在处理文件:600080_20230428_FQ2V.pdf
正在处理文件:600080_20230428_MMWM.pdf
正在处理文件:600080_20230428_PCK7.pdf
正在处理文件:600080_20230819_DNUE.pdf
正在处理文件:600080_20230819_U8CH.pdf
正在处理文件:600080_20231028_F42E.pdf
正在处理文件:600080_20240427_0WKP.pdf
正在处理文件:600080_20240427_IBMB.pdf
正在处理文件:600080_20240427_W39O.pdf
正在处理文件:600080_20240817_5X5X.pdf
正在处理文件:600080_20240817_6AW9.pdf
正在处理文件:600080_20241030_XN72.pdf
正在处理文件:600080_20250425_6GSD.pdf
正在处理文件:600080_20250425_LXGH.pdf
正在处理文件:600080_20250425_P7ID.pdf
正在处理文件:600080_20250822_ABVM.pdf
正在处理文件:600080_20250822_ODWZ.pdf
正在处理文件:600080_20251030_IVCB.pdf
正在处理文件:华润三九:2022年年度报告.pdf
正在处理文件:华润三九:2022年年度报告摘要.pdf
正在处理文件:华润三九:2023年一季度报告.pdf
正在处理文件:华润三九:2023年三季度报告.pdf
正在处理文件:华润三九:2023年半年度报告.pdf
正在处理文件:华润三九:2023年半年度报告摘要.pdf
正在处理文件:华润三九:2023年年度报告.pdf
正在处理文件:华润三九:2023年年度报告摘要.pdf
正在处理文件:华润三九:2024年一季度报告.pdf
正在处理文件:华润三九:2024年三季度报告.pdf
正在处理文件:华润三九:2024年半年度报告.pdf
正在处理文件:华润三九:2024年半年度报告摘要.pdf
正在处理文件:华润三九:2024年年度报告.pdf
正在处理文件:华润三九:2024年年度报告摘要.pdf
正在处理文件:华润三九:2025年一季度报告.pdf
正在处理文件:华润三九:2025年三季度报告.pdf
正在处理文件:华润三九:2025年半年度报告.pdf
正在处理文件:华润三九:2025年半年度报告摘要.pdf

3.6 计算派生指标(以资产负债率为例)

if not result_df.empty:
    debt_total = result_df[result_df['财务指标'] == '负债合计']
    asset_total = result_df[result_df['财务指标'] == '资产总计']
    
    for pdf_name in PDFname:
        debt_val = debt_total[debt_total['PDF文件名'] == pdf_name]['提取数值'].values
        asset_val = asset_total[asset_total['PDF文件名'] == pdf_name]['提取数值'].values
        
        if len(debt_val) > 0 and len(asset_val) > 0:
            try:
                debt_ratio = round(float(debt_val[0]) / float(asset_val[0]), 4)
                result_df = pd.concat([result_df, pd.DataFrame({
                    'PDF文件名': [pdf_name],
                    '财务指标': ['资产负债率'],
                    '提取数值': [debt_ratio]
                })], ignore_index=True)
            except ZeroDivisionError:
                print(f"{pdf_name}的资产总计为0,无法计算资产负债率")

3.7 结果导出为 Excel

# 导出Excel文件
output_excel = os.path.join(pwd, '财务指标提取结果.xlsx')
result_df.to_excel(output_excel, index=False, engine='openpyxl')
print(f"\n提取完成!结果已保存至:{output_excel}")
print(f"共提取有效数据{len(result_df)}条")

四、使用说明

4.1 前置准备

在代码运行目录下创建PDFfile文件夹;

将需要提取的财报 PDF 文件放入PDFfile文件夹;

确保 PDF 文件为可读取的文本格式(非图片扫描件)。

4.2 运行代码

直接运行完整代码,控制台会输出处理进度,最终在当前目录生成财务指标提取结果.xlsx文件。

4.3 结果示例

PDF 文件名财务指标提取数值
某公司 2024 财报.pdf资产总计125890
某公司 2024 财报.pdf负债合计62945
某公司 2024 财报.pdf资产负债率0.5000
某公司 2024 财报.pdf净利润

8956

五、优化与扩展建议

  1. 提升匹配精度:可使用正则表达式模糊匹配指标(如re.search替代find),兼容更多指标命名变体;
  2. 处理图片 PDF:若财报是图片扫描件,需结合pytesseract(OCR)提取文本;
  3. 多线程加速:批量处理大量 PDF 时,使用concurrent.futures多线程并行提取;
  4. 数据校验:增加数值范围校验(如资产负债率应在 0-1 之间),过滤异常值;
  5. 更多派生指标:可扩展计算毛利率、净利率、流动比率等更多财务分析指标。

六、常见问题解决

  1. PDF 读取报错:检查 PDF 文件是否损坏、是否加密,加密 PDF 需先解密;
  2. 指标提取为空:确认 PDF 中存在该指标,或调整value_str提取长度(如从 50 改为 100)
  3. Excel 导出失败:确保openpyxl版本最新,或关闭已打开的结果 Excel 文件。

总结

  1. 本方案基于 PyPDF2+Pandas 实现了 PDF 财报财务指标的批量提取与结构化处理,核心是通过文本匹配 + 正则清洗获取有效数值;
  2. 代码具备容错性(过滤非 PDF 文件、数值校验、异常捕获),适配不同命名规范的财报指标;
  3. 支持派生指标计算和 Excel 导出,可直接应用于财务分析场景,也可根据需求扩展更多指标和功能。

通过这套代码,你可以摆脱手动提取财务数据的繁琐,大幅提升数据处理效率,将精力聚焦于财务分析本身。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐