局面一张图
信任咱们都曾在YouTube和B站看到过相似的视频,这种图在国外叫做BarChartRace,配上一段气势磅礴的BGM,就会营造出一种「浮沉跌宕」的沉溺感,这类型的视频许多都取得了相当可观的播映量。
因为这类视频的大火,网络上已经有专门的制造东西,而且都以NO-CODING为营销卖点,也进一步导致了该类视频的「众多」。不过作为一个喜爱折腾的数据剖析工程师,仍是习气经过手打代码的办法来完结。
数据源可获取的数据有许多,这次也蹭把热门,以近期打工人都想进场大干一番的股市为主题,将历年TOP10的A股股票经过动态排行图将其展现出来。
既然是关于股市的数据,那能够直接在证券交易所的官网查询到相关的数据。果不其然,上海证券交易所官网数据板块中,有向广阔投资者供给「市值排名」的查询进口(sse/market/stockdata/marketvalue/main/),点击进去会看到,咱们「股票市价总值排名前十名」的报表,并能够经过日期筛选框进行查询。
数据源承认了,需求对接下来的工作流进行整理。
数据流剖析网站剖析在网页上更改日期查询后,网址没有改动,页面也没有改写,初步判断经过Ajax进行异步更新。在Chrome浏览器上,右键点击inspect,检查Network模块下的JS标签。
这时再次切换查询日期,便会在JS标签左边面板里找到真实的恳求URL(如query.sse/marketdata/tradedata/queryTopMktValByPage.do?&jsonCallBack=jsonpCallback12925&isPagination=true&searchDate=2021-01-01&_=1610296018800),可见恳求URL需求咱们装备以下的参数:
jsonCallBack:测验后不传入也不影响isPagination:truesearchDate:查询日期_:时刻戳,不传入也不影响点击恳求URL后能够经过右侧面板的Preview、Response标签协助咱们检查该条恳求是不是有爬虫想要的数据回来成果中。
数据抓取Requests库对其进行抓取,Requests库是Python最简略易用的HTTP库,咱们能够经过它来构建URL的恳求,并获取其response成果。
一般来说,要构建一个HTTP恳求,需求传入恳求头(header),恳求地址,恳求办法(GET或POST等)和HTTP协议版别。别的,依据前面的网站剖析,咱们还需求给URL传入参数,Requests库供给了params关键字参数,答应咱们以一个字典来装备URL所需的参数。
importrequestsparams=params={"isPagination":"true","searchDate":"2021-01-11"}headers={"Referer":"sse/market/stockdata/marketvalue/","Accept-Encoding":"gzip,deflate","Connection":"keep-alive","User-Agent":"Mozilla/5.0(Macintosh;IntelMacOSX10_15_4)AppleWebKit/537.36(KHTML,likeGecko)Chrome/83.0.4103.97Safari/537.36"}url="query.sse/marketdata/tradedata/queryTopMktValByPage.do"response=requests.get(url,headers=headers,params=params)print(response.text)最终response.text的输出成果是一个嵌套式的JSON串,咱们想要的市值、排名等数据便藏在result那里
接着,合作正则表达式对response.text的输出成果截取出方针数据
#接上importretext=response.textresult=re.search('"result":[(.*?)]',text).group(1)temp={}stock_info=re.findall('"market":"(.*?)",.*?"productA":"(.*?)",.*?"productName":"(.*?)",.*?"rank":(.*?)}',result,re.DOTALL)f=open(file_path '/stock_history_market_value.csv','a ',newline='')print('正在写入:',trade_date)writer=csv.DictWriter(f,['year','trade_date','code','stock_name','market_value','rank'])forinfoinstock_info:temp={"year":2021,"trade_date":"2021-01-11","code":info[1],"stock_name":info[2],"market_value":info[0],"rank":info[3]}print(temp)writer.writerow(temp)print('已完结',trade_date)履行完结后就会发现程序目录多了一个文件stock_history_market_value.csv
因为动态排行图需求用到历年的数据,需求有必要将上面写入的csv的过程封装到spider_market_value函数中,以便复用。考虑到数据量的问题,这儿只对历年(2000年起)每个月的最终一天的数据进行抓取,别的,相同对该履行命令封装到函数中,便利传参履行。
defget_monthly_market_value(year):#假如参数是本年,则取本月前每个月取最终一天的市值排名,本月则取脚本时刻的前一天的市值排名ifyear==datetime.date.today().year:this_month=datetime.date.today().monthformonthinrange(1,this_month 1):ifmonth==datetime.date.today().month:trade_date=(datetime.date.today()-timedelta(days=1)).strftime('%Y-%m-%d')spider_market_value(year,trade_date)else:trade_date=str(year) '-' str(month) '-' str(calendar.monthrange(year,month)[1])spider_market_value(year,trade_date)#假如参数为历年,则取每个月最终一天的市值排名else:formonthinrange(1,13):trade_date=str(year) '-' str(month) '-' str(calendar.monthrange(year,month)[1])spider_market_value(year,trade_date)给get_monthly_market_value(year)传入年份,便可抓取到对应年份每个月的数据,并汇总写入到stock_history_market_value.csv文件中。
这样,数据部分就预备好了。
绘图可视化在生成动态图之前,先查阅下所用的库与函数的用法,本文将以经典可视化库matplotlib里的animation.FuncAnimation为例,调用前需了解该办法的参数,以便承认下一步的预备工作。
从官网文档能够检查到animation.FuncAnimation主要参数阐明:
fig-传入画布方针,能够经过fig,ax=plt.subplots()创立;func-每一帧更新时所调用的(绘图)函数(如下方要新建的draw_barchart()函数)frames-func函数的参数,作为帧序列,靠它图例才会动态改变#给每一个股票随机一种色彩random.seed(444)get_colors=lambdan:list(map(lambdai:"#" "x"%random.randint(0x111111,0xffffff),range(n)))colors=get_colors(df['code'].nunique())codecolors=dict()uni_code=set(df['code'])forcode,colorinzip(uni_code,colors):codecolors[code]=colordefdraw_barchart(trade_date):plt.rcParams['font.sans-serif']=['MicrosoftYaHei']plt.rcParams['animation.embed_limit']=2**128#读取当天的数据df_date=df[df['trade_date']==trade_date]df_date=df_date.sort_values(by=['market_value'],ascending=True)#每次制作前必须先清空画布,否则图画会堆叠的ax.clear()#制作水平柱状图ax.barh(df_date['stock_name'].astype(str),df_date['market_value'],color=[codecolors[c]forcindf_date['code']])#符号案牍dx=df_date['market_value'].max()/200fori,(value,code)inenumerate(zip(df_date['market_value'],df_date['stock_name'].astype(str))):ax.text(value-dx,i,code,size=14,weight=600,ha='right',va='bottom')ax.text(value dx,i,f'{value:,.0f}',size=14,ha='left',va='bottom')#符号帧日期ax.text(1,0.45,trade_date.split('-')[0] '-' trade_date.split('-')[1],transform=ax.transAxes,color='#777777',size=46,ha='right')#符号轴标签ax.text(0,1.06,"市值(万元)",transform=ax.transAxes,size=12,color='#777777')#设置X轴坐标的方位为顶部ax.xaxis.set_ticks_position('top')#设置X轴坐标的色彩和字体大小ax.tick_params(axis='x',color='#777777')ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.0f}'))#设置图形与边框的距离ax.margins(0,0.01)ax.grid(which='major',axis='x',linestyle='-')ax.set_axisbelow(True)#设置标题ax.text(0.3,1.05,'历年市值前10股票',transform=ax.transAxes,size=48,weight=600,ha='left')#去掉边框plt.box(False)fig,ax=plt.subplots(figsize=(22,10))animator=animation.FuncAnimation(fig,draw_barchart,frames=trade_date_list,interval=125)HTML(animator.to_jshtml())将draw_barchart()作为数据更新函数,月份作为frames帧序列,履行上面的句子,稍等片刻,文章最初的动态排行图便出来了:
动画的流通程度除取决于FuncAnimation的iterval参数(用于设置换帧的时刻距离),也取决于每帧数据的距离,距离越小,按帧播映时就越顺滑,原理跟皮影戏相同,因而,假如要想取得更顺滑的动画,能够考虑下按日或按周抓取方针数据,当然届时要处理的数据量也就越大,运转时刻和功能问题也是需求考虑的点,咱们无妨多调试测验下。