前言
反爬策略中最重要的一块领域就是在前端利用JS对数据请求参数进行加密或者数据隐藏(如大众点评的图像位移技术,即使使用代理也是无济于事,必须对JS进行驱动才能获取到数据。
这里有两种方案,一种是用selenium+浏览器,来驱动浏览器来加载数据,基本能解决所有JS问题,但是此方案效率非常低下,如果数据采集量大或更新频率高,非常不推荐这种方式。
另一种方案采集效率非常高效,需要对网页JS进行研究和分析,最后转换成自己的代码以此来驱动JS完成数据的加载。所以对JS研究和解密是学习研究网络爬虫必不可少的一项技能。
所以我会将遇到的一些JS加密研究和解密方式放上来,与大家一起学习探讨。仅供学习参考,切勿用于商业用途。
一、JS加密研究
(1)介绍
百度企业信用是查询企业一些基本信息的网站,之前也有人对百度企业信用JS研究过,百度企业信用JS加密研究。写的很好的一篇文章,我这里在做一点补充,希望大家一起学习探讨。百度企业信用的加密主要是对tot参数进行加密,只是目前我做的时候发现只需要pid参数即可,不需要tot参数也能获取到信息。所以这里仅作为学习讨论。
(2)页面分析
我们首先打开百度企业信用搜索一家公司,进入二级页面后打开开发者工具,抓取网络数据包,找到他的请求接口。如果有同学对这个步骤不了解的,可以翻阅我之前写的文章python爬虫项目(新手教程)之知乎。
单击页面变更记录,可以发现如图1-1所示:
我们看到变更记录的接口链接,同时发现请求是GET方式,需要三个参数pid、tot、。这里只有pid是必须的,pid获取方式在搜索页面时获取如图1-2所示:
“”这个参数后面是一串数字,其实就是当前请求的时间戳,一般都是13位数字。
最后我们来找tot参数,如何获取。
(3)JS解密
这一步很重要,需要从大量js文件中去找一个参数,并且不会有注释,所以还需要有一点js基础。通过翻js文件,一个个搜索可以发现tot如图1-3所示:
我们可以看到tot值等于window.tk。这里有个window的意思,其实是指整个窗口页面,也就是说在动态的网页下,js控制窗口的值来实现页面“动态”。那么我们直接去主页面的Elements找就可以找到相关信息,通过检索window.tk 可以发现 如图 1-4所示:
这一步需要读js代码,所以最好有一点JS基础,并不需要完全读懂,只需要了解他参数请求方式和哪个函数有关即可。这里可以看到window.tk=mix(tk,baiducode),而function mix(tk,bid)函数已经给出,我们只需要获取tk与baiducode值即可。
- var tk=document.getElementById('tXP72jv').getAttribute('aEaA6u0');
- var baiducode = document.getElementById('baiducode')
这里很明白告诉我们baiducode ,tk 都是在标签中,元素id和元素属性值都告诉我们,我们只需要写正则表达式提取出来即可得到这些值。然而我们会发现每次tk获取的getElementById和getAttribute中的值都是一直在变化,所以我们还需要先从这段js中得到每次请求tk的id名和属性名。正则表达如下
rule = re.compile('var tk = document.getElementById\(\'(.*?)\'\).getAttribute\(\'(.*?)\'\);',re.S)
tk1,tk2 = re.findall(rule,response.text)[0]#先从js中获取有tk内容标签的id值和属性名
print(tk1,tk2)
rule = re.compile(tk2+'="(.*?)"')
tk=re.findall(rule,response.text)[0]#从对应标签中获取tk值
rule =re.compile('id="baiducode">(.*?)<',re.S)
baiducode=re.findall(rule,response.text)[0]#从对应标签中获取baiducode值
做到这里,我们已经筹齐了所以关键参数,只需要带入function mix(tk,bid)函数中,即可得到tot值,这里有两种办法,第一种:读懂js代码,翻译成对应语言,得出结果。第二种:调用execjs这种能够执行js的内库,调用执行js接口得出结果。这里我推荐第二种,方便简单。
使用教程可以参考这里:python3调用js的库之execjs
二、代码实现
这里贴出获取tot参数过程中解密方式。由于execjs在python3.5版本会有问题,主要是execjs本身也是用python2.7版本写的,所以用python3以上版本会有一点问题,所以这里节约时间就用python2.7版本做测试。
#coding=utf-8
import execjs
import requests
import re
import lxml.html
def mix(tk,baiducode):
ctx='''
function mix(tk, bid){
tk = tk.split('');var bdLen = bid.length;bid = bid.split('');var one = tk[bid[bdLen - 1]];for(var i = bdLen - 1; i >= 0; i -= 1) {tk[bid[i]] = tk[bid[i - 1]];if ((i - 2) < 0) {tk[bid[i - 1]] = one;break;}}return tk.join("");
}'''
print(tk,baiducode)
tot = execjs.compile(ctx).call('mix',tk,baiducode)
print(tot)
return tot
if __name__=='__main__':
url ='https://xin.baidu.com/detail/compinfo?pid=xlTM-TogKuTwtCkgkYi3AMoT2WsNQRne0gmd'
#这里先请求主页面,然后分析JS
response=requests.get(url,headers={"User_Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36"})
rule = re.compile('var tk = document.getElementById\(\'(.*?)\'\).getAttribute\(\'(.*?)\'\);',re.S)
tk1,tk2 = re.findall(rule,response.text)[0]#先从js中获取有tk内容标签的id值和属性名
rule = re.compile(tk2+'="(.*?)"')
tk=re.findall(rule,response.text)[0]#从对应标签中获取tk值
rule =re.compile('id="baiducode">(.*?)<',re.S)
baiducode=re.findall(rule,response.text)[0]#从对应标签中获取baiducode值
tot =mix(tk,baiducode)
三、总结
当我们碰到动态网页时,都是习惯性的找数据请求的接口地址,往往这些地址都会有参数验证,这就需要我们分析JS,通过研究JS获取验证参数,从而得到数据。这些加密方式有很多,需要逐一地去分析,接下来我会做一个有关JS解密的系列,尽量会讲细一点。同样,有问题欢迎大家指出,一起探讨交流。