Python爬虫之——爬取基金数据

要点:

​1、数据的分析与爬取

2、pyecharts绘图

数 据 源

数据来源我们选择新浪财经基金板块:https://finance.sina.com.cn/fund/

我们随便选一个基金,就选第一个吧:660001,农银行业成长混合A

老办法,监控Network

分析新浪财经基金板块Network

通过分析,我们发现了三个request:

今日的实时数据从下面的连接返回

https://app.xincai.com/fund/api/jsonp.json/var%20t1fu_660001=/XinCaiFundService.getFundYuCeNav?symbol=660001&___qn=3

返回信息格式如下

var t1fu_660001=({"yes":"2.6775","detail":"09:30,2.6822,09:31,...,15:03,2.7189"});

历史数据从下面的连接返回

https://finance.sina.com.cn/fund/api/xh5Fund/nav/660001.js

返回信息格式如下

xh5Fund({"data":"20200220,2.6775,3.2775,4.15436,82.38,81.8,56.24#

20200219,2.6223,3.2223,4.06871,84.26,81.19,55.47#...#20080804,1,1,1,,,","symbol":"660001","fhday":"20091123,20100426","fhvalue":"0.4,0.2","fhchaifen":"0,0"})

数据顺序为:日期、单位净值、累积净值、历史回报、排位百分比、排位百分比、排位百分比

查询时间下的基金综合数据

https://hq.sinajs.cn/list=fu_660001

返回信息
var hq_str_fu_660001="农银行业成长混合A

,15:04:00,2.6722,2.6223,3.2223,0.0262,1.9029,2020-02-20";

爬取并解析数据

了解了以上信息,我们就可以用requests获得返回的数据。

我们可以先定义header,假装是浏览器访问网站

header = {
    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3704.400 SLBrowser/10.0.3929.400",
}

实时数据的解析

解析返回的实时数据字符串,按照 左括号 ( 与右括号 ) 拆分,将提取出的大括号{}中的数据利用ast.literal_eval转换为字典。yes对应的字符串直接转换为float,detail对应的字符串按照 逗号 , 拆分,每两个数据一组,第一个为“时间”保持字符串即可,第二个为“实时数据”转换为float。将解析好的数据再次打包为字典即可备用。

历史数据的解析

历史数据的解析,与实时数据基本相似,也是先通过对左右括号的拆分,提取出需要的字典字符串数据,而后利用ast.literal_eval将字符串转为字典。字典的data中是每日数据,这里用 # 号拆分历史每日数据字符串,再将每一天的数据字符串用 逗号, 拆分,依照数据顺序转换为float打包为最终字典即可。

定义抓取数据函数代码,返回 实时数据字典 与 每日数据字典

# -*- coding:utf-8 -*-
import requests
import re
import ast
header = {
    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3704.400 SLBrowser/10.0.3929.400",
}
today_url_format = "https://app.xincai.com/fund/api/jsonp.json/var%20t1fu_{}=/XinCaiFundService.getFundYuCeNav?symbol={}&___qn=3"
both_url_format = "https://finance.sina.com.cn/fund/api/xh5Fund/nav/{}.js"
name_url_format = "https://hq.sinajs.cn/list=fu_{}"
def catchData(code):
    today_url = today_url_format.format(code,code)
    both_url = both_url_format.format(code)
    name_url = name_url_format.format(code)
    #获得当天数据,步长为分钟
    today_response = requests.get(today_url,headers=header)
    today_str = re.split("[()]",today_response.text)[1]#通过()拆分字符串
    today_dict_one = ast.literal_eval(today_str)#初步生成字典
    minute_list_strs = today_dict_one['detail'].split(',')#拆分字典中detail的字符串
    minute_dict = {}
    for i in range(0,len(minute_list_strs),2):
        minute_dict[minute_list_strs[i]] = float(minute_list_strs[i+1])
    today_dict = {"yes":float(today_dict_one["yes"]),"detail":minute_dict}
    #获得全部数据,步长为天
    both_response = requests.get(both_url,headers=header)
    both_str = re.split("[()]",both_response.text)[1]
    both_dict_one = ast.literal_eval(both_str)
    day_list_strs = both_dict_one['data'].split('#')
    day_list = {}
    for i in range(len(day_list_strs)):
        str = day_list_strs[i];
        str_list = str.split(',')
        day_list[str_list[0]] = {"dwjz":str2float(str_list[1]),"jljz":str2float(str_list[2]),
                                 "lshb":str2float(str_list[3]),"mper":str2float(str_list[4]),
                                 "qper":str2float(str_list[5]),"yper":str2float(str_list[6])}
    both_dict = {"symbol":both_dict_one["symbol"],"fhday":both_dict_one["fhday"],
                 "fhvalue":both_dict_one["fhvalue"],"fhchaifen":both_dict_one["fhchaifen"],
                "data":day_list}
    #获取基金名称
    name_response = requests.get(name_url,headers=header)
    name_str = name_response.text.split('="')[1].split(',')[0]
    today_dict["name"] = name_str
    both_dict["name"] = name_str
    return today_dict,both_dict
def str2float(str):
    return 0 if ''==str else float(str)

调用函数

我们只需要给出基金代码,既可以获得数据字典

code = "660001"    
today_dict,both_dict = catchData(code) 

绘制数据曲线

pyecharts是非常强大的绘图工具,非常好用。分为 v0.5.X 和 v1 两个大版本,v0.5.X 和 v1 间不兼容,v1 是一个全新的版本。目前官方已经不再维护v0.5.X版本,因此本文建议读者使用v1版本。

:v1版本仅支持 Python3.6+

特别注意:本文使用的环境是一款神器jupyter lab,在该环境下可以使用

render_notebook()直接绘制pyecharts图形。但是因为其无法得知用户的具体开发环境,因此需要指定开发环境:

CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB

如果在调用render_notebook()时绘制的依然为空白,说明环境没有正确加载echarts.js等文件,只需要在绘制前调用:load_javascript()即可。

在jupyter lab环境中,调用load_javascript()与render_notebook()需要和设置图形的代码段分开。

具体详见官方文档:

https://pyecharts.org/#/zh-cn/notebook?id=jupyter-lab

绘制实时数据折线图

下面介绍一下绘制实时数据折线图的关键点:

1)通过is_smooth=True可以平滑折线图

2)通过extend_axis函数定义第二个y轴,并设置其格式:formatter="{value} %"

