本文轉載自以下網站:50 行代碼爬取東方財富網上市公司 10 年近百萬行財務報表數據 https://www.makcyun.top/web_scraping_withpython6.html
主要學習的地方:
1.分析網站的ajax請求信息
2.構造參數
3.發起請求后處理獲得的數據
4.保存表格
重點:分析表格類網站的ajax請求,以及如何保存這類信息(關於表格方面的)
通過分析網址 JavaScript 請求,以比 Selenium 快 100 倍的方法,快速爬取東方財富網各上市公司歷年的財務報表數據。
摘要: 上一篇文章,我們用Selenium成功爬取了東方財富網的財務報表數據,但是速度非常慢,爬取 70 頁需要好幾十分鍾。為了加快速度,本文分析網頁JavaScript請求,找到數據接口然后快速爬取財務報表數據。
1. JavaScript請求分析
上一篇文章,我們簡單分了東方財富網財務報表網頁后台的js請求,文章回顧:(https://www.makcyun.top/web_scraping_withpython5.html)
接下來,我們深入分析。首先,點擊報表底部的下一頁,然后觀察左側Name列,看會彈出什么新的請求來:
可以看到,當不斷點擊下一頁時,會相應彈出以get?type開頭的請求。點擊右邊Headers選項卡,可以看到請求的URL,網址非常長,先不管它,后續我們會分析各項參數。接着,點擊右側的Preview和Response,可以看到里面有很多整齊的數據,嘗試猜測這可能是財務報表中的數據,經過和表格進行對比,發現這正是我們所需的數據,太好了。
然后將URL復制到新鏈接中打開看看,可以看到表格中的數據完美地顯示出來了。竟然不用添加Headers、UA去請求就能獲取到,看來東方財富網很大方啊。
到這里,爬取思路已經很清晰了。首先,用Request請求該URL,將獲取到的數據進行正則匹配,將數據轉變為json格式,然后寫入本地文件,最后再加一個分頁循環爬取就OK了。這比之前的Selenium要簡單很多,而且速度應該會快很多倍。下面我們就先來嘗試爬一頁數據看看。
2. 爬取單頁
2.1. 抓取分析
這里仍然以2018年中報的利潤表
為例,抓取該網頁的第一頁表格數據,網頁url為:http://data.eastmoney.com/bbsj/201806/lrb.html
表格第一頁的js請求的url為:http://dcfm.eastmoney.com/em_mutisvcexpandinterface/api/js/get?type=CWBB_LRB&token=70f12f2f4f091e459a279469fe49eca5&st=noticedate&sr=-1&p=2&ps=50&js=var%20spmVUpAF={pages:(tp),data:%20(x)}&filter=(reportdate=^2018-06-30^)&rt=51312886,data:%20(x)}&filter=(reportdate=^2018-06-30^)&rt=51312886)
下面,我們通過分析該url,來抓取表格內容。
import requests |
這里我們定義了一個get_table()方法,來輸出抓取的第一頁表格內容。params為url請求中所包含的參數。
這里對重要參數進行簡單說明:type為7個表格的類型說明,將type拆成兩部分:’CWBB_‘ 和’LRB’,資產負債表等后3個表是以’CWBB_’ 開頭,業績報表至預約披露時間表等前4個表是以’YJBB20_‘開頭的;’LRB’為利潤表的首字母縮寫,同理業績報表則為’YJBB’。所以,如果要爬取不同的表格,就需要更改type參數。’filter’為表格篩選參數,這里篩選出年中報的數據。不同的表格篩選條件會不一樣,所以當type類型更改的時候,也要相應修改filter類型。
params參數設置好之后,將url和params參數一起傳進requests.get()方法中,這樣就構造好了請求連接。幾行代碼就可以成功獲取網頁第一頁的表格數據了:
可以看到,表格信息存儲在LFtlXDqn變量中,pages表示表格有72頁。data為表格數據,是一個由多個字典構成的列表,每個字典是表格的一行數據。我們可以通過正則表達式分別提取出pages和data數據。
2.2. 正則表達式提取表格
# 確定頁數 |
這里用\d+
匹配頁數中的數值,然后用re.search()方法提取出來。group(1)表示輸出第一個結果,這里就是()中的頁數。
# 提取出list,可以使用json.dumps和json.loads |
這里在匹配表格數據用了(.*)
表示貪婪匹配,因為data中有很多個字典,每個字典都是以’}’結尾,所以我們利用貪婪匹配到最后一個’}’,這樣才能獲取data所有數據。多數情況下,我們可能會用到(.*?),這表示非貪婪匹配,意味着之多匹配一個’}’,這樣的話,我們只能匹配到第一行數據,顯然是不對的。
2.3. json.loads()輸出表格
這里提取出來的list是str字符型的,我們需要轉換為list列表類型。為什么要轉換為list類型呢,因為無法用操作list的方法去操作str,比如list切片。轉換為list后,我們可以對list進行切片,比如data[0]可以獲取第一個{}中的數據,也就是表格第一行,這樣方便后續構造循環從而逐行輸出表格數據。這里采用json.loads()方法將str轉換為list。
data = json.loads(data) |
接下來我們就將表格內容輸入到csv文件中。
# 寫入csv文件 |
通過for循環,依次取出表格中的每一行字典數據{},然后用with…open的方法寫入’eastmoney.csv’文件中。
tips:’a’表示可重復寫入;encoding=’utf_8_sig’ 能保持csv文件的漢字不會亂碼;newline為空能避免每行數據中產生空行。
這樣,第一頁50行的表格數據就成功輸出到csv文件中去了:
這里,我們還可以在輸出表格之前添加上表頭:
# 添加列標題 |
這里,data[0]表示list的一個字典中的數據,data[0].keys()表示獲取字典中的key鍵值,也就是列標題。外面再加一個list序列化(結果如下),然后將該list輸出到’eastmoney.csv’中作為表格的列標題即可。
['scode', 'hycode', 'companycode', 'sname', 'publishname', 'reporttimetypecode', 'combinetypecode', 'dataajusttype', 'mkt', 'noticedate', 'reportdate', 'parentnetprofit', 'totaloperatereve', 'totaloperateexp', 'totaloperateexp_tb', 'operateexp', 'operateexp_tb', 'saleexp', 'manageexp', 'financeexp', 'operateprofit', 'sumprofit', 'incometax', 'operatereve', 'intnreve', 'intnreve_tb', 'commnreve', 'commnreve_tb', 'operatetax', 'operatemanageexp', 'commreve_commexp', 'intreve_intexp', 'premiumearned', 'premiumearned_tb', 'investincome', 'surrenderpremium', 'indemnityexp', 'tystz', 'yltz', 'sjltz', 'kcfjcxsyjlr', 'sjlktz', 'eutime', 'yyzc'] |
以上,就完成了單頁表格的爬取和下載到本地的過程。
3. 多頁表格爬取
將上述代碼整理為相應的函數,再添加for循環,僅50行代碼就可以爬取72頁的利潤報表數據:
import requests |
整個下載只用了20多秒,而之前用selenium花了幾十分鍾,這效率提升了足有100倍!
這里,如果我們想下載全部時期(從2007年-2018年)利潤報表數據,也很簡單。只要將type
中的filter
參數注釋掉,意味着也就是不篩選日期,那么就可以下載全部時期的數據。這里當我們取消注釋filter列,將會發現總頁數page_all會從2018年中報的72頁增加到2528頁,全部下載完成后,表格有超過12萬行的數據。基於這些數據,可以嘗試從中進行一些有價值的數據分析。
4. 通用代碼構造
以上代碼實現了2018年中報利潤報表的爬取,但如果不想局限於該報表,還想爬取其他報表或者其他任意時期的數據,那么就需要手動地去修改代碼中相應的字段,很不方便。所以上面的代碼可以說是簡短但不夠強大。
為了能夠靈活實現爬取任意類別和任意時期的報表數據,需要對代碼再進行一些加工,就可以構造出通用強大的爬蟲程序了。
""" |
以爬取2018年中業績報表為例,感受一下比selenium快得多的爬取效果(視頻鏈接):
https://v.qq.com/x/page/a0519bfxajc.html
利用上面的程序,我們可以下載任意時期和任意報表的數據。這里,我下載完成了2018年中報所有7個報表的數據。
文中代碼和素材資源可以在下面的鏈接中獲取:
https://github.com/makcyun/eastmoney_spider