各位同学,欢迎来到《商业大数据分析与应用》的实践课程。在当今的商业世界,数据已经成为和资本、土地一样重要的生产要素。对于商学院的同学而言,能否高效、准确地获取并分析数据,尤其是公开的金融与财务数据,是衡量我们商业洞察力的关键能力之一。
本章,我们将学习一个强大的工具——Tushare。它是一个专为金融数据分析而生的Python库,可以帮助我们轻松地从互联网上获取海量的中国金融市场数据。我们将从最基础的安装和配置开始,逐步掌握如何获取上市公司的三大核心财务报表,学习不同的数据存储方法,并最终通过一个综合案例,体验批量处理数据的威力。
3.2 获取三大核心财务报表
财务报表是分析一家公司经营状况的窗口。其中,利润表、资产负债表和现金流量表被称为“三大核心报表”,它们从不同角度展示了公司的财务健康状况。
- 利润表 (Income Statement): 总结了公司在一定时期内(如一个季度或一年)的经营成果,展示其“赚了多少钱”。核心是
收入 - 成本费用 = 利润
。 - 资产负债表 (Balance Sheet): 展示了在特定时间点(如年末)公司的资产、负债和所有者权益状况,遵循会计恒等式
资产 = 负债 + 所有者权益
。它告诉我们公司“拥有什么、欠了什么”。 - 现金流量表 (Cash Flow Statement): 反映了公司在一定时期内现金的流入和流出情况,揭示了公司“钱从哪里来,到哪里去”,是评估公司偿债能力和支付能力的重要依据。
接下来,我们将以A股市场的标杆企业“贵州茅台”(股票代码:600519.SH
)为例,学习如何获取这三张报表。
3.2.1 获取利润表
在Tushare中,获取利润表的接口是 pro.income()
。我们可以通过传递不同的参数来精确控制获取的数据范围。
例如,我们可以获取贵州茅台自上市以来发布的所有利润表数据,代码如 列表 lst-moutai-income-full 所示。
#获取利润表数据
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro = pro.income(ts_code='600519.SH',end_date = "20240621")
df df
输出的结果是一个Pandas DataFrame。每一行代表一份利润表(季度或年度),每一列代表利润表中的一个会计科目。可以看到,数据量非常庞大。
在实际分析中,我们往往只关心特定时期和特定几个科目。Tushare允许我们通过 period
和 fields
参数进行筛选,这大大提高了效率,避免下载不必要的数据。
例如,我们只想获取贵州茅台2023年年报中的几项关键数据:营业总收入、营业总成本、净利润和息税前利润。实现方式见 列表 lst-moutai-income-filtered。
= pro.income(ts_code='600519.SH', period='20231231', fields='ts_code,end_date,total_revenue,total_cogs,n_income,ebit')
df df
ts_code
: 股票代码,格式为“代码.交易所”,如600519.SH
。period
: 报告期,格式为YYYYMMDD
,通常是一个季度或年度的最后一天。20231231
代表2023年年报。fields
: 字段列表,一个包含所需列名的字符串,用逗号分隔。我们通常会包含ts_code
和end_date
作为关键索引。
如 列表 lst-moutai-income-filtered 所示,返回的结果清晰、精确,正是我们研究所需。
3.2.2 获取资产负债表
获取资产负债表的逻辑与利润表完全一致,只需将接口换成 pro.balancesheet()
。输出参数的含义可以查阅Tushare官方文档。
让我们来获取贵州茅台2022年资产负债表中的几个关键指标:货币资金、固定资产、存货、总负债和总资产。代码如 列表 lst-moutai-balance 所示。
= pro.balancesheet(ts_code='600519.SH', period='20221231', fields='ts_code,end_date,money_cap,fix_assets,inventories,total_liab,total_assets')
df df
3.2.3 获取现金流量表
同样地,获取现金流量表的接口是 pro.cashflow()
。我们来获取贵州茅台2023年报中三大活动(经营、投资、筹资)产生的现金流量净额。代码如 列表 lst-moutai-cashflow 所示。
= pro.cashflow(ts_code='600519.SH', period='20231231', fields='ts_code,end_date,n_cashflow_act,n_cashflow_inv_act,n_cash_flows_fnc_act')
df df
3.2.4 课堂练习
现在,请同学们动手尝试完成以下三个任务,来巩固刚刚学到的知识。
3.2.4.1 任务一:获取科大讯飞2023年利润表
要求:获取科大讯飞(002230.SZ
)2023年的利润表中的营业总收入、营业总成本、净利润、息税前利润数据。
提示代码:
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro = #获取科大讯飞2022年的利润表数据
df print(df)
参考答案:
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro = pro.income(ts_code='002230.SZ', period='20231231', fields='ts_code,end_date,total_revenue,total_cogs,n_income,ebit')
df print(df)
3.2.4.2 错误分析
在原始的提示代码 列表 lst-task1-iflytek-hint 的注释中,要求是获取“科大讯飞2022年的利润表数据”,而任务标题和要求明确指出需要的是“2023年”的数据。这是一个文本描述与实际要求不符的小错误。参考答案 列表 lst-task1-iflytek-answer 使用了正确的报告期 period='20231231'
,符合任务要求。
3.2.4.3 重要提醒
在线练习时,请注意题目要求与代码注释之间的差异,并以题目要求为准。
3.2.4.4 任务二:获取中国移动2023年资产负债表
要求:获取中国移动(600941.SH
)2023年的资产负债表中的货币资金、固定资产、存货、总负债、总资产数据。
提示代码:
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro = #补全该行代码
data print(data)
参考答案:
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro = pro.balancesheet(ts_code='600941.SH', period='20221231', fields='ts_code,end_date,money_cap,fix_assets,inventories,total_liab,total_assets')
data print(data)
3.2.4.5 错误分析
在上述 列表 lst-task2-chinamobile-answer 代码中,脚本的目标是获取中国移动 2023年 的资产负债表数据,但代码中使用的参数是 period='20221231'
,这实际上获取的是 2022年 的年度数据。这是一个常见的逻辑错误,即代码参数与分析目标不匹配。
3.2.4.6 正确写法
正确的代码应该是: data = pro.balancesheet(ts_code='600941.SH', period='20231231', fields=...)
3.2.4.7 重要提醒
为通过平台检测,在线练习时仍需按原始错误代码输入。 在未来的实际工作中,务必仔细核对代码参数与研究目标的一致性。
3.2.4.8 任务三:获取中国联通2023年现金流量表
要求:获取中国联通(600050.SH
)2023年的现金流量表中经营活动、投资活动、筹资活动产生的现金流量净额数据。
提示代码:
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro = #补全该行代码
data print(data)
参考答案:
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro = pro.cashflow(ts_code='600050.SH',period='20221231',fields='ts_code,end_date,n_cashflow_act,n_cashflow_inv_act,n_cash_flows_fnc_act')
data print(data)
3.2.4.9 错误分析
与上一个任务类似,在 列表 lst-task3-chinaunicom-answer 代码中,脚本的目标是获取中国联通 2023年 的现金流量表数据,但代码中使用的参数是 period='20221231'
,这获取的是 2022年 的数据。
3.2.4.10 正确写法
正确的代码应该是: data = pro.cashflow(ts_code='600050.SH', period='20231231', fields=...)
3.2.4.11 重要提醒
为通过平台检测,在线练习时仍需按原始错误代码输入。
3.3 持久化存储:保存你的数据资产
通过API获取数据只是第一步。在商业分析的实际工作流中,我们通常需要将获取的数据持久化(Persist),也就是保存到文件中,以便未来重复使用、与同事分享,或用其他软件(如Excel, Tableau)进行进一步分析。这避免了每次分析都重新从网络获取数据,不仅节省时间,也能减轻数据服务商的服务器压力。
Python提供了多种数据存储方式,我们将介绍三种最适合商科同学应用场景的方法。
3.3.1 文件系统存储
这是最基础的方式,直接将数据存为文本文件(如.txt
, .csv
)或二进制文件。Python的内置open()
函数即可完成读写操作。
# 写入数据到文件
with open('data.txt', 'w') as f:
'Hello, world!')
f.write(
# 从文件中读取数据
with open('data.txt', 'r') as f:
= f.read()
data
print(data) # 输出:Hello, world!
这种方法简单直接,但对于结构化的表格数据(如我们的财务报表),处理起来较为繁琐。
3.3.2 数据库存储
对于大规模、结构化的数据管理,关系型数据库(如MySQL, SQLite)是工业界的标准。数据库提供了高效的查询、索引和数据一致性保障。在Python中,我们可以通过sqlite3
等库与数据库交互。
# 连接到 SQLite 数据库
import sqlite3
# 创建连接
= sqlite3.connect('example.db')
conn
# 创建游标
= conn.cursor()
cursor
# 执行 SQL 查询
# cursor.execute('SELECT * FROM students') # 假设存在students表
# 获取结果,并打印到控制台
# print(cursor.fetchall()) # 假设存在students表
# 关闭游标和连接
cursor.close() conn.close()
上述 列表 lst-storage-sqlite 代码展示了连接数据库的基本流程。由于我们没有预先创建example.db
和students
表,执行cursor.execute
和cursor.fetchall
会报错。这里主要是为了让大家了解其工作模式。对于商科分析师的日常工作,直接操作数据库的场景相对较少。
3.3.3 利用Pandas进行数据存储
这是我们最推荐的方法。Pandas作为数据分析的核心库,提供了极其便捷的数据导入导出功能。我们从Tushare获取的DataFrame对象,可以被轻松地保存为多种常用格式,如CSV, Excel, JSON等。
- CSV (Comma-Separated Values): 纯文本格式,通用性极强,几乎所有数据分析软件都支持。文件体积小,读取速度快。
- Excel (
.xlsx
): 完美兼容Microsoft Excel,便于不熟悉编程的同事直接打开和分析。可以保存多个工作表和格式。 - JSON (JavaScript Object Notation): 是一种轻量级的数据交换格式,常用于网络应用。
列表 lst-storage-pandas 展示了Pandas强大的数据读写能力。
# 使用 Pandas 读取 CSV 文件
import pandas as pd
# 为了让代码可以运行,我们先创建一个示例DataFrame
= {'ticker': ['AAPL', 'GOOG'], 'price': [150, 2800]}
sample_data = pd.DataFrame(sample_data)
df
# df = pd.read_csv('data.csv') # 假设存在data.csv文件
# 将数据存储为 Excel 文件
'data.xlsx', index=False)
df.to_excel(
# 将数据存储为 JSON 文件
'data.json', orient='records') df.to_json(
在 列表 lst-storage-pandas 中,我们使用了df.to_excel()
和df.to_json()
两个方法。其中 index=False
是一个常用参数,它告诉Pandas在保存文件时不要将DataFrame的行索引(0, 1, 2…)作为单独的一列写入,这通常是我们期望的结果。
3.3.4 课堂练习:存储东方财富利润表
要求:通过Tushare库获取“东方财富”(300059.SZ
)截止到目前所发布的所有利润表数据,并将其存储到名为eastmoney.csv
的文件中。在导出时,请不要包含索引列。
提示代码:
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro = #获取东方财富的利润表数据
data #存储数据
print(data.head(5))
参考答案:
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro = pro.income(ts_code='300059.SZ')
data"easymoney.csv",index=False)
data.to_csv(print(data.head(5))
3.3.4.1 错误分析
在 列表 lst-task4-eastmoney-answer 的参考答案中,代码将数据保存到了 "easymoney.csv"
文件。然而,实验要求中明确指定的文件名是 "eastmoney.csv"
。这是一个常见的拼写错误(typo),在实际工作中可能导致后续的文件读取步骤失败。
3.3.4.2 正确写法
正确的代码应该是: data.to_csv("eastmoney.csv", index=False)
3.3.4.3 重要提醒
为通过平台检测,在线练习时仍需按原始错误代码输入。 请同学们在自己的项目中养成仔细核对文件名和路径的习惯。
3.4 综合案例实战:批量获取与存储
掌握了单次数据获取和存储后,我们来挑战一个更接近真实商业分析场景的案例。假设你是一位消费行业的分析师,你的任务是快速拉取中国白酒行业三大巨头——贵州茅台、五粮液、泸州老窖——过去三年的核心财务报表,为后续的竞品分析做准备。
这个任务的核心在于自动化和批量处理。我们将使用Python的循环结构来遍历公司列表和年份列表,系统地完成数据的获取和存储。
3.4.1 知识回顾
在开始之前,我们先快速回顾一下Tushare的一些常用接口,这些接口在之前的章节中可能已经介绍过,这里作为案例的准备知识再次强调。
首先是安装与导入,这一步对于任何使用Tushare的项目都是必须的。
3.4.1.1 错误分析
在 列表 lst-tushare-install 代码块中,第一行 pip install tushare
是一个命令行工具指令,而不是一个有效的 Python 语句。如果将此代码块作为Python代码执行,解释器会立即因为不识别pip
这个语法而抛出 SyntaxError
错误。
3.4.1.2 正确做法
正确的流程是先在终端(Terminal)或命令行(Command Prompt)中执行 pip install tushare
来安装库,然后再在Python脚本(.py
文件或Jupyter Notebook)中编写 import tushare as ts
来使用这个库。这两个操作属于不同的环境,不能混在一个Python代码块中。
3.4.1.3 重要提醒
为通过平台检测,在线练习时仍需按原始错误代码输入。 但请务必理解,这在真实编程中是错误的。切勿将命令行指令写入Python脚本。
Tushare不仅能获取财务数据,还能获取丰富的行情数据,例如通过 pro_bar
接口获取日线、均线、换手率等。
#取000001的前复权行情
= ts.pro_bar(ts_code='000001.SZ', adj='qfq', start_date='20240101', end_date='20240601')
df
#取上证指数行情数据
= ts.pro_bar(ts_code='000001.SH', asset='I', start_date='20240101', end_date='20240601')
df
#均线
= ts.pro_bar(ts_code='000001.SZ', start_date='20240101', end_date='20240601', ma=[5, 20, 50])
df
#换手率tor,量比vr
= ts.pro_bar(ts_code='000001.SZ', start_date='20240101', end_date='20240601', factors=['tor', 'vr']) df
当然,我们本次案例的核心还是获取三大财务报表。
#利润表数据
= pro.income(ts_code='600000.SH', start_date='20240101', end_date='20240601', fields='ts_code,ann_date,f_ann_date,end_date,report_type,comp_type,basic_eps,diluted_eps')
df
#资产负债表数据
= pro.balancesheet(ts_code='600000.SH', start_date='20240101', end_date='20240601', fields='ts_code,ann_date,f_ann_date,end_date,report_type,comp_type,cap_rese')
df
#现金流量表
= pro.cashflow(ts_code='600000.SH', start_date='20240101', end_date='20240601') df
3.4.2 案例:白酒三巨头财务数据批量下载
案例要求:通过Tushare数据接口批量获取贵州茅台(600519.SH
)、五粮液(000858.SZ
)、泸州老窖(000568.SZ
)2021年-2023年的三张财务报表(资产负债表、利润表、现金流量表)。并将每张报表按照公司名_报表名称_年份年.xlsx
的格式保存到本地。
解题思路: 1. 定义三个列表:公司名称列表 comps
,股票代码列表 codes
,年份列表 years
。确保公司名和代码在列表中的顺序是一一对应的。 2. 使用一个外层 for
循环遍历公司列表。在循环内部,我们可以通过索引 i
同时获取到公司名 comps[i]
和股票代码 codes[i]
。 3. 在外层循环内部,再嵌套一个内层 for
循环遍历年份列表 years
。 4. 在最内层循环体中,我们依次调用 pro.balancesheet()
, pro.income()
, pro.cashflow()
三个函数,传入当前循环的公司代码和年份来获取数据。 5. 每次获取到一张报表(一个DataFrame)后,立刻使用 df.to_excel()
方法将其保存。文件名需要动态拼接,使用当前循环的公司名和年份来构造。
提示代码:
import tushare as ts
= ts.pro_api("ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474")
pro
# 需要获取数据的公司名称、股票代码、年份
= ['贵州茅台','五粮液','泸州老窖']
comps = ['600519.SH','000858.SZ','000568.SZ']
codes = [2021,2020,2019]
years
# 循环获取语句
for i in range(len(comps)):
for year in years:
= pro.balancesheet(ts_code=codes[i],period=str(year)+'1231')
df_balance = pro.income(ts_code=codes[i],period=str(year)+'1231')
df_income = pro.cashflow(ts_code=codes[i],period=str(year)+'1231')
df_cash # 保存为表格
+'_资产负债表_'+str(year)+'年.xlsx')
df_balance.to_excel(comps[i]#保存利润表
#保存现金流量表
= # 转置显示表格,.T可以将DataFrame表格转置
df_balance print(df_balance)
3.4.2.1 错误分析
在 列表 lst-batch-baijiu-hint 的提示代码中,years
列表被定义为 [2021, 2020, 2019]
。然而,案例要求明确指出需要获取的是 2021年-2023年 的数据。这是一个逻辑错误,列表中的年份范围与任务目标不符。
3.4.2.2 正确写法
正确的年份列表应该是: years = [2023, 2022, 2021]
(或 [2021, 2022, 2023]
, 顺序不影响最终结果)。
3.4.2.3 重要提醒
为通过平台检测,在线练习时仍需按原始错误代码输入。
参考答案:
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro
# 1. 定义批量处理所需的信息列表
# 公司名称列表
= ['贵州茅台','五粮液','泸州老窖']
comps # 公司股票代码列表,注意顺序与公司名称一一对应
= ['600519.SH','000858.SZ','000568.SZ']
codes # 需要查询的年份列表
= [2023,2022,2021]
years
# 2. 设置双重循环来遍历所有公司和所有年份
# 外层循环:遍历公司。range(len(comps))会生成一个从0到2的序列(0, 1, 2)。
# 变量i在每次循环中会依次取值为0, 1, 2,作为列表的索引。
for i in range(len(comps)):
# 内层循环:遍历年份。变量year在每次循环中会依次取值为2023, 2022, 2021。
for year in years:
# 3. 在循环内部,获取当前公司和年份的三大报表
# 将数字年份转换为字符串并拼接'1231',以构建Tushare API所需的period参数
= str(year)+'1231'
current_period # 使用codes[i]获取当前公司的代码,使用current_period获取当前年份的报告期
= pro.balancesheet(ts_code=codes[i],period=current_period)
df_balance = pro.income(ts_code=codes[i],period=current_period)
df_income = pro.cashflow(ts_code=codes[i],period=current_period)
df_cash
# 4. 将获取到的数据保存到Excel文件,并动态生成文件名
# 使用comps[i]获取当前公司名,str(year)获取当前年份,通过字符串拼接创建唯一的文件名
+'_资产负债表_'+str(year)+'年.xlsx') # to_excel函数将在下一章讲完pandas再详细讲解
df_balance.to_excel(comps[i]+'_利润表_'+str(year)+'年.xlsx')
df_income.to_excel(comps[i]+'_现金流量表_'+str(year)+'年.xlsx')
df_cash.to_excel(comps[i]
# 循环全部结束后,变量df_balance中存储的是最后一次循环(泸州老窖, 2021年)获取的资产负债表数据。
# 财务报表列数很多,直接显示不方便阅读。
# .T 是Pandas DataFrame的转置属性,它能将行和列互换,使得宽表变成长表,更适合垂直查看。
= df_balance.T
df_balance # 打印转置后的表格,以供检查。
print(df_balance)
当 列表 lst-batch-baijiu-answer 中的代码运行结束后,你的电脑上就会多出9个Excel文件,例如贵州茅台_利润表_2023年.xlsx
,五粮液_资产负债表_2022年.xlsx
等。我们成功地用程序自动化了一个原本需要大量手动操作的重复性工作,这就是编程在商业分析中价值的直观体现。
代码最后两行 df_balance = df_balance.T
和 print(df_balance)
是为了在屏幕上更清晰地展示最后获取的一份报表。.T
是Pandas DataFrame的一个属性,可以实现矩阵的转置,即将行和列互换,有时这更便于查看字段繁多的财务报表。