3)字典的keys方法可以将字典的所有索引提取为数组

4)字典的values方法可以将字典的所有值提取为数组

from pyecharts.globals import CurrentConfig, NotebookType
CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB
import pyecharts.options as opts
from pyecharts.faker import  Faker
from pyecharts.charts import Line
import numpy as np
yes = today_dict["yes"]
xaxis = list(today_dict["detail"].keys())#获得x轴数据
yaxis = list(today_dict["detail"].values())#获得y轴数据
yaxis_plus_yes = [yes-x for x in yaxis]
dy_max_abs = np.max(np.abs(yaxis_plus_yes))#用来计算百分比和坐标轴范围
def today_line_smooth() -> Line:
    c = (
        Line()
        .add_xaxis(xaxis)
        .add_yaxis("净值预测", yaxis , is_smooth=True,is_hover_animation=True,yaxis_index=0)
        .extend_axis(
            yaxis=opts.AxisOpts(
                axislabel_opts=opts.LabelOpts(formatter="{value} %"),
                min_=round(-dy_max_abs/yes*100,2),
                max_=round(dy_max_abs/yes*100,2)
            )
        )
        .set_global_opts(title_opts=opts.TitleOpts(title="Line-smooth"),
                         yaxis_opts=opts.AxisOpts(min_=round(yes-dy_max_abs,4),max_=round(yes+dy_max_abs,4)))
    )
    return c
line = today_line_smooth()
line.load_javascript()
line.render_notebook()

实时数据折线图

绘制历史单位净值及积累净值曲线

