最近基金行情感觉很不错,想着再入手几只,于是有了通过数据分析来帮我做决策这个想法。由于我手里的是一只南方基金的产品,因此为了对比决定把南方基金的基金信息(截止2017.10.27)给扒下来。我仔细分析了该网站我要的产品信息所在的页面,最终决定将所有的产品信息保存为两个文件。一个文件记录基金的基本信息,比如:基金代码、基金名称、基金类型、风险等级等;另一个文件则记录基金的历史净值和收益等信息。
由于每只基金详细信息必须通过基金列表页面的链接点击过去,才能看到。因此第一步爬取列表页面,再通过正则表达式匹配所有的基金代码,再将代码拼接成请求每个基金产品数据的url。
def get_page_fundinfo(self,fundurl):
url = fundurl
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
'Cookie' : 'skillId=6; skillId=6; pageReferrInSession=https%3A//www.baidu.com/link%3Furl%3D54aALJ8xAviE_YUY4z_oXlXPauU2IzKa73YN60b5'
'tTe%26wd%3D%26eqid%3Df187ca530001f47d0000000459f4ac0f; skillId=6; JSESSIONID=sFYfZ1Ld9rq1nVDrw4QTLyvrjvVq0P0ZQzr5JnvyYPQY'
'c8kLJ6Kb!471155903; Hm_lvt_53143ec1a9124a0122dd24b077a6082d=1507820096,1509207062,1509207076,1509207130; Hm_lpvt_53143ec1a'
'9124a0122dd24b077a6082d=1509265974',
'Referer':'http://www.nffund.com/'
}
try:
request=urllib2.Request(url,headers=headers)
respose=urllib2.urlopen(request)
return respose.read()
except Exception,e:
print e
def get_fundcode_list(self, fundurl): # fundurl = 'http://www.nffund.com/new/fundproduct/' fundinfo = self.get_page_fundinfo(fundurl) pattern = re.compile('href="/new/fundproduct/static/searchjjgk_(.*?)_jjgk.html".*?title=', re.S) # 正则匹配基金代码 items = re.findall(pattern, fundinfo) fundcodelist = [] for item in items: fundcodelist.append(item) # print len(fundList) # 229只基金产品代码,正确 return fundcodelist
如上,将所有的基金代码保存在fundcodelist里面,通过循环取数据。由于基金详情页面用的模板生成,所以不能通过请求页面得到真实数据。解决办法是通过抓包获取页面通过get或post方式请求的后台地址,如下面的/FundNewServlet.java,再根据这个请求返回的结果组装数据。
for fundcode in fundcodelist: # 获取基金基本信息 fundinfo_url = self.url + '/FundNewServlet.java?action=searchjjgk&fundcode=' + fundcode # 获取基金历史净值和收益 fundvalue_url = self.url + '/FundNewServlet.java?action=searchjjjz&pageSize=99999×tart=&timeend=&pageNo=&only_syl=1&fundcode=' + fundcode nfspider.save_fundinfo(fundinfo_url,1) nfspider.save_fundinfo(fundvalue_url,2)
抓包结果示例如下:
接下来是最重要的处理部分,上面通过直接请求Servlet后返回的是json格式的数据,在Python中,可以用字典类型来处理。
'''基金产品基本信息''' def get_compile_fundinfo(self,fundurl): page_fundinfo = self.get_page_fundinfo(fundurl) # 请求severlet返回json格式数据 dic_fundinfo=json.loads(page_fundinfo) # ['基金代码', '基金名称', '基金类型', '运作方式', '风险等级', '资产规模', '基金托管人', '基金状态'] fundcode=fundname=styledes=operatorstyle=risk=jjtgr=statedes='' fundasset=0 '''由于dicFundInfo可能部分产品属性不全,这样判断后处理能兼容很多错误情况''' if dic_fundinfo.get('data').get('fund_info') and dic_fundinfo.get('data').get('fund_info').get('fundCode'): fundcode=dic_fundinfo.get('data').get('fund_info').get('fundCode') if dic_fundinfo.get('data').get('fund_info') and dic_fundinfo.get('data').get('fund_info').get('fundName'): fundname = dic_fundinfo.get('data').get('fund_info').get('fundName') if dic_fundinfo.get('data').get('fund_info') and dic_fundinfo.get('data').get('fund_info').get('style_des'): styledes=dic_fundinfo.get('data').get('fund_info').get('style_des') if dic_fundinfo.get('data').get('fund_info') and dic_fundinfo.get('data').get('fund_info').get('operatorStyle'): operatorstyle=dic_fundinfo.get('data').get('fund_info').get('operatorStyle') if dic_fundinfo.get('data').get('fundRiskRating') and dic_fundinfo.get('data').get('fundRiskRating').get('RISKRATING'): risk=dic_fundinfo.get('data').get('fundRiskRating').get('RISKRATING') if dic_fundinfo.get('data').get('quarter') and dic_fundinfo.get('data').get('quarter').get('FUNDASSET'): fundasset=dic_fundinfo.get('data').get('quarter').get('FUNDASSET') if dic_fundinfo.get('data').get('fund_info') and dic_fundinfo.get('data').get('fund_info').get('jjtgr'): jjtgr=dic_fundinfo.get('data').get('fund_info').get('jjtgr') if dic_fundinfo.get('data').get('fund_info') and dic_fundinfo.get('data').get('fund_info').get('state_des'): statedes=dic_fundinfo.get('data').get('fund_info').get('state_des') # print [fundcode, fundname, styledes, operatorstyle, risk, fundasset, jjtgr, statedes] return [fundcode, fundname, styledes, operatorstyle, risk, fundasset, jjtgr, statedes]
'''基金产品历史净值''' def get_compile_fundvalue(self,fundurl): page_fundvalue = self.get_page_fundinfo(fundurl) # 请求severlet返回json格式数据 dic_fundvalue=json.loads(page_fundvalue) #['基金代码', '基金名称', '净值日期', '基金净值', '累积净值', '日涨幅', '最近半年涨幅', '最近一年涨幅', '今年以来涨幅', '成立以来涨幅'] fundcode=fundname=funddate='' fundasset=fundnav=ljfundnav=upratio=recent_halfyear=recent_oneyear=thisyear=since=0 fundnavlist = dic_fundvalue.get('data').get('jjjzList') # 每只产品多条净值数据 fundnavarr=[] '''同样由于dicFundValue可能部分产品属性不全,会报NoneType错误,因此先要判断''' for fnav in fundnavlist: if fnav.get('fundCode'): fundcode=fnav.get('fundCode') if fnav.get('fundName'): fundname=fnav.get('fundName') if fnav.get('fdate'): funddate=fnav.get('fdate') if fnav.get('fundNav'): fundnav=fnav.get('fundNav') if fnav.get('ljfundNav'): ljfundnav=fnav.get('ljfundNav') if fnav.get('upRatio'): upratio=fnav.get('upRatio') if fnav.get('sylBo') and fnav.get('sylBo').get('recentHalfYear'): recent_halfyear=fnav.get('sylBo').get('recentHalfYear') if fnav.get('sylBo') and fnav.get('sylBo').get('recentOneYear'): recent_oneyear=fnav.get('sylBo').get('recentOneYear') if fnav.get('sylBo') and fnav.get('sylBo').get('thisYear'): thisyear=fnav.get('sylBo').get('thisYear') if fnav.get('sylBo') and fnav.get('sylBo').get('since'): since=fnav.get('sylBo').get('since') fundnavarr.append([fundcode, fundname, funddate, fundnav, ljfundnav, upratio, recent_halfyear, recent_oneyear, thisyear, since]) # print fundnavarr return fundnavarr
上面两个方法分别获取基金的基本信息和历史净值收益信息,而每个基金代码对应一条基本信息,每个基金代码对应多条净值及收益数据,因此处理略有不同。
通过Python爬虫获取南方基金所有基金产品数据大致就是这样。最后将爬取的数据保存下来。
基金产品基本信息,共229只基金(截止2017.10.27):
基金净值、收益信息,共153762条数据(截止2017.10.27):
至此,爬数据的过程就算完成了,接下来就需要根据这些数据来获取我想要的信息了。