京公网安备 11010802034615号
经营许可证编号:京B2-20210330
我用Python爬了上市公司财务报表,跟巴菲特学习如何炒股
沃伦·巴菲特( Warren Buffett),全球著名的投资商。从事股票、电子现货、基金行业。在 2017 年 7 月 17 日,《福布斯富豪榜》发布,沃伦·巴菲特以净资产 734 亿美元排名第四。
作为”股神”,他的投资理念被许多人追捧。与其共进午餐的慈善活动都可以拍卖到 345.67 万美元,从中我们可以轻易地看出,他的投资界地位、影响力有多大。
他的资名言有很多:
风险,是来自你不知道你在做什么。
若你不打算持有某只股票达十年,则十分钟也不要持有。
投资的秘诀,不是评估某一行业对社会的影响有多大,或它的发展前景有多好,而是一间公司有多强的竞争优势。这优势可以维持多久,产品和服务的优越性持久而深厚,才能给投资者带来优厚的回报。
我最喜欢的持股时间是……永远!
要投资成功,就要拼命阅读。不但读有兴趣购入的公司资料,也要阅读其他竞争者的资料。
从他的这些名言中,我们不难发现,巴菲特做的是长期投资,他投一家公司,抱定的目标是持续持有,不因为价格原因而出售。他看准一家公司,会分析这家公司的竞争优势,也会分析这家公司的对手的竞争优势,然后做出投资决策。
他是怎么确定一家公司是否值得自己长期投资,是否具有竞争优势的呢?其中,最有效、最常用的手段之一就是分析上市公司财务报表。网上有很多《跟巴菲特学看上市公司财务报表》诸如此类的文章,仁者见仁智者见智。
本文重点不在于如何分析财务报表,而是如何获得财务报表,为后续的方便分析做准备。
实战背景
Github代码获取:https://github.com/Jack-Cherish/python-spider
Python版本: Python3.x
运行平台: Windows
IDE: Sublime text3
每个上市公司的财务报表都是免费提供的,可以在他们的官网进行下载。但是这样一个一个找,太麻烦。有没有一个网站,集成好各个上市公司的财务信息呢?当然有,而且很多!各个金融门户网站都有!
今天,我们看哪个金融门户网站?网易财经!
双手奉上它的地址:http://quotes.money.163.com/hkstock/
这个网站长这样:
我们可以通过股票查询,查看股票情况。比如我输入 00700,查看腾讯控股在美股的情况,如下图:
可以看到,我截图的时间,腾讯控股”绿了”,也就是跌了。点击财务数据,我们就可以看到腾讯控股的财务报表,如图所示:
这个财务数据栏目中,提供了《主要财务指标》、《利润表》、《资产负债表》以及《现金流量表》。
从图中可以看到,该网站提供了财务数据在线浏览功能,但是没有提供财务报表下载功能,如何将每年的财务数据获取,并存入数据库,方便我们后续的分析呢?没错,这就是本文的主题:财务报表爬取入库。
网站分析
我们以腾讯控股的财务数据为例进行分析。
它的URL:http://quotes.money.163.com/hkstock/cwsj_00700.html
看一下这个 URL 地址有什么特点?腾讯控股的股票代码是 00700。对的,你没猜错,’http://quotes.money.163.com/hkstock/cwsj_’ + 股票代码 + ‘.html’,就是各个上市公司的财务数据页面。
思考一个问题,下图的这些数据,我们需要爬取吗?
答曰:不需要!为什么?因为财务报表的格式是统一的。我们需要的是这些报表里的数据,而不是表的栏目名称,这些栏目名称,我们直接手动敲入到数据库中就可以了,直接作为数据库的列名。
那么,这些报表数据如何获取呢?请看下图:
在时间选择框这里,我们可以获取到一共有哪些时间的财务报表。点击查询按钮,我们就可以进行查询,对点击主要财务指标的查询按钮这个动作,使用 Fiddler 进行抓包分析。
抓包截图如下:
我们可以看到,这个点击查询按钮,发送的请求地址和返回数据。从上图可以看出返回的数据是以 JSON 格式存储的。那么我们只要解析出这个 JSON 数据,就可以获得《主要财务指标》了。
同理,通过抓包可知,主要财务指标、利润表、资产负债表、现金流量表请求的 URL 分别如下:
http://quotes.money.163.com/hk/service/cwsj_service.php?symbol=00700&start=2006-06-30&end=2016-12-31&type=cwzb
http://quotes.money.163.com/hk/service/cwsj_service.php?symbol=00700&start=2006-06-30&end=2016-12-31&type=lrb
http://quotes.money.163.com/hk/hk/service/cwsj_service.php?symbol=00700&start=2006-12-31&end=2016-12-31&type=fzb
http://quotes.money.163.com/hk/service/cwsj_service.php?symbol=00700&start=2006-06-30&end=2016-12-31&type=llb
发现规律了吗?
symbol=股票代码
start=最早的财务报表时间
end=最近的财务报表时间
type=报表缩写(cwz代表主要财务指标,lrb代表利润表,fzb代表负债表,llb代表现金流量表)
已经知道了各个请求的地址,那么接下来就是解析 JSON 数据了。
可以看到,数据存储是用的英文,我们得与下图的中文进行对应,创建一个字典进行存储。
别问我,我是怎么对应出来的。我只想说,我花费了半个多小时,对数据,对得我头晕眼花。
最终生成的对照表如下:
编写代码
在继续看文本之前,希望你已经掌握以下知识:SQL 基础语法。
MySQL 数据库的安装与使用。
Python 操作 MySQL 数据库的方法。
SQLyog 的安装与使用。SQLyog 是一个快速而简洁的图形化管理 MySQL 数据库的工具,它能够在任何地点有效地管理你的数据库。
Python3 爬虫基础。
1、在 SQLyog 中创建表
我们创建一个名字为 financialdata 的数据库,并根据网站情况创建四个表,分别为:
cwzb(主要财务指标 )。
fzb(资产负债表 )。
llb(现金流量表 )。
lrb(利润表)。
除了财务报表中的数据,我们还需要额外添加股票名、股票代码、报表日期,用以区分不同股票,不同时间的财务报表情况。
各个数据的数据类型,我是粗略分配的,可以根据实际情况和自己的需求进行设置。当然,如果为了省事,可以像我一样:除了报表时间设置为 date 类型外,其他都设置为 char(30)类型即可。
好了,准备工作都好了,我们开始编写代码吧,需要注意的一点是:在创建数据库连接的时候,我们需要指定 charset 参数,将其设置为 ’utf8’,因为数据库中存在中文,如果不设置,数据无法导入,当然,记得更改你的数据库名和密码。
2、编写代码
编写代码如下:
#-*- coding:UTF-8 -*-
import pymysql
import requests
import json
import re
from bs4 import BeautifulSoup
if __name__ == '__main__':
#打开数据库连接:host-连接主机地址,port-端口号,user-用户名,passwd-用户密码,db-数据库名,charset-编码
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='yourpasswd',db='financialdata',charset='utf8')
#使用cursor()方法获取操作游标
cursor = conn.cursor()
#主要财务指标
cwzb_dict = {'EPS':'基本每股收益','EPS_DILUTED':'摊薄每股收益','GROSS_MARGIN':'毛利率',
'CAPITAL_ADEQUACY':'资本充足率','LOANS_DEPOSITS':'贷款回报率','ROTA':'总资产收益率',
'ROEQUITY':'净资产收益率','CURRENT_RATIO':'流动比率','QUICK_RATIO':'速动比率',
'ROLOANS':'存贷比','INVENTORY_TURNOVER':'存货周转率','GENERAL_ADMIN_RATIO':'管理费用比率',
'TOTAL_ASSET2TURNOVER':'资产周转率','FINCOSTS_GROSSPROFIT':'财务费用比率','TURNOVER_CASH':'销售现金比率','YEAREND_DATE':'报表日期'}
#利润表
lrb_dict = {'TURNOVER':'总营收','OPER_PROFIT':'经营利润','PBT':'除税前利润',
'NET_PROF':'净利润','EPS':'每股基本盈利','DPS':'每股派息',
'INCOME_INTEREST':'利息收益','INCOME_NETTRADING':'交易收益','INCOME_NETFEE':'费用收益','YEAREND_DATE':'报表日期'}
#资产负债表
fzb_dict = {
'FIX_ASS':'固定资产','CURR_ASS':'流动资产','CURR_LIAB':'流动负债',
'INVENTORY':'存款','CASH':'现金及银行存结','OTHER_ASS':'其他资产',
'TOTAL_ASS':'总资产','TOTAL_LIAB':'总负债','EQUITY':'股东权益',
'CASH_SHORTTERMFUND':'库存现金及短期资金','DEPOSITS_FROM_CUSTOMER':'客户存款',
'FINANCIALASSET_SALE':'可供出售之证券','LOAN_TO_BANK':'银行同业存款及贷款',
'DERIVATIVES_LIABILITIES':'金融负债','DERIVATIVES_ASSET':'金融资产','YEAREND_DATE':'报表日期'}
#现金流表
llb_dict = {
'CF_NCF_OPERACT':'经营活动产生的现金流','CF_INT_REC':'已收利息','CF_INT_PAID':'已付利息',
'CF_INT_REC':'已收股息','CF_DIV_PAID':'已派股息','CF_INV':'投资活动产生现金流',
'CF_FIN_ACT':'融资活动产生现金流','CF_BEG':'期初现金及现金等价物','CF_CHANGE_CSH':'现金及现金等价物净增加额',
'CF_END':'期末现金及现金等价物','CF_EXCH':'汇率变动影响','YEAREND_DATE':'报表日期'}
#总表
table_dict = {'cwzb':cwzb_dict,'lrb':lrb_dict,'fzb':fzb_dict,'llb':llb_dict}
#请求头
headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.8',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36',}
#上市股票地址
target_url = 'http://quotes.money.163.com/hkstock/cwsj_00700.html'
req = requests.get(url = target_url, headers = headers)
req.encoding = 'utf-8'
html = req.text
page_bf = BeautifulSoup(html, 'lxml')
#股票名称,股票代码
name = page_bf.find_all('span', class_ = 'name')[0].string
code = page_bf.find_all('span', class_ = 'code')[0].string
code = re.findall('\d+',code)[0]
#打印股票信息
print(name + ':' + code)
print('')
#存储各个表名的列表
table_name_list = []
table_date_list = []
each_date_list = []
url_list = []
#表名和表时间
table_name = page_bf.find_all('div', class_ = 'titlebar3')
for each_table_name in table_name:
#表名
table_name_list.append(each_table_name.span.string)
#表时间
for each_table_date in each_table_name.div.find_all('select', id = re.compile('.+1$')):
url_list.append(re.findall('(\w+)1',each_table_date.get('id'))[0])
for each_date in each_table_date.find_all('option'):
each_date_list.append(each_date.string)
table_date_list.append(each_date_list)
each_date_list = []
#插入信息
for i in range(len(table_name_list)):
print('表名:',table_name_list[i])
print('')
#获取数据地址
url = 'http://quotes.money.163.com/hk/service/cwsj_service.php?symbol={}&start={}&end={}&type={}&unit=yuan'.format(code,table_date_list[i][-1],table_date_list[i][0],url_list[i])
req_table = requests.get(url = url, headers = headers)
value_dict = {}
for each_data in req_table.json():
value_dict['股票名'] = name
value_dict['股票代码'] = code
for key, value in each_data.items():
if key in table_dict[url_list[i]]:
value_dict[table_dict[url_list[i]][key]] = value
# print(value_dict)
sql1 = """
INSERT INTO %s (`股票名`,`股票代码`,`报表日期`) VALUES ('%s','%s','%s')""" % (url_list[i],value_dict['股票名'],value_dict['股票代码'],value_dict['报表日期'])
print(sql1)
try:
cursor.execute(sql1)
# 执行sql语句
conn.commit()
except:
# 发生错误时回滚
conn.rollback()
for key, value in value_dict.items():
if key not in ['股票名','股票代码','报表日期']:
sql2 = """
UPDATE %s SET %s='%s' WHERE `股票名`='%s' AND `报表日期`='%s'""" % (url_list[i],key,value,value_dict['股票名'],value_dict['报表日期'])
print(sql2)
try:
cursor.execute(sql2)
# 执行sql语句
conn.commit()
except:
# 发生错误时回滚
conn.rollback()
value_dict = {}
# 关闭数据库连接
cursor.close()
conn.close()
看下运行效果,我们已经顺利地将腾讯控股的财务报表带入数据库中了。
上述代码比较粗糙,继续完善代码。对代码进行重构,创建一个获取数据报表的类。根据用户输入股票代码,下载相应股票的财务报表,并显示下载进度,实现效果如下所示:
一直在看,何不自己写个代码试试?实现效果如上图所示!只有自己动手,才能体会到编程的快乐,对知识掌握也就更加扎实。
如果你觉得代码编写的差不多了,想对照代码看一看或者感觉自己无需动手,这种东西就可以轻松掌握。
那么可以从我的 Github 获取上图实现效果的代码:https://github.com/Jack-Cherish/python-spider/blob/master/financical.py
总结
本文没有实现批量上市公司财务报表的获取与入库,因为方法有很多。
首先,我们可以根据用户提供的股票代码进行批量下载。比如用户输入:00700,00701,00702。
然后程序根据输入的股票代码,进行相应的解析,创建出对应的URL链接,即可实现批量下载。
另外,也可以通过程序自动获取链接,比如网易财经提供了各个股票板块的涨幅排行榜、跌幅排行榜、成交额排行榜等,我们通过获取这些股票的链接,也可以进行财务报表批量下载,方法很简单,因此不再累赘。
数据分析咨询请扫描二维码
若不方便扫码,搜微信号:CDAshujufenxi
一、问题:为什么 ID 会"断号" 很多刚接触 MySQL 的同学都会有一个困惑:表里的主键 ID 是自增的,删了中间几行数据之后,ID 变 ...
2026-10-22一、问题场景:为什么会重复 JOIN 同一个子查询 在写复杂 SQL 时,经常遇到这种需求:同一张表或同一个聚合子查询,要和主表关联 ...
2026-10-22人力资源专员是HR体系的执行入口。招聘信息发布、简历初筛、面试安排、考勤汇总、入离职手续、劳动合同与档案管理,大多由这个岗 ...
2026-10-22人力资源经理问“必学技巧”,真正要解决的不是知识清单不够长,而是时间有限、业务催得紧,学完却用不上。更有效的路径是:先用 ...
2026-10-22内容营销岗位的核心任务,不是“把文案写得更漂亮”,而是围绕目标受众,用持续的内容供给影响认知、推动转化并沉淀复购。它至少 ...
2026-10-22理财经理培训课程很多,难的是判断“先学什么、后学什么、学完能做什么”。与其按课程名称罗列,不如先看岗位任务:客户分层与维 ...
2026-10-22理财经理的知识结构,可以先用一句话概括:合规与产品知识决定“不出错”,客户经营与资产配置决定“够不够专业”,市场研判与数 ...
2026-10-22经济学背景做数据分析,优势不在工具,而在问题意识:供需如何相互作用、成本与收益如何权衡、变量之间是相关还是因果。真正的缺 ...
2026-10-22需要,但要把“需要”理解准确。经济学本身就在处理数据:GDP、物价、就业、企业营收、问卷调研,都是经济分析的原料。真正的区 ...
2026-10-22经济学分析能力不是单一技能,它至少由三层构成:把现实问题转成可检验假设的理论框架能力、用数据与统计方法验证假设的工具能力 ...
2026-10-22金融分析师的知识提升,先补的不是更多模型,而是让结论可复核的底层能力。按任务顺序,大致是四层:财务与业务理解、指标口径管 ...
2026-10-22金融分析师的“通用技能”,指换到不同机构、不同行业组仍用得上的底层能力,而非某家公司内部系统的操作经验。按任务拆,日常要 ...
2026-10-22先给结论:数据分析证书不是金融分析师的准入条件,招聘方也不会因为一张证书就直接判断你能做分析。要不要考,取决于你当前任务 ...
2026-10-22金融分析师的核心技能,不是“会看K线”或“会讲宏观”,而是能把一家公司、一个行业或一类资产,转化为可核查的数据判断与书面 ...
2026-10-22产品经理的知识提升,不是把需求文档、原型、SQL、数据分析、项目管理都学一遍,而是先对准目标岗位的真实任务,找出自己最缺的 ...
2026-10-22产品专员提升工作知识,最有效的方式不是先囤课程,而是围绕日常交付的四件事来补:需求调研与优先级判断、原型与PRD表达、竞品 ...
2026-10-22产品运营专员提升个人能力,常见误区是先把Excel、SQL、Python列成一张学习清单,然后从第一个工具开始学。更有效的做法是先回答 ...
2026-10-22产品运营的能力提升,最怕从“课程目录”出发:今天学增长模型,明天背SQL语法,学完却说不清自己负责的功能为什么激活率低。更 ...
2026-10-22产品经理的技能和影响力不是两条分开的赛道。技能决定你能不能把问题分析清楚、把方案设计对;影响力决定你的判断能否被团队接受 ...
2026-10-22产品经理要学的数据分析能力,不是“会写SQL”或“会用BI工具”这么简单。更准确地说,是围绕需求评估、功能迭代、指标监控、实 ...
2026-10-22