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

































