搭建自己的金融数据源和量化分析平台(八):解析PDF财报中的资产负债表
到了最复杂也是最扯淡的部分:从pdf中解析三大报表
前面下载到了PDF格式的财报。现在根据PDF格式的财报来解析资产负债表的会计项并入库。
这里经过研究发现,诸如三大报表之类的公开数据都受困于各种pdf,无法得到很好的利用,不得不说是一种散户和某某的悲哀。
至于某花顺,某得等大公司,他们就有强大的解析能力或者直接从交易所拿到格式化的三大报表的数据吗?并不是,请看下面的一段引用的话:
“ 这些最原始的数据源主要就是各大金融市场的交易所,这些交易所绝大部分是政府机构运营管理。
它们只提供raw data,也就是最原始的数据。譬如行情数据(market data)都是数据串流,基本面数据(reference data)要么是原始的公告PDF,要么是非常非常简单的基础数据。这些数据往往无法直接使用。
所以最主流的数据供应商都有自己至少100人以上(多则有500人)的数据编辑团队,从事大量的数据加工处理工作,数据采编可以说是高科技行业中的劳动密集型。把那些raw data最终变成可供统计分析使用的结构化数据。”
因此,某花顺的财务数据也是牛马们手打的啊,这也是一种讽刺了。
但我们还是要有点梦想,尽量设计一个能够尽可能多地自动化解析财务数据的系统出来,至于解析不出来和解析错误的股票怎么办?我能想到的办法只有自己手动补全无法解析的结果,此外还是得写爬虫白嫖某花顺,将爬虫结果与解析结果作对比。(这里不得不向某花顺妥协,唉)
首先设计资产负债表的数据库字段如下:
stock_code str 股票代码
report_year str 所属年度(2023、2024等)
report_type str 报表类型(一季报、中报、三季报、年报)
comp_type str 公司类型(1一般工商业2银行3保险4证券)
total_share str 期末总股本
cap_rese str 资本公积
undistr_porfit str 未分配利润
surplus_rese str 盈余公积
special_rese str 专项储备
money_cap str 货币资金
trad_asset str 交易性金融资产
notes_receiv str 应收票据
accounts_receiv str 应收账款
oth_receiv str 其他应收款
prepayment str 预付款项
div_receiv str 应收股利
int_receiv str 应收利息
inventories str 存货
amor_exp str 待摊费用
nca_within_1y str 一年内到期的非流动资产
sett_rsrv str 结算备付金
loanto_oth_bank_fi str 拆出资金
premium_receiv str 应收保费
reinsur_receiv str 应收分保账款
reinsur_res_receiv str 应收分保合同准备金
pur_resale_fa str 买入返售金融资产
oth_cur_assets str 其他流动资产
total_cur_assets str 流动资产合计
fa_avail_for_sale str 可供出售金融资产
htm_invest str 持有至到期投资
lt_eqt_invest str 长期股权投资
invest_real_estate str 投资性房地产
time_deposits str 定期存款
oth_assets str 其他资产
lt_rec str 长期应收款
fix_assets str 固定资产
cip str 在建工程
const_materials str 工程物资
fixed_assets_disp str 固定资产清理
produc_bio_assets str 生产性生物资产
oil_and_gas_assets str 油气资产
intan_assets str 无形资产
r_and_d str 研发支出
goodwill str 商誉
lt_amor_exp str 长期待摊费用
defer_tax_assets str 递延所得税资产
decr_in_disbur str 发放贷款及垫款
oth_nca str 其他非流动资产
total_nca str 非流动资产合计
cash_reser_cb str 现金及存放中央银行款项
depos_in_oth_bfi str 存放同业和其它金融机构款项
prec_metals str 贵金属
deriv_assets str 衍生金融资产
rr_reins_une_prem str 应收分保未到期责任准备金
rr_reins_outstd_cla str 应收分保未决赔款准备金
rr_reins_lins_liab str 应收分保寿险责任准备金
rr_reins_lthins_liab str 应收分保长期健康险责任准备金
refund_depos str 存出保证金
ph_pledge_loans str 保户质押贷款
refund_cap_depos str 存出资本保证金
indep_acct_assets str 独立账户资产
client_depos str 其中:客户资金存款
client_prov str 其中:客户备付金
transac_seat_fee str 其中:交易席位费
invest_as_receiv str 应收款项类投资
total_assets str 资产总计
lt_borr str 长期借款
st_borr str 短期借款
cb_borr str 向中央银行借款
depos_ib_deposits str 吸收存款及同业存放
loan_oth_bank str 拆入资金
trading_fl str 交易性金融负债
notes_payable str 应付票据
acct_payable str 应付账款
adv_receipts str 预收款项
sold_for_repur_fa str 卖出回购金融资产款
comm_payable str 应付手续费及佣金
payroll_payable str 应付职工薪酬
taxes_payable str 应交税费
int_payable str 应付利息
div_payable str 应付股利
oth_payable str 其他应付款
acc_exp str 预提费用
deferred_inc str 递延收益
st_bonds_payable str 应付短期债券
payable_to_reinsurer str 应付分保账款
rsrv_insur_cont str 保险合同准备金
acting_trading_sec str 代理买卖证券款
acting_uw_sec str 代理承销证券款
non_cur_liab_due_1y str 一年内到期的非流动负债
oth_cur_liab str 其他流动负债
total_cur_liab str 流动负债合计
bond_payable str 应付债券
lt_payable str 长期应付款
specific_payables str 专项应付款
estimated_liab str 预计负债
defer_tax_liab str 递延所得税负债
defer_inc_non_cur_liab str 递延收益-非流动负债
oth_ncl str 其他非流动负债
total_ncl str 非流动负债合计
depos_oth_bfi str 同业和其它金融机构存放款项
deriv_liab str 衍生金融负债
depos str 吸收存款
agency_bus_liab str 代理业务负债
oth_liab str 其他负债
prem_receiv_adva str 预收保费
depos_received str 存入保证金
ph_invest str 保户储金及投资款
reser_une_prem str 未到期责任准备金
reser_outstd_claims str 未决赔款准备金
reser_lins_liab str 寿险责任准备金
reser_lthins_liab str 长期健康险责任准备金
indept_acc_liab str 独立账户负债
pledge_borr str 其中:质押借款
indem_payable str 应付赔付款
policy_div_payable str 应付保单红利
total_liab str 负债合计
treasury_share str 减:库存股
ordin_risk_reser str 一般风险准备
forex_differ str 外币报表折算差额
invest_loss_unconf str 未确认的投资损失
minority_int str 少数股东权益
total_hldr_eqy_exc_min_int str 股东权益合计(不含少数股东权益)
total_hldr_eqy_inc_min_int str 股东权益合计(含少数股东权益)
total_liab_hldr_eqy str 负债及股东权益总计
lt_payroll_payable str 长期应付职工薪酬
oth_comp_income str 其他综合收益
oth_eqt_tools str 其他权益工具
oth_eqt_tools_p_shr str 其他权益工具(优先股)
lending_funds str 融出资金
acc_receivable str 应收款项
st_fin_payable str 应付短期融资款
payables str 应付款项
hfs_assets str 持有待售的资产
hfs_sales str 持有待售的负债
cost_fin_assets str 以摊余成本计量的金融资产
fair_value_fin_assets str 以公允价值计量且其变动计入其他综合收益的金融资产
cip_total str 在建工程(合计)(元)
oth_pay_total str 其他应付款(合计)(元)
long_pay_total str 长期应付款(合计)(元)
debt_invest str 债权投资(元)
oth_debt_invest str 其他债权投资(元)
oth_eq_invest str 其他权益工具投资(元)
oth_illiq_fin_assets str 其他非流动金融资产(元)
oth_eq_ppbond str 其他权益工具:永续债(元)
receiv_financing str 应收款项融资
use_right_assets str 使用权资产
lease_liab str 租赁负债
contract_assets str 合同资产
contract_liab str 合同负债
accounts_receiv_bill str 应收票据及应收账款
accounts_pay str 应付票据及应付账款
oth_rcv_total str 其他应收款(合计)(元)
fix_assets_total str 固定资产(合计)(元)
然后上解析pdf的代码(该代码尚不完善,后期完善了也不会再在博客更新这部分代码,CSDN不配):
# -*- coding: utf-8 -*-
# 自定义异常、格式转换等工具类
import pdfplumber
import os
import spark
PUBLIC_CORPORATION = 1 # 普通公司
BANK = 2 # 银行
INSURANCE = 3 # 保险
BROKER = 4 # 券商
MONEY_CAP = "货币资金"
monetary_unit_Y = 1
'''货币单位元'''
monetary_unit_T = 1000
'''货币单位千元'''
monetary_unit_10T = 10000
'''货币单位万元'''
monetary_unit_100T = 100000
'''货币单位十万元'''
monetary_unit_M = 1000000
'''货币单位百万元'''
monetary_unit_100M = 10000000
'''货币单位千万元'''
monetary_unit_1000M = 100000000
'''货币单位亿元'''
def check_mode(text,head):
'''根据给定的text字符串判断该字符串是否是以head子串开始,中间间隔空格,末尾以数字结束'''
head_withblank = text[0]
for i in range(1,len(text)-1):
head_withblank = head_withblank+' '+text[i]
head_withblank = head_withblank+' '+text[len(text)-1]
if text.startswith(' '+head) or text.startswith(head) or text.startswith(head_withblank):
return text
else:
return None
def analysis_balance_sheet(file_path,stock_code,comp_type):
'''解析财报数据获取资产负债表内容'''
# 处理单份财报
# 定义资产负债表在财报中的索引
possible_sheet_index = []
with pdfplumber.open(file_path) as pdf:
for i, page in enumerate(pdf.pages):
# 记录当前页数
page_number = i+1
text = page.extract_text()
for mid in text.split("\n"):
if mid.find(MONEY_CAP) >= 0 and mid.find(MONEY_CAP) <= 1:
possible_sheet_index.append(page_number)
pdf.close()
# 资产负债表起始页命中标志
hit_flag = False
# 资产负债表起始页索引
sheet_index = []
# 货币单位标志
monetary_unit = None
for page_number in possible_sheet_index:
# 读取当前页和前一页,检查合并资产负债表的存在
with pdfplumber.open(path_or_fp=file_path, pages=[page_number]) as pdf:
text = pdf.pages[0].extract_text()
if text.find("合") >= 0 and text.find("并") >= 0 and text.find("资") >= 0 and text.find("产") >= 0 and text.find("负") >= 0 and text.find("债") >= 0 and text.find("表") >= 0 and text.find("单") >= 0 and text.find("位") >= 0 and text.find("元") >= 0:
hit_flag = True
sheet_index.append(page_number)
monetary_unit = analysis_money_unit(text)
else:
pdf.close()
if hit_flag is False:
with pdfplumber.open(path_or_fp=file_path, pages=[page_number-1]) as pdf:
text = pdf.pages[0].extract_text()
if text.find("合") >= 0 and text.find("并") >= 0 and text.find("资") >= 0 and text.find("产") >= 0 and text.find("负") >= 0 and text.find("债") >= 0 and text.find("表") >= 0 and text.find("单") >= 0 and text.find("位") >= 0 and text.find("元") >= 0:
hit_flag = True
sheet_index.append(page_number)
monetary_unit = analysis_money_unit(text)
else:
pdf.close()
if hit_flag is False:
print('股票代码:'+'检索失败,请手动检查')
else:
if monetary_unit == None:
print("货币单位标志解析失败,请手动补全")
else:
# 总结果集,单位统一为人民币元
balance_sheet = {}
# 解析货币资金
with pdfplumber.open(path_or_fp=file_path, pages=sheet_index) as pdf:
text = pdf.pages[0].extract_text()
for line in text.split("\n"):
print(line)
# money_cap = check_mode(line, MONEY_CAP)
# if money_cap is not None:
# # balance_sheet["money_cap"] = money_cap * monetary_unit
# print(money_cap)
# break
# print(balance_sheet)