许多网站都有反爬机制,x-sign加密就是许多反爬虫机制的其中一种,本次将以易车号作为目标进行演示。
方法仅供学习参考。
链接:https://hao.yiche.com
用到的库:requests , urllib3 , time , hashlib
pip install hashlib
首先判断页面内容的更新方式,打开F12开发者工具,将页面一直下拉到底部,页面会自动更新出下一页内容,很明显是动态加载的,因此可以确定是Ajax请求。
在网络工具中选择Fetch/XHR,只有几条请求,一条一条查看,发现get_latest_video_list这一条包含了页面内容,在请求了几次之后,每次都是这个名字的请求携带内容,因此爬取目标就先确定为这条请求。
点击标头,在请求头中首先可以看到请求URL请求方式。
继续往下拉,可以看到x-sign,说明这条请求是经过x-sign加密的,因此就需要把请求头中的一些内容进行处理后再进行提交。首先看到有一条名字叫做x-timestamp的内容,很容易想到时间戳,调用 time.time() 后返回的内容和这条内容非常接近,也就可以确当这条内容是当前时间戳,并且长度为13位
经过测试发现,其中有三条是必须填写并且需要对应上的:x-sign(密钥),x-platform(设备类型),x-timestamp(时间戳)
点击负载选项,可以看到请求携带了一些json参数,在发送请求时需要加入请求头。
爬虫的基本框架基本就是这样了,只需要把x-sign获取到就可以了。另外,requests再爬取https时会报错,需要引用urllib3后加上一句urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)就可以了
import requests,json,urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def down(url):
headers = {
"authority": "hao.yiche.com",
"x-platform": "pc",
"x-sign": "",
"x-timestamp": "",
"x-user-guid": "a9f123e80087cd4aec7983ad6d611113"
}
jsons = ''
res = requests.post(url=url,headers=headers,verify=False,json=jsons)
return res
if __name__ == '__main__':
url = 'https://hao.yiche.com/site_web/hao/api/get_latest_video_list'
res = down(url).json()
print(res)
InsecureRequestWarning: Unverified HTTPS request is being made to host 'hao.yiche.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
warnings.warn(
{'status': '10600', 'message': 'JSON序列化异常 或 Request body is missing', 'data': None}
获取x-sign首先找到源代码位置,利用Ctrl+Shift+F全局搜索x-sign,
将js文本格式化之后可以看到x-sign的值是s(e,t)的返回值,另外在上边看到了些熟悉的东西,刚才提到的头文件中三个必需值中有一个是设备类型,而这里写的name值应该就是定义的几种设备类型和对应的Value值,先记下来备用,例如我用PC端,就记下Value= “19DDD1FBDFF065D3A4DA777D2D7A81EC”, cid = “508”
在这里打上断点,下滑页面再次发送一条请求,可以看出,返回来cid值也确实是508,因此可以确定这里的508页确实和上边的一样是写死的参数。
接下来看 s(e,t)的位置,末尾写着返回值是s,而s就是对n进行md5加密,n则是由"cid=" + t.cid + “¶m=” + i + o + t.timestamp这一串内容拼接而来。我们只需要知道t.cid、i、o、t.timestamp的值。
1、其中cid就是上边已知的定义好的值。例如pc的cid为508。
2、从内容上来看,i 貌似是定义页面的参数也是写死的{“pageIndex”:8,“pageSize”:10},经过几次请求后发现也确实是写死的参数
3、根据请求的内容可以看到timestamp是长度13位的数字,因此不妨假设他就是长度为13位的时间戳。
4、o的内容就很明显了,和刚才看到的一模一样,大概率也是写死的,需要name,value,cid同时对应
因此四个值都已经的出来了,只需要进行一次md5加密就可以得出x-sign值了,下面来放进代码里验证一下
利用第三方库对拼接后的内容进行md5加密
import hashlib
hex = hashlib.md5()
hex.update(sign.encode('utf-8'))
sign = hex.hexdigest()
调用time.time()时返回的是一个小数点前10位的浮点数,需要将返回值乘以1000后使用int()取整,并且用str() 转换为字符串后进行拼接,否则会报出类型错误
在拼接时需要注意一下pageIndex的值是否和页面的对应,例如页面给出的是pageindex=5,pagesize=10,若在这里写成pageindex=1或者其他非5的数字,就会报出签名错误或者json非法。
过程不算很难,但是很考验耐心和细节
没有接受挑战,没有失败经验,怎进步向前?
源代码已经提交至Gitee:逆向 · Primice/Python演示 - 码云 - 开源中国 (gitee.com)
爬虫请勿过度使用,使用时需遵守robots协议。