用Python爬取歷年基金資料
寫在前面
忙於學習,我已經好久沒有寫過部落格了。最近,由於越來越意識到了理財的重要性,於是我選擇了從最容易入門且風險較低的基金入手,看的同時也能夠學習到一些金融知識。不過,不買就沒有看的慾望,所以我也還是選擇了幾支基金入手了,當然,最終還是以學習為主,收益只能作為一個檢驗學習效果的手段,而不是我當前的目的(是未來的目的hhh)。
要想分析基金,少不了歷年的資料,為了分析方便,我還是覺得先把所有的資料爬下來,然後再做進一步處理。
介面分析
爬資料需要先思考從哪裡爬?經過一番搜尋和考慮,我發現 天天基金網 的資料既比較全,又十分容易爬取,所以就從它入手了。
首先,隨便點開一支基金,我們可以看到域名就是該基金的程式碼,十分方便,其次下面有生成的淨值圖。

基金詳情
開啟chrome的開發者除錯,選擇Network,然後重新整理一下,很快我們就能發現我們想要的東西了。可以看到,這是基金程式碼加當前時間的一個介面,請求的url是 http://fund.eastmoney.com/pingzhongdata/003511.js?v=20190304115823
也就是說我們可以簡單的通過 http://fund.eastmoney.com/pingzhongdata/基金程式碼.js?v=當前時間
這樣一個介面就能獲取到相應的資料了。

開啟開發者模式
現在我們來看看這個檔案的具體內容是什麼?
顯然,這裡面的東西就是我們想要的, Data_netWorthTrend
裡面的"y"就包含了每一天的淨值

獲取資料
現在我們的介面已經十分明確了,就是 http://fund.eastmoney.com/pingzhongdata/基金程式碼.js?v=當前時間
通過 基金程式碼 和 當前時間 我們就能夠獲取到相應的資料,接下來就是需要將我們想要的資料從獲取的檔案中提取出來了,也就是我們說的資料清洗的過程。
這個網站提供的資料不是常見的json格式,因此提取會有點麻煩,比如通過字串查詢等,但是由於這個是js檔案,因此,我找到了更合適的方法——利用了PyExecJs模組就能很方便地編譯解析js程式碼啦。
現在直接上程式碼。
首先終端裡, pip install PyExecJs
安裝上該模組。然後引入這些模組
import requests import time import execjs
介面構造
構造一個url
def getUrl(fscode): head = 'http://fund.eastmoney.com/pingzhongdata/' tail = '.js?v='+ time.strftime("%Y%m%d%H%M%S",time.localtime()) return head+fscode+tail
獲取淨值
def getWorth(fscode): #用requests獲取到對應的檔案 content = requests.get(getUrl(fscode)) #使用execjs獲取到相應的資料 jsContent = execjs.compile(content.text) name = jsContent.eval('fS_name') code = jsContent.eval('fS_code') #單位淨值走勢 netWorthTrend = jsContent.eval('Data_netWorthTrend') #累計淨值走勢 ACWorthTrend = jsContent.eval('Data_ACWorthTrend') netWorth = [] ACWorth = [] #提取出裡面的淨值 for dayWorth in netWorthTrend[::-1]: netWorth.append(dayWorth['y']) for dayACWorth in ACWorthTrend[::-1]: ACWorth.append(dayACWorth[1]) print(name,code) return netWorth, ACWorth
檢視資料
這樣我們就可以通過基金程式碼來查到對應的資料啦
netWorth, ACWorth = getWorth('003511') print(netWorth)
可以看到,最近一天的淨值是1.0831,從網站上我們也可以驗證一下這個資料是否正確


當然,我們也可以自己畫一個走勢圖來驗證一下
import matplotlib.pyplot as plt plt.figure(figsize=(10,5)) plt.plot(netWorth[:60][::-1]) plt.show()

可以看到,和天天基金網畫的是一樣的。
不過這個方法獲取的資料有個小問題,就是無法獲得對應的確切日期。我們如果分析最近幾個周、幾個月的資料,其實也可以不需要了解具體某一天的資料,取最近20天、40天等方式即可。當然,也可以從當天開始逆推回去,給每個淨值標上日期,不過這個需要忽略節假日,處理起來比較麻煩且必要性不大,我就沒有做這個處理。
獲取所有基金資料
這裡我通過同樣的方式,找到了所有基金列表的介面。
通過 'http://fund.eastmoney.com/js/fundcode_search.js'
便可以直接獲取到所有的基金程式碼,再通過基金程式碼可以遍歷爬取所有基金的資料,具體就不再演示了,下面提供一個可用的程式碼供參考。
我將下載的資料存成了csv,方便excel開啟或用程式碼讀取。當然,總共有近8000支基金,爬取需要大量的時間,因此我將它放在了伺服器後臺爬取,如果你想提高效率,可以改寫成多程序同步爬取,時間將會大大縮短。
import requests import time import execjs def getUrl(fscode): head = 'http://fund.eastmoney.com/pingzhongdata/' tail = '.js?v='+ time.strftime("%Y%m%d%H%M%S",time.localtime()) return head+fscode+tail # 根據基金程式碼獲取淨值 def getWorth(fscode): content = requests.get(getUrl(fscode)) jsContent = execjs.compile(content.text) name = jsContent.eval('fS_name') code = jsContent.eval('fS_code') #單位淨值走勢 netWorthTrend = jsContent.eval('Data_netWorthTrend') #累計淨值走勢 ACWorthTrend = jsContent.eval('Data_ACWorthTrend') netWorth = [] ACWorth = [] for dayWorth in netWorthTrend[::-1]: netWorth.append(dayWorth['y']) for dayACWorth in ACWorthTrend[::-1]: ACWorth.append(dayACWorth[1]) print(name,code) return netWorth, ACWorth def getAllCode(): url = 'http://fund.eastmoney.com/js/fundcode_search.js' content = requests.get(url) jsContent = execjs.compile(content.text) rawData = jsContent.eval('r') allCode = [] for code in rawData: allCode.append(code[0]) return allCode allCode = getAllCode() netWorthFile = open('./netWorth.csv','w') ACWorthFile = open('./ACWorth.csv','w') for code in allCode: try: netWorth, ACWorth = getWorth(code) except: continue if len(netWorth)<=0 or len(ACWorth)<0: print(code+"'s' data is empty.") continue netWorthFile.write("\'"+code+"\',") netWorthFile.write(",".join(list(map(str, netWorth)))) netWorthFile.write("\n") ACWorthFile.write("\'"+code+"\',") ACWorthFile.write(",".join(list(map(str, ACWorth)))) ACWorthFile.write("\n") print("write "+code+"'s data success.") netWorthFile.close() ACWorthFile.close()
這是我用伺服器爬取的資料,可以看到,總共大概35M+35M。
