3  使用Tushare获取并存储财务数据

各位同学,欢迎来到《商业大数据分析与应用》的实践课程。在当今的商业世界,数据已经成为和资本、土地一样重要的生产要素。对于商学院的同学而言,能否高效、准确地获取并分析数据,尤其是公开的金融与财务数据,是衡量我们商业洞察力的关键能力之一。

本章,我们将学习一个强大的工具——Tushare。它是一个专为金融数据分析而生的Python库,可以帮助我们轻松地从互联网上获取海量的中国金融市场数据。我们将从最基础的安装和配置开始,逐步掌握如何获取上市公司的三大核心财务报表,学习不同的数据存储方法,并最终通过一个综合案例,体验批量处理数据的威力。

3.1 Tushare:金融数据分析的利器

3.1.1 Tushare是什么?

想象一下,你需要为一份关于“贵州茅台”的行业研究报告搜集其过去十年的所有财务数据。传统的方法可能是访问公司官网,在年报PDF中逐个查找、复制、粘贴。这个过程不仅枯燥乏味,而且极易出错。

Tushare库彻底改变了这一工作模式。它是一个开源的Python金融数据接口包,允许我们通过编写几行简单的代码,直接从其数据服务器上获取结构化的、干净的金融数据,涵盖股票、基金、期货、债券、宏观经济等多个维度。我们课程中使用的Tushare Pro是其专业升级版,提供了更稳定和全面的数据服务。

3.1.2 Tushare的安装与配置

在开始之前,我们需要完成两个基本步骤:安装Tushare库和获取个人访问凭证(Token)。

第一步:安装库

对于绝大多数同学来说,我们的教学环境已经预装好了所有必要的库。但在你自己的电脑上,你可以通过pip(Python的包管理工具)来安装。打开终端或命令行工具,输入以下命令即可:

pip install tushare -i https://pypi.tuna.tsinghua.edu.cn/simple
注记

我们使用了清华大学的镜像源 (-i https://...) 来加速国内的下载速度,这是一个非常实用的小技巧。

第二步:获取Token凭证

Tushare Pro的服务需要一个Token作为个人身份凭证,以管理数据访问权限。大家可以访问 Tushare 官网 (https://tushare.pro/) 注册账户,进入个人主页后即可找到自己的Token。

为了方便大家在课堂上练习,我在这里提供一个课程公用的高积分Token,请大家在练习中使用。

ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474

第三步:初始化接口

获取Token后,我们就可以在Python代码中初始化Tushare Pro的接口了。基本步骤如下:

  1. 导入tushare库,并通常将其简写为ts
  2. 调用ts.pro_api()函数,并将你的Token字符串作为参数传入。
列表 3.1
import tushare as ts
# 调用Token,初始化pro接口
pro = ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')

完成 列表 lst-tushare-init 中的初始化后,变量 pro 就成为了我们与Tushare数据服务器沟通的桥梁。接下来所有的操作都将通过调用pro对象的方法来完成。

3.2 获取三大核心财务报表

财务报表是分析一家公司经营状况的窗口。其中,利润表、资产负债表和现金流量表被称为“三大核心报表”,它们从不同角度展示了公司的财务健康状况。

  • 利润表 (Income Statement): 总结了公司在一定时期内(如一个季度或一年)的经营成果,展示其“赚了多少钱”。核心是收入 - 成本费用 = 利润
  • 资产负债表 (Balance Sheet): 展示了在特定时间点(如年末)公司的资产、负债和所有者权益状况,遵循会计恒等式资产 = 负债 + 所有者权益。它告诉我们公司“拥有什么、欠了什么”。
  • 现金流量表 (Cash Flow Statement): 反映了公司在一定时期内现金的流入和流出情况,揭示了公司“钱从哪里来,到哪里去”,是评估公司偿债能力和支付能力的重要依据。

接下来,我们将以A股市场的标杆企业“贵州茅台”(股票代码:600519.SH)为例,学习如何获取这三张报表。

3.2.1 获取利润表

在Tushare中,获取利润表的接口是 pro.income()。我们可以通过传递不同的参数来精确控制获取的数据范围。

例如,我们可以获取贵州茅台自上市以来发布的所有利润表数据,代码如 列表 lst-moutai-income-full 所示。

列表 3.2
#获取利润表数据
import tushare as ts
pro = ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
df = pro.income(ts_code='600519.SH',end_date = "20240621")
df

输出的结果是一个Pandas DataFrame。每一行代表一份利润表(季度或年度),每一列代表利润表中的一个会计科目。可以看到,数据量非常庞大。

在实际分析中,我们往往只关心特定时期和特定几个科目。Tushare允许我们通过 periodfields 参数进行筛选,这大大提高了效率,避免下载不必要的数据。

例如,我们只想获取贵州茅台2023年年报中的几项关键数据:营业总收入、营业总成本、净利润和息税前利润。实现方式见 列表 lst-moutai-income-filtered

列表 3.3
df = pro.income(ts_code='600519.SH', period='20231231', fields='ts_code,end_date,total_revenue,total_cogs,n_income,ebit')
df
参数解读
  • ts_code: 股票代码,格式为“代码.交易所”,如600519.SH
  • period: 报告期,格式为YYYYMMDD,通常是一个季度或年度的最后一天。20231231代表2023年年报。
  • fields: 字段列表,一个包含所需列名的字符串,用逗号分隔。我们通常会包含ts_codeend_date作为关键索引。

列表 lst-moutai-income-filtered 所示,返回的结果清晰、精确,正是我们研究所需。

3.2.2 获取资产负债表

获取资产负债表的逻辑与利润表完全一致,只需将接口换成 pro.balancesheet()。输出参数的含义可以查阅Tushare官方文档。

让我们来获取贵州茅台2022年资产负债表中的几个关键指标:货币资金、固定资产、存货、总负债和总资产。代码如 列表 lst-moutai-balance 所示。

列表 3.4
df = pro.balancesheet(ts_code='600519.SH', period='20221231', fields='ts_code,end_date,money_cap,fix_assets,inventories,total_liab,total_assets')
df

3.2.3 获取现金流量表

同样地,获取现金流量表的接口是 pro.cashflow()。我们来获取贵州茅台2023年报中三大活动(经营、投资、筹资)产生的现金流量净额。代码如 列表 lst-moutai-cashflow 所示。

列表 3.5
df = 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

3.2.4 课堂练习

现在,请同学们动手尝试完成以下三个任务,来巩固刚刚学到的知识。

3.2.4.1 任务一:获取科大讯飞2023年利润表

要求:获取科大讯飞(002230.SZ)2023年的利润表中的营业总收入、营业总成本、净利润、息税前利润数据。

提示代码

列表 3.6
import tushare as ts
pro = ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
df =                                                                                                       #获取科大讯飞2022年的利润表数据
print(df)

参考答案

列表 3.7
import tushare as ts
pro = ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
df = pro.income(ts_code='002230.SZ', period='20231231', fields='ts_code,end_date,total_revenue,total_cogs,n_income,ebit')
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年的资产负债表中的货币资金、固定资产、存货、总负债、总资产数据。

提示代码

列表 3.8
import tushare as ts
pro = ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
data =                                                                                                     #补全该行代码
print(data)

参考答案

列表 3.9
import tushare as ts
pro = ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
data = pro.balancesheet(ts_code='600941.SH', period='20221231', fields='ts_code,end_date,money_cap,fix_assets,inventories,total_liab,total_assets')
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年的现金流量表中经营活动、投资活动、筹资活动产生的现金流量净额数据。

提示代码

列表 3.10
import tushare as ts
pro = ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
data =                                                                                                     #补全该行代码
print(data)

参考答案

列表 3.11
import tushare as ts
pro = ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
data = 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')
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()函数即可完成读写操作。

列表 3.12
# 写入数据到文件
with open('data.txt', 'w') as f:
 f.write('Hello, world!')

# 从文件中读取数据
with open('data.txt', 'r') as f:
 data = f.read()

print(data) # 输出:Hello, world!

这种方法简单直接,但对于结构化的表格数据(如我们的财务报表),处理起来较为繁琐。

3.3.2 数据库存储

对于大规模、结构化的数据管理,关系型数据库(如MySQL, SQLite)是工业界的标准。数据库提供了高效的查询、索引和数据一致性保障。在Python中,我们可以通过sqlite3等库与数据库交互。

列表 3.13
# 连接到 SQLite 数据库
import sqlite3

# 创建连接
conn = sqlite3.connect('example.db')

# 创建游标
cursor = conn.cursor()

# 执行 SQL 查询
# cursor.execute('SELECT * FROM students') # 假设存在students表

# 获取结果,并打印到控制台
# print(cursor.fetchall()) # 假设存在students表

# 关闭游标和连接
cursor.close()
conn.close()
注记

上述 列表 lst-storage-sqlite 代码展示了连接数据库的基本流程。由于我们没有预先创建example.dbstudents表,执行cursor.executecursor.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强大的数据读写能力。

列表 3.14
# 使用 Pandas 读取 CSV 文件
import pandas as pd

# 为了让代码可以运行,我们先创建一个示例DataFrame
sample_data = {'ticker': ['AAPL', 'GOOG'], 'price': [150, 2800]}
df = pd.DataFrame(sample_data)

# df = pd.read_csv('data.csv') # 假设存在data.csv文件

# 将数据存储为 Excel 文件
df.to_excel('data.xlsx', index=False)

# 将数据存储为 JSON 文件
df.to_json('data.json', orient='records')
注记

列表 lst-storage-pandas 中,我们使用了df.to_excel()df.to_json()两个方法。其中 index=False 是一个常用参数,它告诉Pandas在保存文件时不要将DataFrame的行索引(0, 1, 2…)作为单独的一列写入,这通常是我们期望的结果。

3.3.4 课堂练习:存储东方财富利润表

要求:通过Tushare库获取“东方财富”(300059.SZ)截止到目前所发布的所有利润表数据,并将其存储到名为eastmoney.csv的文件中。在导出时,请不要包含索引列。

提示代码

列表 3.15
import tushare as ts
pro = ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
data =                                       #获取东方财富的利润表数据
                                             #存储数据
print(data.head(5))

参考答案

列表 3.16
import tushare as ts
pro = ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
data= pro.income(ts_code='300059.SZ')
data.to_csv("easymoney.csv",index=False)
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.17
pip install tushare
import tushare as ts

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 接口获取日线、均线、换手率等。

列表 3.18
#取000001的前复权行情
df = 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])

#换手率tor,量比vr
df = ts.pro_bar(ts_code='000001.SZ', start_date='20240101', end_date='20240601', factors=['tor', 'vr'])

当然,我们本次案例的核心还是获取三大财务报表。

列表 3.19
#利润表数据
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')

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() 方法将其保存。文件名需要动态拼接,使用当前循环的公司名和年份来构造。

提示代码

列表 3.20
import tushare as ts

pro = ts.pro_api("ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474")

# 需要获取数据的公司名称、股票代码、年份
comps = ['贵州茅台','五粮液','泸州老窖']
codes = ['600519.SH','000858.SZ','000568.SZ']
years = [2021,2020,2019]

# 循环获取语句
for i in range(len(comps)):
  for year in years:
    df_balance = pro.balancesheet(ts_code=codes[i],period=str(year)+'1231')
    df_income = pro.income(ts_code=codes[i],period=str(year)+'1231')
    df_cash = pro.cashflow(ts_code=codes[i],period=str(year)+'1231')
    # 保存为表格
    df_balance.to_excel(comps[i]+'_资产负债表_'+str(year)+'年.xlsx') 
                                    #保存利润表
                                    #保存现金流量表
     
     

df_balance =                                             # 转置显示表格,.T可以将DataFrame表格转置
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 重要提醒

为通过平台检测,在线练习时仍需按原始错误代码输入。

参考答案

列表 3.21
import tushare as ts
pro = ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')

# 1. 定义批量处理所需的信息列表
# 公司名称列表
comps = ['贵州茅台','五粮液','泸州老窖']
# 公司股票代码列表,注意顺序与公司名称一一对应
codes = ['600519.SH','000858.SZ','000568.SZ']
# 需要查询的年份列表
years = [2023,2022,2021]

# 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参数
    current_period = str(year)+'1231'
    # 使用codes[i]获取当前公司的代码,使用current_period获取当前年份的报告期
    df_balance = pro.balancesheet(ts_code=codes[i],period=current_period)
    df_income = pro.income(ts_code=codes[i],period=current_period)
    df_cash = pro.cashflow(ts_code=codes[i],period=current_period)
    
    # 4. 将获取到的数据保存到Excel文件,并动态生成文件名
    # 使用comps[i]获取当前公司名,str(year)获取当前年份,通过字符串拼接创建唯一的文件名
    df_balance.to_excel(comps[i]+'_资产负债表_'+str(year)+'年.xlsx') # to_excel函数将在下一章讲完pandas再详细讲解
    df_income.to_excel(comps[i]+'_利润表_'+str(year)+'年.xlsx')
    df_cash.to_excel(comps[i]+'_现金流量表_'+str(year)+'年.xlsx')
          
# 循环全部结束后,变量df_balance中存储的是最后一次循环(泸州老窖, 2021年)获取的资产负债表数据。
# 财务报表列数很多,直接显示不方便阅读。
# .T 是Pandas DataFrame的转置属性,它能将行和列互换,使得宽表变成长表,更适合垂直查看。
df_balance = df_balance.T
# 打印转置后的表格,以供检查。
print(df_balance)

列表 lst-batch-baijiu-answer 中的代码运行结束后,你的电脑上就会多出9个Excel文件,例如贵州茅台_利润表_2023年.xlsx五粮液_资产负债表_2022年.xlsx等。我们成功地用程序自动化了一个原本需要大量手动操作的重复性工作,这就是编程在商业分析中价值的直观体现。

提示

代码最后两行 df_balance = df_balance.Tprint(df_balance) 是为了在屏幕上更清晰地展示最后获取的一份报表。.T 是Pandas DataFrame的一个属性,可以实现矩阵的转置,即将行和列互换,有时这更便于查看字段繁多的财务报表。