因为宿舍想搞个云顶之弈排名,我并没有找到腾讯官方查询战绩的网站,所以决定对云顶官网的排行榜下手,云顶官网的数据排行网页(下链接)对排名的显示是一个可以滑动的divbox
https://lol.qq.com/tft/#/rank/list
可以发现当鼠标放到排行榜上滑动滚轮时,后续排名数据会被不断显示出来,推断这个排名数据应该是被js动态加载出来的,不能通过简单爬html文件的方式抓取到,所以必须通过找到ajax请求的方式用python模拟发送请求进而获得服务器返回数据。
在浏览器开发者模式中打开<网络>选项卡,在排行榜上滑动滚轮时,每滑动几下就会出现一些网络请求,进行筛选后,得到形如
“http://qt.qq.com/lua/mlol_battle_info/get_total_tier_rank_list?area_id=XX&offset=XX&sign=XX”
的post请求url,用request.post()进行测试后,发现其返回值是一个json对象(如下图所示)
,内包含20个人的ID,排名,段位等信息,其中area_id是所在大区id,offset为排名(如想得到20-40名的数据offset就为20,网页每划过20人就对服务器发送一次请求),sign是网页为了反爬虫而设下的签名值,每次请求都不一样,要爬取所有的数据,就要对sign的生成进行解析,才能每次发送正确的请求得到数据。因为ajax请求一定是由js发送的,所以在网页的js文件中找到请求发送函数就能看到sign签名的生成过程,寻找后发现在"api.js"中可以找到如下代码段
getAreaTierRank: function (area_id, offset) {// 获取sign参数var params = "area_id=" + area_id + "&offset=" + offset;var key = "qtld^xibt#a*";var sign = hex_md5(params + key);// body数据var withdata = {next_offset: "",player_list: []};return this.baseRequestPromise('//qt.qq.com/lua/mlol_battle_info/get_total_tier_rank_list?area_id=' + area_id + '&offset=' + offset + '&sign=' + sign, { credentials: 'include', method: 'POST',body: JSON.stringify(withdata)});}
sign是由hex_md5()函数计算出来的,其中传参params是一段当前url内的字符串,key值则为一个规定好的字符串,md5是一个加密算法,只要得到传入的字符串,我们就可以也用md5算法得到sign值,至此请求url被解析完成,后续就是用python自动生成每次请求的url,得到所有返回值并进行格式处理就可以了。下面粘上源代码
import requests
import hashlibdef signCal(areaID,offset):str1 = 'area_id='+areaID+'&offset='+str(offset)+'qtld^xibt#a*' #用于计算sign签名的字符串,最后一项为keyb = str1.encode(encoding='utf-8')m = hashlib.md5()m.update(b)str_md5 = m.hexdigest() #MD5编码return str_md5def getReqURL(areaID,count):offset = 20*count;str_ = "http://qt.qq.com/lua/mlol_battle_info/get_total_tier_rank_list?" #请求url前面不变部分return str_+'area_id='+areaID+'&offset='+str(offset)+'&sign='+signCal(areaID,offset) #请求urlname = []
ranking = []
points = []
title = [] #存储返回数据
for i in range(0,1): #一次请求有20个数据,range()中可以随意填写[0,250]内数,以获取前5000名数据r= requests.post(getReqURL('12',i)) #得到返回数据tem = r.json()temm = tem['data']['player_list']for a in temm:name.append(a['name'])ranking.append(a['ranking'])points.append(a['league_points'])title.append(a['tier_title']) #解析
k = 0
for j in name:print(str(k+1)+'\tname: '+str(name[k])+'\ttitle: '+str(title[k])+'\tranking: '+str(ranking[k])+'\tpoints: '+str(points[k]))k+=1
输出(无畏先锋服务器4981-5000名为例):