Python 批量提取 PDF 财报财务指标并导出 Excel
在金融、财务分析场景中,我们经常需要从上市公司财报 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
五、优化与扩展建议
- 提升匹配精度:可使用正则表达式模糊匹配指标(如
re.search替代find),兼容更多指标命名变体; - 处理图片 PDF:若财报是图片扫描件,需结合
pytesseract(OCR)提取文本; - 多线程加速:批量处理大量 PDF 时,使用
concurrent.futures多线程并行提取; - 数据校验:增加数值范围校验(如资产负债率应在 0-1 之间),过滤异常值;
- 更多派生指标:可扩展计算毛利率、净利率、流动比率等更多财务分析指标。
六、常见问题解决
- PDF 读取报错:检查 PDF 文件是否损坏、是否加密,加密 PDF 需先解密;
- 指标提取为空:确认 PDF 中存在该指标,或调整
value_str提取长度(如从 50 改为 100) - Excel 导出失败:确保
openpyxl版本最新,或关闭已打开的结果 Excel 文件。
总结
- 本方案基于 PyPDF2+Pandas 实现了 PDF 财报财务指标的批量提取与结构化处理,核心是通过文本匹配 + 正则清洗获取有效数值;
- 代码具备容错性(过滤非 PDF 文件、数值校验、异常捕获),适配不同命名规范的财报指标;
- 支持派生指标计算和 Excel 导出,可直接应用于财务分析场景,也可根据需求扩展更多指标和功能。
通过这套代码,你可以摆脱手动提取财务数据的繁琐,大幅提升数据处理效率,将精力聚焦于财务分析本身。
更多推荐





所有评论(0)