主要需要注意如下几点:

  1. 配置AxisOpts,min_=min,max_=max,使曲线上只提示最大值和最小值

2)配置TooltipOpts,使鼠标移动显示十字交叉线,并提示关键信息:

 trigger="axis",trigger_on="mousemove",axis_pointer_type="cross"

3)通过函数形式,返回JsCode字符串设置自定义Tooltip文字

3.1) 需要包含from pyecharts.commons.utils import JsCode

3.2)JsCode字符串中的字符串必须用单引号'包扩,否则会报错

3.3)函数的参数是个列表,具体形式可以通过console.log(params)查看,自定义文字所需要的数据均在params中,可以通过拼接字符串的型式,将python中的变量引入到JsCode中。

def getToolip():
    a1 = yaxis_dwjz[0]
    a2 = yaxis_jljz[0]
    return JsCode("""function (params) {
               var dwjz_per =  (params[0].value[1]-"""+str(a1)+""")/"""+str(a1)+""";
               dwjz_per = (dwjz_per*100).toFixed(2);
               var jljz_per = (params[1].value[1]-"""+str(a2)+""")/"""+str(a2)+""";
               jljz_per = (jljz_per*100).toFixed(2);
               return params[0].axisValue+'
'+ params[0].seriesName+' : '+params[0].value[1]+'('+dwjz_per+'%)
'+ params[1].seriesName+' : '+params[1].value[1]+'('+jljz_per+'%)
'; }""")

定义曲线

from pyecharts.commons.utils import JsCode
datas = both_dict["data"]
xaxis_all = list(datas.keys())
xaxis_all.sort()#升序排列,返回的数据是降序的
beginX = "20191113"#设置绘制的开始时间
endX = "20200221"#设置绘制的结束时间
xaxis = []
yaxis_dwjz = []
yaxis_jljz = []
for key in xaxis_all:#提取y轴数据
    if keyendX:
        continue
    data = datas[key]
    xaxis.append(key)
    yaxis_dwjz.append(data["dwjz"])
    yaxis_jljz.append(data["jljz"])
#计算坐标轴范围
max = round(1.05*np.max([yaxis_dwjz,yaxis_jljz]),1)
min = round(0.95*np.min([yaxis_dwjz,yaxis_jljz]),1)
dy_min = (min-yaxis_dwjz[0])/yaxis_dwjz[0]*100
dy_max = (max-yaxis_dwjz[0])/yaxis_dwjz[0]*100

def both_line_smooth_1() -> Line:
    c = (
        Line()
        .add_xaxis(xaxis)
        .add_yaxis("单位净值", yaxis_dwjz)
        .add_yaxis("积累净值", yaxis_jljz)
        .extend_axis(
            yaxis=opts.AxisOpts(
                axislabel_opts=opts.LabelOpts(formatter="{value} %"),
                min_=round(dy_min,1),
                max_=round(dy_max,1)
            )
        )
        .set_global_opts(title_opts=opts.TitleOpts(title="Line-smooth"),
                         yaxis_opts=opts.AxisOpts(min_=min,max_=max,interval=round((max-min)/8,1)),
                         tooltip_opts=opts.TooltipOpts(is_show=True,trigger="axis",
                                                       trigger_on="mousemove",axis_pointer_type="cross",
                                                       formatter=getToolip()))
        .set_series_opts(label_opts = opts.LabelOpts(is_show = False), is_smooth=True,is_hover_animation=True,
                         markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_ = "max",name = "max"),
                                                                 opts.MarkPointItem(name = "min",type_ = "min")]))
    )
    return c
line = both_line_smooth_1()
line.render_notebook()

历史数据折线图

通过同样的方式,绘制 历史回报曲线、以及排位百分比曲线

历史回报曲线

排位百分比曲线

总结

本文为处理量化投资数据,做了最基本的事情。爬取数据,解析,并且研究了如何美观的绘制折线图,希望对您有所帮助!

你可能感兴趣的:(Python爬虫之——爬取基金数据)