社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

我用Python爬了上市公司财务报表,跟巴菲特学习如何炒股

CDA数据分析师 • 8 年前 • 1223 次点击  

作者    崔家华

专栏:https://www.zhihu.com/people/Jack--Cui/activities

本文转自公众号51CTO技术栈,转载需授权

沃伦·巴菲特( 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链接,即可实现批量下载。


另外,也可以通过程序自动获取链接,比如网易财经提供了各个股票板块的涨幅排行榜、跌幅排行榜、成交额排行榜等,我们通过获取这些股票的链接,也可以进行财务报表批量下载,方法很简单,因此不再累赘。


其他:

  • 在使用 MySQL 创建数据库连接的时候,如果数据库(utf8 编码)中有中文,一定要记得设置 charset 参数为 utf8(对应数据库编码)。

  • 学习 SQL 很有帮助,数据库查询很方便,方便我们进行数据分析。

  • 所有爬虫实战的代码,均可以在我的 Github 进行下载(Star 数量要破 100 了,给个助攻好不好?):https://github.com/Jack-Cherish/python-spider。

  • 如有问题,请留言。如有错误,还望指正,谢谢!


往期精彩文章回顾

 
骑行在华盛顿 针对320万次共享单车骑行数据的分析


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/G4d9KL8stP
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/3321
 
1223 次点击