
我用Python爬了上市公司財務(wù)報表,跟巴菲特學(xué)習(xí)如何炒股
沃倫·巴菲特( Warren Buffett),全球著名的投資商。從事股票、電子現(xiàn)貨、基金行業(yè)。在 2017 年 7 月 17 日,《福布斯富豪榜》發(fā)布,沃倫·巴菲特以凈資產(chǎn) 734 億美元排名第四。
作為”股神”,他的投資理念被許多人追捧。與其共進(jìn)午餐的慈善活動都可以拍賣到 345.67 萬美元,從中我們可以輕易地看出,他的投資界地位、影響力有多大。
他的資名言有很多:
風(fēng)險,是來自你不知道你在做什么。
若你不打算持有某只股票達(dá)十年,則十分鐘也不要持有。
投資的秘訣,不是評估某一行業(yè)對社會的影響有多大,或它的發(fā)展前景有多好,而是一間公司有多強(qiáng)的競爭優(yōu)勢。這優(yōu)勢可以維持多久,產(chǎn)品和服務(wù)的優(yōu)越性持久而深厚,才能給投資者帶來優(yōu)厚的回報。
我最喜歡的持股時間是……永遠(yuǎn)!
要投資成功,就要拼命閱讀。不但讀有興趣購入的公司資料,也要閱讀其他競爭者的資料。
從他的這些名言中,我們不難發(fā)現(xiàn),巴菲特做的是長期投資,他投一家公司,抱定的目標(biāo)是持續(xù)持有,不因?yàn)閮r格原因而出售。他看準(zhǔn)一家公司,會分析這家公司的競爭優(yōu)勢,也會分析這家公司的對手的競爭優(yōu)勢,然后做出投資決策。
他是怎么確定一家公司是否值得自己長期投資,是否具有競爭優(yōu)勢的呢?其中,最有效、最常用的手段之一就是分析上市公司財務(wù)報表。網(wǎng)上有很多《跟巴菲特學(xué)看上市公司財務(wù)報表》諸如此類的文章,仁者見仁智者見智。
本文重點(diǎn)不在于如何分析財務(wù)報表,而是如何獲得財務(wù)報表,為后續(xù)的方便分析做準(zhǔn)備。
實(shí)戰(zhàn)背景
Github代碼獲?。篽ttps://github.com/Jack-Cherish/python-spider
Python版本: Python3.x
運(yùn)行平臺: Windows
IDE: Sublime text3
每個上市公司的財務(wù)報表都是免費(fèi)提供的,可以在他們的官網(wǎng)進(jìn)行下載。但是這樣一個一個找,太麻煩。有沒有一個網(wǎng)站,集成好各個上市公司的財務(wù)信息呢?當(dāng)然有,而且很多!各個金融門戶網(wǎng)站都有!
今天,我們看哪個金融門戶網(wǎng)站?網(wǎng)易財經(jīng)!
雙手奉上它的地址:http://quotes.money.163.com/hkstock/
這個網(wǎng)站長這樣:
我們可以通過股票查詢,查看股票情況。比如我輸入 00700,查看騰訊控股在美股的情況,如下圖:
可以看到,我截圖的時間,騰訊控股”綠了”,也就是跌了。點(diǎn)擊財務(wù)數(shù)據(jù),我們就可以看到騰訊控股的財務(wù)報表,如圖所示:
這個財務(wù)數(shù)據(jù)欄目中,提供了《主要財務(wù)指標(biāo)》、《利潤表》、《資產(chǎn)負(fù)債表》以及《現(xiàn)金流量表》。
從圖中可以看到,該網(wǎng)站提供了財務(wù)數(shù)據(jù)在線瀏覽功能,但是沒有提供財務(wù)報表下載功能,如何將每年的財務(wù)數(shù)據(jù)獲取,并存入數(shù)據(jù)庫,方便我們后續(xù)的分析呢?沒錯,這就是本文的主題:財務(wù)報表爬取入庫。
網(wǎng)站分析
我們以騰訊控股的財務(wù)數(shù)據(jù)為例進(jìn)行分析。
它的URL:http://quotes.money.163.com/hkstock/cwsj_00700.html
看一下這個 URL 地址有什么特點(diǎn)?騰訊控股的股票代碼是 00700。對的,你沒猜錯,’http://quotes.money.163.com/hkstock/cwsj_’ + 股票代碼 + ‘.html’,就是各個上市公司的財務(wù)數(shù)據(jù)頁面。
思考一個問題,下圖的這些數(shù)據(jù),我們需要爬取嗎?
答曰:不需要!為什么?因?yàn)樨攧?wù)報表的格式是統(tǒng)一的。我們需要的是這些報表里的數(shù)據(jù),而不是表的欄目名稱,這些欄目名稱,我們直接手動敲入到數(shù)據(jù)庫中就可以了,直接作為數(shù)據(jù)庫的列名。
那么,這些報表數(shù)據(jù)如何獲取呢?請看下圖:
在時間選擇框這里,我們可以獲取到一共有哪些時間的財務(wù)報表。點(diǎn)擊查詢按鈕,我們就可以進(jìn)行查詢,對點(diǎn)擊主要財務(wù)指標(biāo)的查詢按鈕這個動作,使用 Fiddler 進(jìn)行抓包分析。
抓包截圖如下:
我們可以看到,這個點(diǎn)擊查詢按鈕,發(fā)送的請求地址和返回數(shù)據(jù)。從上圖可以看出返回的數(shù)據(jù)是以 JSON 格式存儲的。那么我們只要解析出這個 JSON 數(shù)據(jù),就可以獲得《主要財務(wù)指標(biāo)》了。
同理,通過抓包可知,主要財務(wù)指標(biāo)、利潤表、資產(chǎn)負(fù)債表、現(xiàn)金流量表請求的 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
發(fā)現(xiàn)規(guī)律了嗎?
symbol=股票代碼
start=最早的財務(wù)報表時間
end=最近的財務(wù)報表時間
type=報表縮寫(cwz代表主要財務(wù)指標(biāo),lrb代表利潤表,fzb代表負(fù)債表,llb代表現(xiàn)金流量表)
已經(jīng)知道了各個請求的地址,那么接下來就是解析 JSON 數(shù)據(jù)了。
可以看到,數(shù)據(jù)存儲是用的英文,我們得與下圖的中文進(jìn)行對應(yīng),創(chuàng)建一個字典進(jìn)行存儲。
別問我,我是怎么對應(yīng)出來的。我只想說,我花費(fèi)了半個多小時,對數(shù)據(jù),對得我頭暈眼花。
最終生成的對照表如下:
編寫代碼
在繼續(xù)看文本之前,希望你已經(jīng)掌握以下知識:SQL 基礎(chǔ)語法。
MySQL 數(shù)據(jù)庫的安裝與使用。
Python 操作 MySQL 數(shù)據(jù)庫的方法。
SQLyog 的安裝與使用。SQLyog 是一個快速而簡潔的圖形化管理 MySQL 數(shù)據(jù)庫的工具,它能夠在任何地點(diǎn)有效地管理你的數(shù)據(jù)庫。
Python3 爬蟲基礎(chǔ)。
1、在 SQLyog 中創(chuàng)建表
我們創(chuàng)建一個名字為 financialdata 的數(shù)據(jù)庫,并根據(jù)網(wǎng)站情況創(chuàng)建四個表,分別為:
cwzb(主要財務(wù)指標(biāo) )。
fzb(資產(chǎn)負(fù)債表 )。
llb(現(xiàn)金流量表 )。
lrb(利潤表)。
除了財務(wù)報表中的數(shù)據(jù),我們還需要額外添加股票名、股票代碼、報表日期,用以區(qū)分不同股票,不同時間的財務(wù)報表情況。
各個數(shù)據(jù)的數(shù)據(jù)類型,我是粗略分配的,可以根據(jù)實(shí)際情況和自己的需求進(jìn)行設(shè)置。當(dāng)然,如果為了省事,可以像我一樣:除了報表時間設(shè)置為 date 類型外,其他都設(shè)置為 char(30)類型即可。
好了,準(zhǔn)備工作都好了,我們開始編寫代碼吧,需要注意的一點(diǎn)是:在創(chuàng)建數(shù)據(jù)庫連接的時候,我們需要指定 charset 參數(shù),將其設(shè)置為 ’utf8’,因?yàn)閿?shù)據(jù)庫中存在中文,如果不設(shè)置,數(shù)據(jù)無法導(dǎo)入,當(dāng)然,記得更改你的數(shù)據(jù)庫名和密碼。
2、編寫代碼
編寫代碼如下:
#-*- coding:UTF-8 -*-
import pymysql
import requests
import json
import re
from bs4 import BeautifulSoup
if __name__ == '__main__':
#打開數(shù)據(jù)庫連接:host-連接主機(jī)地址,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()方法獲取操作游標(biāo)
cursor = conn.cursor()
#主要財務(wù)指標(biāo)
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':'管理費(fèi)用比率',
'TOTAL_ASSET2TURNOVER':'資產(chǎn)周轉(zhuǎn)率','FINCOSTS_GROSSPROFIT':'財務(wù)費(fèi)用比率','TURNOVER_CASH':'銷售現(xiàn)金比率','YEAREND_DATE':'報表日期'}
#利潤表
lrb_dict = {'TURNOVER':'總營收','OPER_PROFIT':'經(jīng)營利潤','PBT':'除稅前利潤',
'NET_PROF':'凈利潤','EPS':'每股基本盈利','DPS':'每股派息',
'INCOME_INTEREST':'利息收益','INCOME_NETTRADING':'交易收益','INCOME_NETFEE':'費(fèi)用收益','YEAREND_DATE':'報表日期'}
#資產(chǎn)負(fù)債表
fzb_dict = {
'FIX_ASS':'固定資產(chǎn)','CURR_ASS':'流動資產(chǎn)','CURR_LIAB':'流動負(fù)債',
'INVENTORY':'存款','CASH':'現(xiàn)金及銀行存結(jié)','OTHER_ASS':'其他資產(chǎn)',
'TOTAL_ASS':'總資產(chǎn)','TOTAL_LIAB':'總負(fù)債','EQUITY':'股東權(quán)益',
'CASH_SHORTTERMFUND':'庫存現(xiàn)金及短期資金','DEPOSITS_FROM_CUSTOMER':'客戶存款',
'FINANCIALASSET_SALE':'可供出售之證券','LOAN_TO_BANK':'銀行同業(yè)存款及貸款',
'DERIVATIVES_LIABILITIES':'金融負(fù)債','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()
看下運(yùn)行效果,我們已經(jīng)順利地將騰訊控股的財務(wù)報表帶入數(shù)據(jù)庫中了。
上述代碼比較粗糙,繼續(xù)完善代碼。對代碼進(jìn)行重構(gòu),創(chuàng)建一個獲取數(shù)據(jù)報表的類。根據(jù)用戶輸入股票代碼,下載相應(yīng)股票的財務(wù)報表,并顯示下載進(jìn)度,實(shí)現(xiàn)效果如下所示:
一直在看,何不自己寫個代碼試試?實(shí)現(xiàn)效果如上圖所示!只有自己動手,才能體會到編程的快樂,對知識掌握也就更加扎實(shí)。
如果你覺得代碼編寫的差不多了,想對照代碼看一看或者感覺自己無需動手,這種東西就可以輕松掌握。
那么可以從我的 Github 獲取上圖實(shí)現(xiàn)效果的代碼:https://github.com/Jack-Cherish/python-spider/blob/master/financical.py
總結(jié)
本文沒有實(shí)現(xiàn)批量上市公司財務(wù)報表的獲取與入庫,因?yàn)榉椒ㄓ泻芏唷?
首先,我們可以根據(jù)用戶提供的股票代碼進(jìn)行批量下載。比如用戶輸入:00700,00701,00702。
然后程序根據(jù)輸入的股票代碼,進(jìn)行相應(yīng)的解析,創(chuàng)建出對應(yīng)的URL鏈接,即可實(shí)現(xiàn)批量下載。
另外,也可以通過程序自動獲取鏈接,比如網(wǎng)易財經(jīng)提供了各個股票板塊的漲幅排行榜、跌幅排行榜、成交額排行榜等,我們通過獲取這些股票的鏈接,也可以進(jìn)行財務(wù)報表批量下載,方法很簡單,因此不再累贅。
數(shù)據(jù)分析咨詢請掃描二維碼
若不方便掃碼,搜微信號:CDAshujufenxi
SQL Server 中 CONVERT 函數(shù)的日期轉(zhuǎn)換:從基礎(chǔ)用法到實(shí)戰(zhàn)優(yōu)化 在 SQL Server 的數(shù)據(jù)處理中,日期格式轉(zhuǎn)換是高頻需求 —— 無論 ...
2025-09-18MySQL 大表拆分與關(guān)聯(lián)查詢效率:打破 “拆分必慢” 的認(rèn)知誤區(qū) 在 MySQL 數(shù)據(jù)庫管理中,“大表” 始終是性能優(yōu)化繞不開的話題。 ...
2025-09-18CDA 數(shù)據(jù)分析師:表結(jié)構(gòu)數(shù)據(jù) “獲取 - 加工 - 使用” 全流程的賦能者 表結(jié)構(gòu)數(shù)據(jù)(如數(shù)據(jù)庫表、Excel 表、CSV 文件)是企業(yè)數(shù)字 ...
2025-09-18DSGE 模型中的 Et:理性預(yù)期算子的內(nèi)涵、作用與應(yīng)用解析 動態(tài)隨機(jī)一般均衡(Dynamic Stochastic General Equilibrium, DSGE)模 ...
2025-09-17Python 提取 TIF 中地名的完整指南 一、先明確:TIF 中的地名有哪兩種存在形式? 在開始提取前,需先判斷 TIF 文件的類型 —— ...
2025-09-17CDA 數(shù)據(jù)分析師:解鎖表結(jié)構(gòu)數(shù)據(jù)特征價值的專業(yè)核心 表結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 規(guī)范存儲的結(jié)構(gòu)化數(shù)據(jù),如數(shù)據(jù)庫表、Excel 表、 ...
2025-09-17Excel 導(dǎo)入數(shù)據(jù)含缺失值?詳解 dropna 函數(shù)的功能與實(shí)戰(zhàn)應(yīng)用 在用 Python(如 pandas 庫)處理 Excel 數(shù)據(jù)時,“缺失值” 是高頻 ...
2025-09-16深入解析卡方檢驗(yàn)與 t 檢驗(yàn):差異、適用場景與實(shí)踐應(yīng)用 在數(shù)據(jù)分析與統(tǒng)計學(xué)領(lǐng)域,假設(shè)檢驗(yàn)是驗(yàn)證研究假設(shè)、判斷數(shù)據(jù)差異是否 “ ...
2025-09-16CDA 數(shù)據(jù)分析師:掌控表格結(jié)構(gòu)數(shù)據(jù)全功能周期的專業(yè)操盤手 表格結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 存儲的結(jié)構(gòu)化數(shù)據(jù),如 Excel 表、數(shù)據(jù) ...
2025-09-16MySQL 執(zhí)行計劃中 rows 數(shù)量的準(zhǔn)確性解析:原理、影響因素與優(yōu)化 在 MySQL SQL 調(diào)優(yōu)中,EXPLAIN執(zhí)行計劃是核心工具,而其中的row ...
2025-09-15解析 Python 中 Response 對象的 text 與 content:區(qū)別、場景與實(shí)踐指南 在 Python 進(jìn)行 HTTP 網(wǎng)絡(luò)請求開發(fā)時(如使用requests ...
2025-09-15CDA 數(shù)據(jù)分析師:激活表格結(jié)構(gòu)數(shù)據(jù)價值的核心操盤手 表格結(jié)構(gòu)數(shù)據(jù)(如 Excel 表格、數(shù)據(jù)庫表)是企業(yè)最基礎(chǔ)、最核心的數(shù)據(jù)形態(tài) ...
2025-09-15Python HTTP 請求工具對比:urllib.request 與 requests 的核心差異與選擇指南 在 Python 處理 HTTP 請求(如接口調(diào)用、數(shù)據(jù)爬取 ...
2025-09-12解決 pd.read_csv 讀取長浮點(diǎn)數(shù)據(jù)的科學(xué)計數(shù)法問題 為幫助 Python 數(shù)據(jù)從業(yè)者解決pd.read_csv讀取長浮點(diǎn)數(shù)據(jù)時的科學(xué)計數(shù)法問題 ...
2025-09-12CDA 數(shù)據(jù)分析師:業(yè)務(wù)數(shù)據(jù)分析步驟的落地者與價值優(yōu)化者 業(yè)務(wù)數(shù)據(jù)分析是企業(yè)解決日常運(yùn)營問題、提升執(zhí)行效率的核心手段,其價值 ...
2025-09-12用 SQL 驗(yàn)證業(yè)務(wù)邏輯:從規(guī)則拆解到數(shù)據(jù)把關(guān)的實(shí)戰(zhàn)指南 在業(yè)務(wù)系統(tǒng)落地過程中,“業(yè)務(wù)邏輯” 是連接 “需求設(shè)計” 與 “用戶體驗(yàn) ...
2025-09-11塔吉特百貨孕婦營銷案例:數(shù)據(jù)驅(qū)動下的精準(zhǔn)零售革命與啟示 在零售行業(yè) “流量紅利見頂” 的當(dāng)下,精準(zhǔn)營銷成為企業(yè)突圍的核心方 ...
2025-09-11CDA 數(shù)據(jù)分析師與戰(zhàn)略 / 業(yè)務(wù)數(shù)據(jù)分析:概念辨析與協(xié)同價值 在數(shù)據(jù)驅(qū)動決策的體系中,“戰(zhàn)略數(shù)據(jù)分析”“業(yè)務(wù)數(shù)據(jù)分析” 是企業(yè) ...
2025-09-11Excel 數(shù)據(jù)聚類分析:從操作實(shí)踐到業(yè)務(wù)價值挖掘 在數(shù)據(jù)分析場景中,聚類分析作為 “無監(jiān)督分組” 的核心工具,能從雜亂數(shù)據(jù)中挖 ...
2025-09-10統(tǒng)計模型的核心目的:從數(shù)據(jù)解讀到?jīng)Q策支撐的價值導(dǎo)向 統(tǒng)計模型作為數(shù)據(jù)分析的核心工具,并非簡單的 “公式堆砌”,而是圍繞特定 ...
2025-09-10