我用Python爬了上市公司財務報表,跟巴菲特學習如何炒股
原創(chuàng)【51CTO.com原創(chuàng)稿件】沃倫·巴菲特( Warren Buffett),全球著名的投資商。從事股票、電子現(xiàn)貨、基金行業(yè)。在 2017 年 7 月 17 日,《福布斯富豪榜》發(fā)布,沃倫·巴菲特以凈資產(chǎn) 734 億美元排名第四。
作為”股神”,他的投資理念被許多人追捧。與其共進午餐的慈善活動都可以拍賣到 345.67 萬美元,從中我們可以輕易地看出,他的投資界地位、影響力有多大。
他的投資名言有很多:
風險,是來自你不知道你在做什么。
若你不打算持有某只股票達十年,則十分鐘也不要持有。
投資的秘訣,不是評估某一行業(yè)對社會的影響有多大,或它的發(fā)展前景有多好,而是一間公司有多強的競爭優(yōu)勢。這優(yōu)勢可以維持多久,產(chǎn)品和服務的優(yōu)越性持久而深厚,才能給投資者帶來優(yōu)厚的回報。
我最喜歡的持股時間是……永遠!
要投資成功,就要拼命閱讀。不但讀有興趣購入的公司資料,也要閱讀其他競爭者的資料。
從他的這些名言中,我們不難發(fā)現(xiàn),巴菲特做的是長期投資,他投一家公司,抱定的目標是持續(xù)持有,不因為價格原因而出售。他看準一家公司,會分析這家公司的競爭優(yōu)勢,也會分析這家公司的對手的競爭優(yōu)勢,然后做出投資決策。
他是怎么確定一家公司是否值得自己長期投資,是否具有競爭優(yōu)勢的呢?其中,最有效、最常用的手段之一就是分析上市公司財務報表。網(wǎng)上有很多《跟巴菲特學看上市公司財務報表》諸如此類的文章,仁者見仁智者見智。
本文重點不在于如何分析財務報表,而是如何獲得財務報表,為后續(xù)的方便分析做準備。
實戰(zhàn)背景
- Github代碼獲?。?a >https://github.com/Jack-Cherish/python-spider
- Python版本: Python3.x
- 運行平臺: Windows
- IDE: Sublime text3
每個上市公司的財務報表都是免費提供的,可以在他們的官網(wǎng)進行下載。但是這樣一個一個找,太麻煩。有沒有一個網(wǎng)站,集成好各個上市公司的財務信息呢?當然有,而且很多!各個金融門戶網(wǎng)站都有!
今天,我們看哪個金融門戶網(wǎng)站?網(wǎng)易財經(jīng)!
雙手奉上它的地址:http://quotes.money.163.com/hkstock/
這個網(wǎng)站長這樣:
我們可以通過股票查詢,查看股票情況。比如我輸入 00700,查看騰訊控股在美股的情況,如下圖:
可以看到,我截圖的時間,騰訊控股”綠了”,也就是跌了。點擊財務數(shù)據(jù),我們就可以看到騰訊控股的財務報表,如圖所示:
這個財務數(shù)據(jù)欄目中,提供了《主要財務指標》、《利潤表》、《資產(chǎn)負債表》以及《現(xiàn)金流量表》。
從圖中可以看到,該網(wǎng)站提供了財務數(shù)據(jù)在線瀏覽功能,但是沒有提供財務報表下載功能,如何將每年的財務數(shù)據(jù)獲取,并存入數(shù)據(jù)庫,方便我們后續(xù)的分析呢?沒錯,這就是本文的主題:財務報表爬取入庫。
網(wǎng)站分析
我們以騰訊控股的財務數(shù)據(jù)為例進行分析。
它的URL:http://quotes.money.163.com/hkstock/cwsj_00700.html
看一下這個 URL 地址有什么特點?騰訊控股的股票代碼是 00700。對的,你沒猜錯,’http://quotes.money.163.com/hkstock/cwsj_’ + 股票代碼 + ‘.html’,就是各個上市公司的財務數(shù)據(jù)頁面。
思考一個問題,下圖的這些數(shù)據(jù),我們需要爬取嗎?
答曰:不需要!為什么?因為財務報表的格式是統(tǒng)一的。我們需要的是這些報表里的數(shù)據(jù),而不是表的欄目名稱,這些欄目名稱,我們直接手動敲入到數(shù)據(jù)庫中就可以了,直接作為數(shù)據(jù)庫的列名。
那么,這些報表數(shù)據(jù)如何獲取呢?請看下圖:
在時間選擇框這里,我們可以獲取到一共有哪些時間的財務報表。點擊查詢按鈕,我們就可以進行查詢,對點擊主要財務指標的查詢按鈕這個動作,使用 Fiddler 進行抓包分析。
抓包截圖如下:
我們可以看到,這個點擊查詢按鈕,發(fā)送的請求地址和返回數(shù)據(jù)。從上圖可以看出返回的數(shù)據(jù)是以 JSON 格式存儲的。那么我們只要解析出這個 JSON 數(shù)據(jù),就可以獲得《主要財務指標》了。
同理,通過抓包可知,主要財務指標、利潤表、資產(chǎn)負債表、現(xiàn)金流量表請求的 URL 分別如下:
發(fā)現(xiàn)規(guī)律了嗎?
- symbol=股票代碼
- start=最早的財務報表時間
- end=最近的財務報表時間
- type=報表縮寫(cwz代表主要財務指標,lrb代表利潤表,fzb代表負債表,llb代表現(xiàn)金流量表)
已經(jīng)知道了各個請求的地址,那么接下來就是解析 JSON 數(shù)據(jù)了。
可以看到,數(shù)據(jù)存儲是用的英文,我們得與下圖的中文進行對應,創(chuàng)建一個字典進行存儲。
別問我,我是怎么對應出來的。我只想說,我花費了半個多小時,對數(shù)據(jù),對得我頭暈眼花。
最終生成的對照表如下:
編寫代碼
在繼續(xù)看文本之前,希望你已經(jīng)掌握以下知識:
- SQL 基礎(chǔ)語法。
- MySQL 數(shù)據(jù)庫的安裝與使用。
- Python 操作 MySQL 數(shù)據(jù)庫的方法。
- SQLyog 的安裝與使用。SQLyog 是一個快速而簡潔的圖形化管理 MySQL 數(shù)據(jù)庫的工具,它能夠在任何地點有效地管理你的數(shù)據(jù)庫。
- Python3 爬蟲基礎(chǔ)。
01.在 SQLyog 中創(chuàng)建表
我們創(chuàng)建一個名字為 financialdata 的數(shù)據(jù)庫,并根據(jù)網(wǎng)站情況創(chuàng)建四個表,分別為:
- cwzb(主要財務指標 )。
- fzb(資產(chǎn)負債表 )。
- llb(現(xiàn)金流量表 )。
- lrb(利潤表)。
除了財務報表中的數(shù)據(jù),我們還需要額外添加股票名、股票代碼、報表日期,用以區(qū)分不同股票,不同時間的財務報表情況。
各個數(shù)據(jù)的數(shù)據(jù)類型,我是粗略分配的,可以根據(jù)實際情況和自己的需求進行設(shè)置。當然,如果為了省事,可以像我一樣:除了報表時間設(shè)置為 date 類型外,其他都設(shè)置為 char(30)類型即可。
好了,準備工作都好了,我們開始編寫代碼吧,需要注意的一點是:在創(chuàng)建數(shù)據(jù)庫連接的時候,我們需要指定 charset 參數(shù),將其設(shè)置為 ’utf8’,因為數(shù)據(jù)庫中存在中文,如果不設(shè)置,數(shù)據(jù)無法導入,當然,記得更改你的數(shù)據(jù)庫名和密碼。
02.編寫代碼
編寫代碼如下:
- #-*- coding:UTF-8 -*-
- import pymysql
- import requests
- import json
- import re
- from bs4 import BeautifulSoup
- if __name__ == '__main__':
- #打開數(shù)據(jù)庫連接:host-連接主機地址,port-端口號,user-用戶名,passwd-用戶密碼,db-數(shù)據(jù)庫名,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':'總資產(chǎn)收益率',
- 'ROEQUITY':'凈資產(chǎn)收益率','CURRENT_RATIO':'流動比率','QUICK_RATIO':'速動比率',
- 'ROLOANS':'存貸比','INVENTORY_TURNOVER':'存貨周轉(zhuǎn)率','GENERAL_ADMIN_RATIO':'管理費用比率',
- 'TOTAL_ASSET2TURNOVER':'資產(chǎn)周轉(zhuǎn)率','FINCOSTS_GROSSPROFIT':'財務費用比率','TURNOVER_CASH':'銷售現(xiàn)金比率','YEAREND_DATE':'報表日期'}
- #利潤表
- lrb_dict = {'TURNOVER':'總營收','OPER_PROFIT':'經(jīng)營利潤','PBT':'除稅前利潤',
- 'NET_PROF':'凈利潤','EPS':'每股基本盈利','DPS':'每股派息',
- 'INCOME_INTEREST':'利息收益','INCOME_NETTRADING':'交易收益','INCOME_NETFEE':'費用收益','YEAREND_DATE':'報表日期'}
- #資產(chǎn)負債表
- fzb_dict = {
- 'FIX_ASS':'固定資產(chǎn)','CURR_ASS':'流動資產(chǎn)','CURR_LIAB':'流動負債',
- 'INVENTORY':'存款','CASH':'現(xiàn)金及銀行存結(jié)','OTHER_ASS':'其他資產(chǎn)',
- 'TOTAL_ASS':'總資產(chǎn)','TOTAL_LIAB':'總負債','EQUITY':'股東權(quán)益',
- 'CASH_SHORTTERMFUND':'庫存現(xiàn)金及短期資金','DEPOSITS_FROM_CUSTOMER':'客戶存款',
- 'FINANCIALASSET_SALE':'可供出售之證券','LOAN_TO_BANK':'銀行同業(yè)存款及貸款',
- 'DERIVATIVES_LIABILITIES':'金融負債','DERIVATIVES_ASSET':'金融資產(chǎn)','YEAREND_DATE':'報表日期'}
- #現(xiàn)金流表
- llb_dict = {
- 'CF_NCF_OPERACT':'經(jīng)營活動產(chǎn)生的現(xiàn)金流','CF_INT_REC':'已收利息','CF_INT_PAID':'已付利息',
- 'CF_INT_REC':'已收股息','CF_DIV_PAID':'已派股息','CF_INV':'投資活動產(chǎn)生現(xiàn)金流',
- 'CF_FIN_ACT':'融資活動產(chǎn)生現(xiàn)金流','CF_BEG':'期初現(xiàn)金及現(xiàn)金等價物','CF_CHANGE_CSH':'現(xiàn)金及現(xiàn)金等價物凈增加額',
- 'CF_END':'期末現(xiàn)金及現(xiàn)金等價物','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('')
- #獲取數(shù)據(jù)地址
- 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)
- # 執(zhí)行sql語句
- conn.commit()
- except:
- # 發(fā)生錯誤時回滾
- 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)
- # 執(zhí)行sql語句
- conn.commit()
- except:
- # 發(fā)生錯誤時回滾
- conn.rollback()
- value_dict = {}
- # 關(guān)閉數(shù)據(jù)庫連接
- cursor.close()
- conn.close()
看下運行效果,我們已經(jīng)順利地將騰訊控股的財務報表帶入數(shù)據(jù)庫中了。
上述代碼比較粗糙,繼續(xù)完善代碼。對代碼進行重構(gòu),創(chuàng)建一個獲取數(shù)據(jù)報表的類。根據(jù)用戶輸入股票代碼,下載相應股票的財務報表,并顯示下載進度,實現(xiàn)效果如下所示:
一直在看,何不自己寫個代碼試試?實現(xiàn)效果如上圖所示!只有自己動手,才能體會到編程的快樂,對知識掌握也就更加扎實。
如果你覺得代碼編寫的差不多了,想對照代碼看一看或者感覺自己無需動手,這種東西就可以輕松掌握。
那么可以從我的 Github 獲取上圖實現(xiàn)效果的代碼:https://github.com/Jack-Cherish/python-spider/blob/master/financical.py
總結(jié)
本文沒有實現(xiàn)批量上市公司財務報表的獲取與入庫,因為方法有很多。
首先,我們可以根據(jù)用戶提供的股票代碼進行批量下載。比如用戶輸入:00700,00701,00702。
然后程序根據(jù)輸入的股票代碼,進行相應的解析,創(chuàng)建出對應的URL鏈接,即可實現(xiàn)批量下載。
另外,也可以通過程序自動獲取鏈接,比如網(wǎng)易財經(jīng)提供了各個股票板塊的漲幅排行榜、跌幅排行榜、成交額排行榜等,我們通過獲取這些股票的鏈接,也可以進行財務報表批量下載,方法很簡單,因此不再累贅。
其他:
- 在使用 MySQL 創(chuàng)建數(shù)據(jù)庫連接的時候,如果數(shù)據(jù)庫(utf8 編碼)中有中文,一定要記得設(shè)置 charset 參數(shù)為 utf8(對應數(shù)據(jù)庫編碼)。
- 學習 SQL 很有幫助,數(shù)據(jù)庫查詢很方便,方便我們進行數(shù)據(jù)分析。
- 所有爬蟲實戰(zhàn)的代碼,均可以在我的 Github 進行下載(Star 數(shù)量要破 100 了,給個助攻好不好?):https://github.com/Jack-Cherish/python-spider。
- 如有問題,請留言。如有錯誤,還望指正,謝謝!
崔家華
知名博客博主
現(xiàn)就讀于東北大學模式識別與智能系統(tǒng)專業(yè)。本科期間,曾擔任學生會”科技創(chuàng)新中心主任”一職,負責組織各類科技競賽相關(guān)活動。與此同時,熱愛科技競賽,曾于 2015 年獲得第十屆全國大學生“飛思卡爾”杯智能車競賽全國一等獎。研究生期間,致力于機器學習,在知名博客已取得了不錯的關(guān)注度。
【51CTO原創(chuàng)稿件,合作站點轉(zhuǎn)載請注明原文作者和出處為51CTO.com】