1.首先查看需要获取的数据即热门评论是否在源代码中,如果在源代码中就可以直接xpath等方式进行抓取
2.但是发现在网页和框架源代码里面都无法搜到评论内容,此时,使用网络抓包工具即:查看network中的XHD,在js代码中:get?csrf_token=中找到了hotComments
3.同时可以在headers里得到get包里请求的url,在payload中可以看到传递了两个加密的参数:params,encSecKey,说明真实的参数params是被加密了,网页收到后会根据这个key还原真实的参数,还原出来之后再去做解锁呀,查询等各种的操作
4.所以我们爬评论的话,首先需要想办法找到没加密之前数据以及加密过程,然后在我们的程序里来模拟网易的加密过程,进行手工加密,加密完成后再进行传递
5.因此,我们打开initiator逐个查找加密时是在哪个js里面
callstack为调用栈,这是一个当请求发送至get?csrf_token=时,程序经过哪些js脚本执行的过程,栈帧从下至上传递的,因此请求从底部开始调用至第一条最后发送到get?csrf_token=,因此我们可以看到调用过程
6.首先查看第一条即:最后执行的动作(美化一下js代码)
7. 可以看到在此处,程序将请求send出去:
8.在此处打断点,刷新页面可以查看断点之前程序执行函数的所有变量,发现request请求的url并不是我们需要的get?csrf_token=,所以放开进行下一次拦截:
9.最后找到目标js: 可以看到data被加密了,也就是说该函数所执行的为加密数据
10.因此需要逐个回查,查看e8e的data在进入该函数前的哪个函数中的data为未加密数据,同样,在callstack中往下挨个查看data是否被加密,可以看到d8f参数依旧被加密,因此继续慢慢查找。
11.最终查找到这个函数的时候发现数据在此时为未加密数据,在进入t8l.be8W函数之后data进行了加密:
12.因此我们回到t8l.be8W,查看参数在哪个阶段进行了加密。在t8l.be8W函数处重新打断点,刷新页面(breakpoint中释放原来的端断点),同样方法找get 13.但此时数据依旧未加密,因此执行"下一步",直到找到加密数据
加密数据找到在这里:
14.因此在此函数之前的函数打断点,重新刷新:
i8a里是我们的真实参数,进行下一步可以看到bVj2x有数据,也就是说:当参数i8a传递进入window.asrea进行加密,执行之后得到两个参数encseckey和enctext。
15.接着看函数,可以看到,这两个参数被重新传递给了e8e的data中的params和encSecKey:因此,关键点找到了,我们需要的params和其实就是encText和encSecKey
e8e.data = j8b.cq8i({
params: bVj2x.encText,
encSecKey: bVj2x.encSecKey
1.接下来就是第二步,正向加密,在此之前需要逆向查找到加密函数,发现是d赋值给了windows.asrea,因此加密函数为d
2.此时需要找到函数的参数d,e,f,g,可以查看windows.asrea函数:
var bVj2x = window.asrsea(JSON.stringify(i8a), bsR7K(["流泪", "强"]), bsR7K(Xp4t.md), bsR7K(["爱心", "女孩", "惊恐", "大笑"]));
可得:
JSON.stringify(i8a)==>d
bsR7K(["流泪", "强"])==>e
bsR7K(Xp4t.md)==>f
bsR7K(["爱心", "女孩", "惊恐", "大笑"])==>g
带入终端执行发现e,f,g为固定值
function a(a) { //返回随机的16位字符串
var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
for (d = 0; a > d; d += 1) //循环16次
e = Math.random() * b.length, //e为随机数处理值
e = Math.floor(e), //取整:floor函数表示对x 的向下取整,返回≤x的最大整数
c += b.charAt(e); //取b的第e个数:charAt函数返回某个索引下的char值
return c
}
function b(a, b) {
var c = CryptoJS.enc.Utf8.parse(b)
, d = CryptoJS.enc.Utf8.parse("0102030405060708")
, e = CryptoJS.enc.Utf8.parse(a)
, f = CryptoJS.AES.encrypt(e, c, {
iv: d,
mode: CryptoJS.mode.CBC
});
return f.toString()
}
function c(a, b, c) { //一个加密算法(因为没有随机数产生,所以可以逆向)
var d, e;
return setMaxDigits(131),
d = new RSAKeyPair(b,"",c),
e = encryptedString(d, a)
}
function d(d, e, f, g) {
var h = {} //空对象
, i = a(16); //查看a函数
return h.encText = b(d, g), //h.encText返回的为params
h.encText = b(h.encText, i), //h.encSecKey返回的为encSecKey
h.encSecKey = c(i, e, f), //c由i决定,若将i固定则c固定,因此将i设置成定值
h
}
function e(a, b, d, e) {
var f = {};
return f.encText = c(a + e, b, d),
f
}
4.因此需要固定encText和encSeckey:
1)获取encText
经过分析函数可知,encText进行了两次加密:
return h.encText = b(d, g),
h.encText = b(h.encText, i)
首先将d和g进行函数b的运算,将返回的结果再和随机数i进行加密即为params
此时回到函数b,可知:
function b(a, b) {
var c = CryptoJS.enc.Utf8.parse(b) //utf8转换
, d = CryptoJS.enc.Utf8.parse("0102030405060708")
, e = CryptoJS.enc.Utf8.parse(a)
, f = CryptoJS.AES.encrypt(e, c, { //AES加密算法
iv: d, //iv为AES加密的偏移量
mode: CryptoJS.mode.CBC //CBC加密模式
});
return f.toString() //返回字符串
a为原始数据即需要加密的内容,f为AES加密算法,传递的参数为e,c,iv
e:utf转换后的a,因此推理可知c为密钥,所以b为原始密钥
因此,逆向回到第一步,return h.encText中,g和i为原始密钥,d为偏移量
2)获取encSeckey:
重点是找到i的值:
此时,在h.encText = b(h.encText, i)处打断点,刷新界面,一直放到该断点处,可以获取到i值:
5.同时在e8e.data打断点放行,可以得到该i值下的encSeckey的值:
注意两点:
1.构造加密函数时,由于参数的属性(按住ctrl点击函数可以查看到需要的参数),所以key要进行字节转换。
2.因为加密后数据不能被decode("utf-8")识别,不能直接转换成字符串,同时,加密内容必须为16倍数的字符串,具体原因参考AES加密规则,因此需要进行数据处理
def to_16(data):
pad = 16-len(data) % 16
data += chr(pad) * pad
return data
成功爬取评论: