Python自动化小技巧28——批量获取上市公司财务数据(利润表、现金表、资产负债表)

背景

上次做的这个程序:

Python自动获取指定上市公司的所有财务数据(资产负债表,利润表,现金流量表)

很受欢迎,我知道很多经管类的同学需要获取财务数据,但是我没想到他们居然这么高频的需求,并且这么低的动手能力...........而且,但是吧,上次的tushare库的api_key过期了,不能用了。

并且上次做的程序也很简陋,也不符合用户的体验。

我这次全新升级,用户不需要再弄一堆乱七八糟的txt文件,只需要双击一个exe就能有完好的UI界面,输入自己想要的股票的名称,就能够查询到所有的上市公司数据,并且还能够计算一些常见的财务指标。

代码还是开源!但我知道很多同学看了代码也不会写,写了也不会配环境去用,就算都会,自己账号也没权限获取数据。(要获取财务数据需要充两百块钱)

我不可能公开我个人账号的api_key给大家用,但是我可以嵌入到 我用的exe应用里面去给大家用。

下载链接:财务数据获取接口

使用很简单,双击打开,输入要下载的股票代码

点击下载就好了。

就会存在本地excel文件,打开就是这样的:

想要多长时间就有多长时间,想要季度就有季度,想要年度就有年度。

还可以计算财务指标:

使用起来非常简洁和方便。

如果会Python,想自己动手的同学,想学是怎么做这个程序的,下面这次教大家怎么构建这样一个程序,从写代码,到构建为exe程序都有。但是重点在exe上面,因为上一篇文章已经很详细的写了这个获取财务数据的代码思路,所以说本次直接给代码就行了,不详细讲每一行代码是啥意思了。想学习tushare获取还是看这篇文章:

Python自动获取指定上市公司的所有财务数据(资产负债表,利润表,现金流量表)

代码实现

都是做数据科学的,做成应用web前端一个UI界面当然不擅长,所以这里就用streamlit包,来提供一个浏览器界面,方便用户使用。

streamlit代码如下,包含了获取财务数据的全流程:


# 定义一个空字典来存储变量名称和描述
variables_dict = {
    "ts_code": "TS股票代码",
    "ann_date": "公告日期",
    "f_ann_date": "实际公告日期",
    "end_date": "报告期",
    "report_type": "报告类型 见底部表",
    "comp_type": "公司类型(1一般工商业2银行3保险4证券)",
    "end_type": "报告期类型",
    "basic_eps": "基本每股收益",
    "diluted_eps": "稀释每股收益",
    "total_revenue": "营业总收入",
    "revenue": "营业收入",
    "int_income": "利息收入",
    "prem_earned": "已赚保费",
    "comm_income": "手续费及佣金收入",
    "n_commis_income": "手续费及佣金净收入",
    "n_oth_income": "其他经营净收益",
    "n_oth_b_income": "加:其他业务净收益",
    "prem_income": "保险业务收入",
    "out_prem": "减:分出保费",
    "une_prem_reser": "提取未到期责任准备金",
    "reins_income": "其中:分保费收入",
    "n_sec_tb_income": "代理买卖证券业务净收入",
    "n_sec_uw_income": "证券承销业务净收入",
    "n_asset_mg_income": "受托客户资产管理业务净收入",
    "oth_b_income": "其他业务收入",
    "fv_value_chg_gain": "加:公允价值变动净收益",
    "invest_income": "加:投资净收益",
    "ass_invest_income": "其中:对联营企业和合营企业的投资收益",
    "forex_gain": "加:汇兑净收益",
    "total_cogs": "营业总成本",
    "oper_cost": "减:营业成本",
    "int_exp": "减:利息支出",
    "comm_exp": "减:手续费及佣金支出",
    "biz_tax_surchg": "减:营业税金及附加",
    "sell_exp": "减:销售费用",
    "admin_exp": "减:管理费用",
    "fin_exp": "减:财务费用",
    "assets_impair_loss": "减:资产减值损失",
    "prem_refund": "退保金",
    "compens_payout": "赔付总支出",
    "reser_insur_liab": "提取保险责任准备金",
    "div_payt": "保户红利支出",
    "reins_exp": "分保费用",
    "oper_exp": "营业支出",
    "compens_payout_refu": "减:摊回赔付支出",
    "insur_reser_refu": "减:摊回保险责任准备金",
    "reins_cost_refund": "减:摊回分保费用",
    "other_bus_cost": "其他业务成本",
    "operate_profit": "营业利润",
    "non_oper_income": "加:营业外收入",
    "non_oper_exp": "减:营业外支出",
    "nca_disploss": "其中:减:非流动资产处置净损失",
    "total_profit": "利润总额",
    "income_tax": "所得税费用",
    "n_income": "净利润(含少数股东损益)",
    "n_income_attr_p": "净利润(不含少数股东损益)",
    "minority_gain": "少数股东损益",
    "oth_compr_income": "其他综合收益",
    "t_compr_income": "综合收益总额",
    "compr_inc_attr_p": "归属于母公司(或股东)的综合收益总额",
    "compr_inc_attr_m_s": "归属于少数股东的综合收益总额",
    "ebit": "息税前利润",
    "ebitda": "息税折旧摊销前利润",
    "insurance_exp": "保险业务支出",
    "undist_profit": "年初未分配利润",
    "distable_profit": "可分配利润",
    "rd_exp": "研发费用",
    "fin_exp_int_exp": "财务费用:利息费用",
    "fin_exp_int_inc": "财务费用:利息收入",
    "transfer_surplus_rese": "盈余公积转入",
    "transfer_housing_imprest": "住房周转金转入",
    "transfer_oth": "其他转入",
    "adj_lossgain": "调整以前年度损益",
    "withdra_legal_surplus": "提取法定盈余公积",
    "withdra_legal_pubfund": "提取法定公益金",
    "withdra_biz_devfund": "提取企业发展基金",
    "withdra_rese_fund": "提取储备基金",
    "withdra_oth_ersu": "提取任意盈余公积金",
    "workers_welfare": "职工奖金福利",
    "distr_profit_shrhder": "可供股东分配的利润",
    "prfshare_payable_dvd": "应付优先股股利",
    "comshare_payable_dvd": "应付普通股股利",
    "capit_comstock_div": "转作股本的普通股股利",
    "net_after_nr_lp_correct": "扣除非经常性损益后的净利润(更正前)",
    "credit_impa_loss": "信用减值损失",
    "net_expo_hedging_benefits": "净敞口套期收益",
    "oth_impair_loss_assets": "其他资产减值损失",
    "total_opcost": "营业总成本(二)",
    "amodcost_fin_assets": "以摊余成本计量的金融资产终止确认收益",
    "oth_income": "其他收益",
    "asset_disp_income": "资产处置收益",
    "continued_net_profit": "持续经营净利润",
    "end_net_profit": "终止经营净利润",
    "update_flag": "更新标识"
}


# In[11]:


# 定义一个空字典来存储变量名称和描述
variables_dict_2 = {
    "ts_code": "TS股票代码",
    "ann_date": "公告日期",
    "f_ann_date": "实际公告日期",
    "end_date": "报告期",
    "report_type": "报表类型",
    "comp_type": "公司类型(1一般工商业2银行3保险4证券)",
    "end_type": "报告期类型",
    "total_share": "期末总股本",
    "cap_rese": "资本公积金",
    "undistr_porfit": "未分配利润",
    "surplus_rese": "盈余公积金",
    "special_rese": "专项储备",
    "money_cap": "货币资金",
    "trad_asset": "交易性金融资产",
    "notes_receiv": "应收票据",
    "accounts_receiv": "应收账款",
    "oth_receiv": "其他应收款",
    "prepayment": "预付款项",
    "div_receiv": "应收股利",
    "int_receiv": "应收利息",
    "inventories": "存货",
    "amor_exp": "待摊费用",
    "nca_within_1y": "一年内到期的非流动资产",
    "sett_rsrv": "结算备付金",
    "loanto_oth_bank_fi": "拆出资金",
    "premium_receiv": "应收保费",
    "reinsur_receiv": "应收分保账款",
    "reinsur_res_receiv": "应收分保合同准备金",
    "pur_resale_fa": "买入返售金融资产",
    "oth_cur_assets": "其他流动资产",
    "total_cur_assets": "流动资产合计",
    "fa_avail_for_sale": "可供出售金融资产",
    "htm_invest": "持有至到期投资",
    "lt_eqt_invest": "长期股权投资",
    "invest_real_estate": "投资性房地产",
    "time_deposits": "定期存款",
    "oth_assets": "其他资产",
    "lt_rec": "长期应收款",
    "fix_assets": "固定资产",
    "cip": "在建工程",
    "const_materials": "工程物资",
    "fixed_assets_disp": "固定资产清理",
    "produc_bio_assets": "生产性生物资产",
    "oil_and_gas_assets": "油气资产",
    "intan_assets": "无形资产",
    "r_and_d": "研发支出",
    "goodwill": "商誉",
    "lt_amor_exp": "长期待摊费用",
    "defer_tax_assets": "递延所得税资产",
    "decr_in_disbur": "发放贷款及垫款",
    "oth_nca": "其他非流动资产",
    "total_nca": "非流动资产合计",
    "cash_reser_cb": "现金及存放中央银行款项",
    "depos_in_oth_bfi": "存放同业和其它金融机构款项",
    "prec_metals": "贵金属",
    "deriv_assets": "衍生金融资产",
    "rr_reins_une_prem": "应收分保未到期责任准备金",
    "rr_reins_outstd_cla": "应收分保未决赔款准备金",
    "rr_reins_lins_liab": "应收分保寿险责任准备金",
    "rr_reins_lthins_liab": "应收分保长期健康险责任准备金",
    "refund_depos": "存出保证金",
    "ph_pledge_loans": "保户质押贷款",
    "refund_cap_depos": "存出资本保证金",
    "indep_acct_assets": "独立账户资产",
    "client_depos": "其中:客户资金存款",
    "client_prov": "其中:客户备付金",
    "transac_seat_fee": "其中:交易席位费",
    "invest_as_receiv": "应收款项类投资",
    "total_assets": "资产总计",
    "lt_borr": "长期借款",
    "st_borr": "短期借款",
    "cb_borr": "向中央银行借款",
    "depos_ib_deposits": "吸收存款及同业存放",
    "loan_oth_bank": "拆入资金",
    "trading_fl": "交易性金融负债",
    "notes_payable": "应付票据",
    "acct_payable": "应付账款",
    "adv_receipts": "预收款项",
    "sold_for_repur_fa": "卖出回购金融资产款",
    "comm_payable": "应付手续费及佣金",
    "payroll_payable": "应付职工薪酬",
    "taxes_payable": "应交税费",
    "int_payable": "应付利息",
    "div_payable": "应付股利",
    "oth_payable": "其他应付款",
    "acc_exp": "预提费用",
    "deferred_inc": "递延收益",
    "st_bonds_payable": "应付短期债券",
    "payable_to_reinsurer": "应付分保账款",
    "rsrv_insur_cont": "保险合同准备金",
    "acting_trading_sec": "代理买卖证券款",
    "acting_uw_sec": "代理承销证券款",
    "non_cur_liab_due_1y": "一年内到期的非流动负债",
    "oth_cur_liab": "其他流动负债",
    "total_cur_liab": "流动负债合计",
    "bond_payable": "应付债券",
    "lt_payable": "长期应付款",
    "specific_payables": "专项应付款",
    "estimated_liab": "预计负债",
    "defer_tax_liab": "递延所得税负债",
    "defer_inc_non_cur_liab": "递延收益-非流动负债",
    "oth_ncl": "其他非流动负债",
    "total_ncl": "非流动负债合计",
    "depos_oth_bfi": "同业和其它金融机构存放款项",
    "deriv_liab": "衍生金融负债",
    "depos": "吸收存款",
    "agency_bus_liab": "代理业务负债",
    "oth_liab": "其他负债",
    "prem_receiv_adva": "预收保费",
    "depos_received": "存入保证金",
    "ph_invest": "保户储金及投资款",
    "reser_une_prem": "未到期责任准备金",
    "reser_outstd_claims": "未决赔款准备金",
    "reser_lins_liab": "寿险责任准备金",
    "reser_lthins_liab": "长期健康险责任准备金",
    "indept_acc_liab": "独立账户负债",
    "pledge_borr": "其中:质押借款",
    "indem_payable": "应付赔付款",
    "policy_div_payable": "应付保单红利",
    "total_liab": "负债合计",
    "treasury_share": "减:库存股",
    "ordin_risk_reser": "一般风险准备",
    "forex_differ": "外币报表折算差额",
    "invest_loss_unconf": "未确认的投资损失",
    "minority_int": "少数股东权益",
    "total_hldr_eqy_exc_min_int": "股东权益合计(不含少数股东权益)",
    "total_hldr_eqy_inc_min_int": "股东权益合计(含少数股东权益)",
    "total_liab_hldr_eqy": "负债及股东权益总计",
    "lt_payroll_payable": "长期应付职工薪酬",
    "oth_comp_income": "其他综合收益",
    "oth_eqt_tools": "其他权益工具",
    "oth_eqt_tools_p_shr": "其他权益工具(优先股)",
    "lending_funds": "融出资金",
    "acc_receivable": "应收款项",
    "st_fin_payable": "应付短期融资款",
    "payables": "应付款项",
    "hfs_assets": "持有待售的资产",
    "hfs_sales": "持有待售的负债",
    "cost_fin_assets": "以摊余成本计量的金融资产",
    "fair_value_fin_assets": "以公允价值计量且其变动计入其他综合收益的金融资产",
    "cip_total": "在建工程(合计)(元)",
    "oth_pay_total": "其他应付款(合计)(元)",
    "long_pay_total": "长期应付款(合计)(元)",
    "debt_invest": "债权投资(元)",
    "oth_debt_invest": "其他债权投资(元)",
    "oth_eq_invest": "其他权益工具投资(元)",
    "oth_illiq_fin_assets": "其他非流动金融资产(元)",
    "oth_eq_ppbond": "其他权益工具:永续债(元)",
    "receiv_financing": "应收款项融资",
    "use_right_assets": "使用权资产",
    "lease_liab": "租赁负债",
    "contract_assets": "合同资产",
    "contract_liab": "合同负债",
    "accounts_receiv_bill": "应收票据及应收账款",
    "accounts_pay": "应付票据及应付账款",
    "oth_rcv_total": "其他应收款(合计)(元)",
    "fix_assets_total": "固定资产(合计)(元)",
    "update_flag": "更新标识"
}


# In[12]:


# 定义一个空字典来存储变量名称和描述
variables_dict_3 = {
    "ts_code": "TS股票代码",
    "ann_date": "公告日期",
    "f_ann_date": "实际公告日期",
    "end_date": "报告期",
    "comp_type": "公司类型(1一般工商业2银行3保险4证券)",
    "report_type": "报表类型",
    "end_type": "报告期类型",
    "net_profit": "净利润",
    "finan_exp": "财务费用",
    "c_fr_sale_sg": "销售商品、提供劳务收到的现金",
    "recp_tax_rends": "收到的税费返还",
    "n_depos_incr_fi": "客户存款和同业存放款项净增加额",
    "n_incr_loans_cb": "向中央银行借款净增加额",
    "n_inc_borr_oth_fi": "向其他金融机构拆入资金净增加额",
    "prem_fr_orig_contr": "收到原保险合同保费取得的现金",
    "n_incr_insured_dep": "保户储金净增加额",
    "n_reinsur_prem": "收到再保业务现金净额",
    "n_incr_disp_tfa": "处置交易性金融资产净增加额",
    "ifc_cash_incr": "收取利息和手续费净增加额",
    "n_incr_disp_faas": "处置可供出售金融资产净增加额",
    "n_incr_loans_oth_bank": "拆入资金净增加额",
    "n_cap_incr_repur": "回购业务资金净增加额",
    "c_fr_oth_operate_a": "收到其他与经营活动有关的现金",
    "c_inf_fr_operate_a": "经营活动现金流入小计",
    "c_paid_goods_s": "购买商品、接受劳务支付的现金",
    "c_paid_to_for_empl": "支付给职工以及为职工支付的现金",
    "c_paid_for_taxes": "支付的各项税费",
    "n_incr_clt_loan_adv": "客户贷款及垫款净增加额",
    "n_incr_dep_cbob": "存放央行和同业款项净增加额",
    "c_pay_claims_orig_inco": "支付原保险合同赔付款项的现金",
    "pay_handling_chrg": "支付手续费的现金",
    "pay_comm_insur_plcy": "支付保单红利的现金",
    "oth_cash_pay_oper_act": "支付其他与经营活动有关的现金",
    "st_cash_out_act": "经营活动现金流出小计",
    "n_cashflow_act": "经营活动产生的现金流量净额",
    "oth_recp_ral_inv_act": "收到其他与投资活动有关的现金",
    "c_disp_withdrwl_invest": "收回投资收到的现金",
    "c_recp_return_invest": "取得投资收益收到的现金",
    "n_recp_disp_fiolta": "处置固定资产、无形资产和其他长期资产收回的现金净额",
    "n_recp_disp_sobu": "处置子公司及其他营业单位收到的现金净额",
    "stot_inflows_inv_act": "投资活动现金流入小计",
    "c_pay_acq_const_fiolta": "购建固定资产、无形资产和其他长期资产支付的现金",
    "c_paid_invest": "投资支付的现金",
    "n_disp_subs_oth_biz": "取得子公司及其他营业单位支付的现金净额",
    "oth_pay_ral_inv_act": "支付其他与投资活动有关的现金",
    "n_incr_pledge_loan": "质押贷款净增加额",
    "stot_out_inv_act": "投资活动现金流出小计",
    "n_cashflow_inv_act": "投资活动产生的现金流量净额",
    "c_recp_borrow": "取得借款收到的现金",
    "proc_issue_bonds": "发行债券收到的现金",
    "oth_cash_recp_ral_fnc_act": "收到其他与筹资活动有关的现金",
    "stot_cash_in_fnc_act": "筹资活动现金流入小计",
    "free_cashflow": "企业自由现金流量",
    "c_prepay_amt_borr": "偿还债务支付的现金",
    "c_pay_dist_dpcp_int_exp": "分配股利、利润或偿付利息支付的现金",
    "incl_dvd_profit_paid_sc_ms": "其中:子公司支付给少数股东的股利、利润",
    "oth_cashpay_ral_fnc_act": "支付其他与筹资活动有关的现金",
    "stot_cashout_fnc_act": "筹资活动现金流出小计",
    "n_cash_flows_fnc_act": "筹资活动产生的现金流量净额",
    "eff_fx_flu_cash": "汇率变动对现金的影响",
    "n_incr_cash_cash_equ": "现金及现金等价物净增加额",
    "c_cash_equ_beg_period": "期初现金及现金等价物余额",
    "c_cash_equ_end_period": "期末现金及现金等价物余额",
    "c_recp_cap_contrib": "吸收投资收到的现金",
    "incl_cash_rec_saims": "其中:子公司吸收少数股东投资收到的现金",
    "uncon_invest_loss": "未确认投资损失",
    "prov_depr_assets": "加:资产减值准备",
    "depr_fa_coga_dpba": "固定资产折旧、油气资产折耗、生产性生物资产折旧",
    "amort_intang_assets": "无形资产摊销",
    "lt_amort_deferred_exp": "长期待摊费用摊销",
    "decr_deferred_exp": "待摊费用减少",
    "incr_acc_exp": "预提费用增加",
    "loss_disp_fiolta": "处置固定、无形资产和其他长期资产的损失",
    "loss_scr_fa": "固定资产报废损失",
    "loss_fv_chg": "公允价值变动损失",
    "invest_loss": "投资损失",
    "decr_def_inc_tax_assets": "递延所得税资产减少",
    "incr_def_inc_tax_liab": "递延所得税负债增加",
    "decr_inventories": "存货的减少",
    "decr_oper_payable": "经营性应收项目的减少",
    "incr_oper_payable": "经营性应付项目的增加",
    "others": "其他",
    "im_net_cashflow_oper_act": "经营活动产生的现金流量净额(间接法)",
    "conv_debt_into_cap": "债务转为资本",
    "conv_copbonds_due_within_1y": "一年内到期的可转换公司债券",
    "fa_fnc_leases": "融资租入固定资产",
    "im_n_incr_cash_equ": "现金及现金等价物净增加额(间接法)",
    "net_dism_capital_add": "拆出资金净增加额",
    "net_cash_rece_sec": "代理买卖证券收到的现金净额(元)",
    "credit_impa_loss": "信用减值损失",
    "use_right_asset_dep": "使用权资产折旧",
    "oth_loss_asset": "其他资产减值损失",
    "end_bal_cash": "现金的期末余额",
    "beg_bal_cash": "减:现金的期初余额",
    "end_bal_cash_equ": "加:现金等价物的期末余额",
    "beg_bal_cash_equ": "减:现金等价物的期初余额",
    "update_flag": "更新标志(1最新)"
}

def get_f(df):
    return df[df['报告期'].str.contains('1231')].reset_index(drop=True)


def Company_Financial_Data(code='',start_date='20160101',end_date='20231231',kind='季度'):
    columns1=['公告日期','实际公告日期','报告类型 见底部表','公司类型(1一般工商业2银行3保险4证券)','报告期类型']
    income_data = pro.income(ts_code=code, start_date=start_date, end_date=end_date).rename(columns=variables_dict).iloc[:,:-1].drop_duplicates(['报告期']).drop(columns=columns1)
    
    columns2=['公告日期','实际公告日期','报表类型','公司类型(1一般工商业2银行3保险4证券)','报告期类型']
    balance_sheet = pro.balancesheet(ts_code=code, start_date=start_date, end_date=end_date).rename(columns=variables_dict_2).iloc[:,:-1].drop_duplicates(['报告期']).drop(columns=columns2)
    
    columns3=['公告日期','实际公告日期','报表类型','公司类型(1一般工商业2银行3保险4证券)','报告期类型']
    cash_flow = pro.cashflow(ts_code=code, start_date=start_date, end_date=end_date).rename(columns=variables_dict_3).iloc[:,:-1].drop_duplicates(['报告期']).drop(columns=columns3)
    
    if kind=='年度':
        balance_sheet = get_f(balance_sheet)
        income_data = get_f(income_data)
        cash_flow=get_f(cash_flow)
    merged_df = pd.merge(cash_flow, balance_sheet, on='报告期', how='inner', suffixes=('', '_balance'))
    # 再次合并时,对 income_data 使用不同的后缀
    final_merged_df = pd.merge(merged_df, income_data, on='报告期', how='inner', suffixes=('', '_income'))
    # 假设你想保留 cash_flow 中的 'TS股票代码' 列,你可以删除其他重复的 'TS股票代码' 列
    final_merged_df=final_merged_df.drop(columns=['TS股票代码_balance', 'TS股票代码_income']).dropna(axis=1, how='any')
    return final_merged_df


def Financial_Data_analysis(financial_datas):
    financial_data = financial_datas.copy()
    
    # Convert report date to datetime format and extract the year for easier manipulation
    try:
        financial_data['报告期'] = pd.to_datetime(financial_data['报告期'], format='%Y%m%d')
        financial_data['年份'] = financial_data['报告期'].dt.year
    except KeyError:
        pass  # 如果没有 '报告期' 列,跳过

    # Calculating the financial ratios
    # ROE = Net Income / Shareholder's Equity
    try:
        if '净利润' in financial_data.columns and '股东权益合计(含少数股东权益)' in financial_data.columns:
            financial_data['净资产收益率'] = financial_data['净利润'] / financial_data['股东权益合计(含少数股东权益)']
        elif '净利润(含少数股东损益)' in financial_data.columns and '股东权益合计(含少数股东权益)' in financial_data.columns:
            financial_data['净资产收益率'] = financial_data['净利润(含少数股东损益)'] / financial_data['股东权益合计(含少数股东权益)']
    except KeyError:
        pass  # 如果所需列缺失,跳过

    # ROA = Net Income / Total Assets
    try:
        if '净利润' in financial_data.columns and '资产总计' in financial_data.columns:
            financial_data['总资产报酬率'] = financial_data['净利润'] / financial_data['资产总计']
        elif '净利润(含少数股东损益)' in financial_data.columns and '资产总计' in financial_data.columns:
            financial_data['总资产报酬率'] = financial_data['净利润(含少数股东损益)'] / financial_data['资产总计']
    except KeyError:
        pass  # 如果所需列缺失,跳过

    # EBIT = Net Income + Income Tax + Financial Expenses
    try:
        if '净利润' in financial_data.columns and '所得税费用' in financial_data.columns and '财务费用' in financial_data.columns:
            financial_data['EBIT'] = financial_data['净利润'] + financial_data['所得税费用'] + financial_data['财务费用']
    except KeyError:
        pass  # 如果所需列缺失,跳过

    # Receivables Turnover Ratio = Operating Revenue / Average Receivables
    try:
        if '营业收入' in financial_data.columns and '应收账款' in financial_data.columns:
            financial_data['应收账款周转率'] = financial_data['营业收入'] / financial_data['应收账款']
    except KeyError:
        pass  # 如果所需列缺失,跳过

    # Debt to Asset Ratio = Total Liabilities / Total Assets
    try:
        if '负债合计' in financial_data.columns and '资产总计' in financial_data.columns:
            financial_data['资产负债率'] = financial_data['负债合计'] / financial_data['资产总计']
    except KeyError:
        pass  # 如果所需列缺失,跳过

    # Quick Ratio = (Cash + Receivables) / Current Liabilities
    try:
        if '货币资金' in financial_data.columns and '应收账款' in financial_data.columns and '流动负债合计' in financial_data.columns:
            financial_data['速动比率'] = (financial_data['货币资金'] + financial_data['应收账款']) / financial_data['流动负债合计']
    except KeyError:
        pass  # 如果所需列缺失,跳过

    # Inventory Turnover Ratio = Cost of Goods Sold / Average Inventory
    try:
        if '营业总成本' in financial_data.columns and '存货' in financial_data.columns:
            financial_data['存货周转率'] = financial_data['营业总成本'] / financial_data['存货']
    except KeyError:
        pass  # 如果所需列缺失,跳过

    # Calculating Yearly Growth Rates for Sales and Operating Profit
    try:
        financial_data_sorted = financial_data.sort_values(by=['TS股票代码', '年份'])
        if '营业收入' in financial_data_sorted.columns:
            financial_data_sorted['销售增长率'] = financial_data_sorted.groupby('TS股票代码')['营业收入'].pct_change()
        if '营业利润' in financial_data_sorted.columns:
            financial_data_sorted['销售利润增长率'] = financial_data_sorted.groupby('TS股票代码')['营业利润'].pct_change()
    except KeyError:
        pass  # 如果所需列缺失,跳过

    # Recalculate ROA with the adjusted formula
    try:
        if 'EBIT' in financial_data_sorted.columns and '资产总计' in financial_data_sorted.columns:
            financial_data_sorted['总资产报酬率'] = financial_data_sorted['EBIT'] / financial_data_sorted['资产总计']
    except KeyError:
        pass  # 如果所需列缺失,跳过

    # Sales Gross Margin = (Operating Revenue - Operating Cost) / Operating Revenue
    try:
        if '营业总收入' in financial_data_sorted.columns and '营业总成本' in financial_data_sorted.columns:
            financial_data_sorted['销售毛利率'] = (financial_data_sorted['营业总收入'] - financial_data_sorted['营业总成本']) / financial_data_sorted['营业总收入']
    except KeyError:
        pass  # 如果所需列缺失,跳过

    # 流动比率
    try:
        if '流动资产合计' in financial_data_sorted.columns and '流动负债合计' in financial_data_sorted.columns:
            financial_data_sorted['流动比率'] = financial_data_sorted['流动资产合计'] / financial_data_sorted['流动负债合计']
    except KeyError:
        pass  # 如果所需列缺失,跳过

    # Selecting columns to display
    columns_to_display = ['TS股票代码', '年份', '净资产收益率', '总资产报酬率', '应收账款周转率', '资产负债率', '速动比率', '销售增长率', '销售利润增长率', '存货周转率', '总资产报酬率',
                          '销售毛利率', '流动比率', '资产收益率']

    financial_ratios = financial_data_sorted[[i for i in columns_to_display if i in financial_data_sorted.columns]]
    return financial_ratios


import tushare as ts
import pandas as pd
import streamlit as st  # 导入Streamlit库

# Tushare API
api_key = '7d*************************************************5'
ts.set_token(api_key)
pro = ts.pro_api()

# 用户认证信息
st.write(pro.user(token=api_key))
st.write('如果你看到2k以上积分,就有权限')

def read_file_to_list(file_path):
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            content_list = [item.strip() for item in file.read().split(',') if item.strip()]
        st.success('读取关键词成功')
        return content_list
    except FileNotFoundError:
        st.error(f"文件 {file_path} 未找到。")
        return []
    except Exception as e:
        st.error(f"读取文件时发生错误:{e}")
        return []

# Streamlit 输入框
st.title("财务数据获取与分析")

# 使用 Streamlit tabs 来分隔不同界面
tab1, tab2 = st.tabs(["财务数据下载", "财务数据分析"])

with tab1:
    # 股票名称输入框,用户输入英文逗号分割的股票名称,自动去除空格
    stock_input = st.text_input('请输入股票名称(英文逗号分隔)', '贵州茅台,格力电器')
    codes_names = [code.strip() for code in stock_input.split(',') if code.strip()]
    st.write("选择的行业/股票:", codes_names)

    # 日期选择框
    start_date = st.text_input('请输入开始年份(例如:“2022”)', '2022')
    end_date = st.text_input('请输入结束年份(例如:“2025”)', '2025')

    start_date = start_date + '0101'
    end_date = end_date + '1231'



    # 财务数据的年度/季度选择
    kind = st.selectbox("选择财务数据类型", ["年度", "季度"])

    # 使用 Streamlit 按钮来控制代码执行
    run_button = st.button('开始获取上述公司财务数据')
    if run_button:
        # 获取股票列表
        data = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,area,industry,list_date')

        codes = []
        for codes_name in codes_names:
            try:
                longbai_code = data[data['name'].str.contains(codes_name)]['ts_code'].to_numpy()
                codes.append(longbai_code[0])
                st.write(f'{codes_name}的股票代码是:{longbai_code[0]}')
            except IndexError:
                st.write(f'无法查询到{codes_name}对应的代码,请检查名称')

        st.write(f"\n获取上述查询到代码的股票从{start_date}开始到{end_date}结束的财务数据")
        st.markdown("<hr style='border: 1px solid blue;'>", unsafe_allow_html=True)
    
        # 获取财务数据并保存
        with pd.ExcelWriter('上市公司财务数据.xlsx', engine='xlsxwriter') as writer:
            for code in codes:
                st.write(f'{code}财务数据储存成功')
                df = Company_Financial_Data(code=code, start_date=start_date, end_date=end_date, kind=kind)
                df.to_excel(writer, sheet_name=code, index=False)


            st.write("刚刚下载好的财务数据文件为:上市公司财务数据.xlsx")

with tab2:
        # 财务数据分析
    calculate = st.checkbox("是否进行财务指标的计算?")

    if calculate:
        file_name = st.text_input('请输入刚刚下载的数据的表名', '上市公司财务数据.xlsx')
        xls = pd.ExcelFile(f'{file_name}')
        sheet_names = xls.sheet_names
        xls.close()

        with pd.ExcelWriter('财务数据计算的指标.xlsx', engine='xlsxwriter') as writer:
            for sheet_name in sheet_names:
                df = pd.DataFrame()
                financial_datas = pd.read_excel('上市公司财务数据.xlsx', sheet_name=sheet_name)
                df1 = Financial_Data_analysis(financial_datas)
                df = pd.concat([df, df1], axis=0, ignore_index=True)
                df.to_excel(writer, sheet_name=sheet_name, index=False)

        st.success("财务数据计算完成,结果已保存到:财务数据计算的指标.xlsx里面")

代码很长,毕竟是一个应用的代码,全都在这一个文件里面了。我们把这段代码放入 “财务数据.py”文件中,然后再新建一个 ‘财务数据接口程序.py’ 文件,写入下面代码:

import streamlit.web.cli as stcli
import os,sys
 
def get_base_path():
    """获取脚本运行的基础路径,支持 PyInstaller 打包"""
    if getattr(sys, 'frozen', False):  # 如果是 PyInstaller 打包的环境
        return sys._MEIPASS  # 获取临时目录路径
    return os.path.dirname(__file__)  # 普通运行环境
 
