用Python爬取歷年基金數(shù)據(jù)

寫在前面

忙于學(xué)習(xí),我已經(jīng)好久沒有寫過博客了。最近,由于越來越意識(shí)到了理財(cái)?shù)闹匾裕谑俏疫x擇了從最容易入門且風(fēng)險(xiǎn)較低的基金入手,看的同時(shí)也能夠?qū)W習(xí)到一些金融知識(shí)。不過,不買就沒有看的欲望,所以我也還是選擇了幾支基金入手了,當(dāng)然,最終還是以學(xué)習(xí)為主,收益只能作為一個(gè)檢驗(yàn)學(xué)習(xí)效果的手段,而不是我當(dāng)前的目的(是未來的目的hhh)。
要想分析基金,少不了歷年的數(shù)據(jù),為了分析方便,我還是覺得先把所有的數(shù)據(jù)爬下來,然后再做進(jìn)一步處理。

接口分析

爬數(shù)據(jù)需要先思考從哪里爬?經(jīng)過一番搜索和考慮,我發(fā)現(xiàn)天天基金網(wǎng)的數(shù)據(jù)既比較全,又十分容易爬取,所以就從它入手了。

首先,隨便點(diǎn)開一支基金,我們可以看到域名就是該基金的代碼,十分方便,其次下面有生成的凈值圖。


基金詳情

打開chrome的開發(fā)者調(diào)試,選擇Network,然后刷新一下,很快我們就能發(fā)現(xiàn)我們想要的東西了。可以看到,這是基金代碼加當(dāng)前時(shí)間的一個(gè)接口,請(qǐng)求的url是http://fund.eastmoney.com/pingzhongdata/003511.js?v=20190304115823
也就是說我們可以簡(jiǎn)單的通過http://fund.eastmoney.com/pingzhongdata/基金代碼.js?v=當(dāng)前時(shí)間這樣一個(gè)接口就能獲取到相應(yīng)的數(shù)據(jù)了。

打開開發(fā)者模式

現(xiàn)在我們來看看這個(gè)文件的具體內(nèi)容是什么?
顯然,這里面的東西就是我們想要的,Data_netWorthTrend里面的"y"就包含了每一天的凈值

獲取數(shù)據(jù)

現(xiàn)在我們的接口已經(jīng)十分明確了,就是http://fund.eastmoney.com/pingzhongdata/基金代碼.js?v=當(dāng)前時(shí)間

通過基金代碼當(dāng)前時(shí)間我們就能夠獲取到相應(yīng)的數(shù)據(jù),接下來就是需要將我們想要的數(shù)據(jù)從獲取的文件中提取出來了,也就是我們說的數(shù)據(jù)清洗的過程。
這個(gè)網(wǎng)站提供的數(shù)據(jù)不是常見的json格式,因此提取會(huì)有點(diǎn)麻煩,比如通過字符串查找等,但是由于這個(gè)是js文件,因此,我找到了更合適的方法——利用了PyExecJs模塊就能很方便地編譯解析js代碼啦。

現(xiàn)在直接上代碼。
首先終端里,pip install PyExecJs安裝上該模塊。然后引入這些模塊

import requests
import time
import execjs

接口構(gòu)造

構(gòu)造一個(gè)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獲取到對(duì)應(yīng)的文件
    content = requests.get(getUrl(fscode))
    
   #使用execjs獲取到相應(yīng)的數(shù)據(jù)
    jsContent = execjs.compile(content.text)
    name = jsContent.eval('fS_name')
    code = jsContent.eval('fS_code')
    #單位凈值走勢(shì)
    netWorthTrend = jsContent.eval('Data_netWorthTrend')
    #累計(jì)凈值走勢(shì)
    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

查看數(shù)據(jù)

這樣我們就可以通過基金代碼來查到對(duì)應(yīng)的數(shù)據(jù)啦

netWorth, ACWorth = getWorth('003511')
print(netWorth)

可以看到,最近一天的凈值是1.0831,從網(wǎng)站上我們也可以驗(yàn)證一下這個(gè)數(shù)據(jù)是否正確


當(dāng)然,我們也可以自己畫一個(gè)走勢(shì)圖來驗(yàn)證一下

import matplotlib.pyplot as plt
plt.figure(figsize=(10,5))
plt.plot(netWorth[:60][::-1])
plt.show()

可以看到,和天天基金網(wǎng)畫的是一樣的。
不過這個(gè)方法獲取的數(shù)據(jù)有個(gè)小問題,就是無法獲得對(duì)應(yīng)的確切日期。我們?nèi)绻治鲎罱鼛讉€(gè)周、幾個(gè)月的數(shù)據(jù),其實(shí)也可以不需要了解具體某一天的數(shù)據(jù),取最近20天、40天等方式即可。當(dāng)然,也可以從當(dāng)天開始逆推回去,給每個(gè)凈值標(biāo)上日期,不過這個(gè)需要忽略節(jié)假日,處理起來比較麻煩且必要性不大,我就沒有做這個(gè)處理。

獲取所有基金數(shù)據(jù)

這里我通過同樣的方式,找到了所有基金列表的接口。
通過'http://fund.eastmoney.com/js/fundcode_search.js'便可以直接獲取到所有的基金代碼,再通過基金代碼可以遍歷爬取所有基金的數(shù)據(jù),具體就不再演示了,下面提供一個(gè)可用的代碼供參考。
我將下載的數(shù)據(jù)存成了csv,方便excel打開或用代碼讀取。當(dāng)然,總共有近8000支基金,爬取需要大量的時(shí)間,因此我將它放在了服務(wù)器后臺(tái)爬取,如果你想提高效率,可以改寫成多進(jìn)程同步爬取,時(shí)間將會(huì)大大縮短。

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

# 根據(jù)基金代碼獲取凈值
def getWorth(fscode):
    content = requests.get(getUrl(fscode))
    jsContent = execjs.compile(content.text)
    
    name = jsContent.eval('fS_name')
    code = jsContent.eval('fS_code')
    #單位凈值走勢(shì)
    netWorthTrend = jsContent.eval('Data_netWorthTrend')
    #累計(jì)凈值走勢(shì)
    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()

這是我用服務(wù)器爬取的數(shù)據(jù),可以看到,總共大概35M+35M。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容