# 获取 QZCA_AI.py 的路径(适配打包环境) ###注意这里要改成你自己的应用py文件的名称
script_path = os.path.join(get_base_path(), "财务数据.py")
 
def resolve_path(path):
    """解析相对路径为绝对路径"""
    resolved_path = os.path.abspath(os.path.join(os.getcwd(), path))
    return resolved_path
 
if __name__ == "__main__":
    # 检查 QZCA_AI.py 是否存在
    if not os.path.exists(script_path):
        print(f"Error: The script '{script_path}' does not exist!")
        sys.exit(1)
 
    # 构造 Streamlit 命令
    sys.argv = [
        "streamlit",
        "run",
        script_path,  # 替换为解析后的路径
        "--global.developmentMode=false",
    ]
 
    try:
        sys.exit(stcli.main())
    except Exception as e:
        print(f"Error while running Streamlit: {e}")
        sys.exit(1)

双击 ‘财务数据接口程序.py’ 文件 ,就可以运行了。

下面学习怎么打包。

streamlit打包

咱们首先创建一个hooks文件夹,在下面创建一个python文件(我的叫hook-streamlit.py),代码如下:


from PyInstaller.utils.hooks import copy_metadata
 
datas = copy_metadata("streamlit")

准备好这三个文件,然后咱们先打包第一次:

(是在财务数据接口程序.py文件夹目录下面打包)

打包命令如下,:

在这个工作目录下,打开cmd输入(如果环境在conda里面就行打开anaconda 的prompt命令提示符):

pyinstaller --onefile --additional-hooks-dir=./hooks 财务数据接口程序.py --clean

然后等待打包:

 然后就会生成上面的 财务数据接口程序.spec 这个文件,咱们先将这时候的dist和build文件删掉。


可选 :

我们可以对我们的应用加个图表,从而更加好看一些。准备一张图片,然后用下面的网站转化为ico文件。

ICO图标在线转换工具

准备好后将你的 .ico文件 放在项目目录下。


打开财务数据接口程序.spec文件(自带的记事本就可以打开),编辑下面部分
 

# -*- mode: python ; coding: utf-8 -*-

#添加下面部分
from PyInstaller.utils.hooks import collect_data_files
from PyInstaller.utils.hooks import copy_metadata
 
### 这里改成自己的python包的路径
datas = [("C:/Users/qzca/AppData/Local/Programs/Python/Python311/Lib/site-packages/streamlit/runtime","./streamlit/runtime") ,
("./财务数据.py", "./")]
datas += collect_data_files("streamlit")
datas += copy_metadata("streamlit")
 
excludes = [
    "tkinter",  # 排除 GUI 模块
    "unittest",  # 排除测试模块
#   "pydoc",     # 排除文档生成模块
#  "http",      # 排除 HTTP 相关模块
    "sqlite3",   # 排除 SQLite 数据库模块
#   "multiprocessing",  # 排除多进程模块(如果不需要)
 'transformers','jupyte','conda','FuzzyTM','Faker',
#    "numpy",  # 如果不需要 numpy
    "scipy",  # 如果不需要 scipy
    "matplotlib",  # 如果不需要 matplotlib
'searbon','xgboost','akshare','yfinance'
]
 

a = Analysis(
    ['财务数据接口程序.py'],
    pathex=[],
    binaries=[],
  datas=datas, ### 改这里,还有需要的包都放在下面
    hiddenimports=['pandas','tushare'],
    hookspath=['./hooks'],
    hooksconfig={},
    runtime_hooks=[],
     excludes=excludes,
    noarchive=False,
    optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.datas,
    [],
    name='财务数据接口程序',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
   # 这里添加ico文件
    icon='fm.ico',
)

里面的datas里面的安装包的路径要改成你自己安装的路径(代码里面是我的路径),你可以通过Python里面的cmd命令(pip show streamlit)去找到你的streamlit包安装在哪个文件夹(或者是环境在anaconda里面就conda命令),然后打开这个文件夹复制路径就可以了。

然后确认无误以后就开始咱们的第二次打包。

输入以下指令:

pyinstaller 财务数据接口程序.spec --clean

 然后会在dist文件夹下面,生成的财务数据接口获取软件.exe文件,

点击exe文件运行即可,出现下面这个页面就是成功了:

就会弹出浏览器的界面里面去获取财务数据就可以了:

这样就可以带着这个exe文件,无论什么window电脑都可以下载财务数据了,当然需要互联网。