python+execjs爬取网易云评论
分析网站
首先打开网易云首页,随便点一首歌曲进入到评论区。
接着按F12进入开发者工具,重新刷新页面找发送评论数据的接口,找到接口链接为:[https://music.163.com/weapi/v1/resource/comments/A_PL_0_924680166?csrf_token=]
在接口返回的数据里有个键为comments里面就包含了页面上的评论,好了,接下来看接口需要发送什么数据给后台,有两个参数分别为params和encSecKey,这两个参数都经过了加密,所以我们要找到参数对应的js文件,按ctrl+shift+F搜索encSecKey,发现对应着core开头的js文件,接下来就直接进入此文件分析js。
JS分析
进入到此页面后我们就来开始分析js代码,首先按ctrl+f查找encSecKey,发现params和encSecKey的返回值都在13020行代码那里,从图中看出params和encSecKey都是接收于bXL9C这个值里的属性的,这时我们就要开始分析13017行这行代码的意思了,我们首先可以打上断点,然后重新刷新看下window.asrsea这个方法里的四个参数分别是什么。
接下来我们就可以来记下4个参数,分别分为(a,b,c,d)
a = “{“rid”:“A_PL_0_924680166”,“offset”:“0”,“total”:“true”,“limit”:“20”,“csrf_token”:”"}"
b = “010001”
c = “00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7”
d = “0CoJUm6Qyw8W8jud”
经过几次测试发现除了a会改变之外,其余三个值都是固定值。
再进一步查找window.asrsea究竟是什么。
发现window.asrsea对应着d方法,此时我们发现d方法返回值对应着我们前面的两个参数,好了,到这就知道了方法源头,因为d方法里面调用的b、c方法这些都是加密的,所以我们可以利用execjs来破解,而不需要自己去用python重写js加密代码了。
execjs解密js
此时我们可以把js代码复制到pycharm中分析,我们需要的方法分别是js代码中的a、b、c、d四个方法,我们可以将其复制到新的js文件中。
复制前:
复制后:
然后在python代码中我们可以用execjs来调用js代码,并把之前记录的四个参数传参进去给d方法,代码如下:
此时会发现有报错,会提醒未定义CryptoJS,这时我们就需要到npm中安装了,全局安装cnpm install crypto-js --save-dev,并到js文件中引入此代码let CryptoJS = require(‘crypto-js’);,再到py文件中的修改此处,添加cwd指定你的npm路径,如果不知道你的npm路径是什么,可以在cmd中输入npm config get prefix获取。
此时还会发现报错缺少函数什么的,也就是js代码中的一些函数,这时我们从js代码中的12447行的RSAKeyPair()方法开始复制到12869此处全部复制到js文件中,基本上运行就可以获取两个加密参数的值了。
运行结果
两个参数和获取的评论。
代码
python
import requests
import execjs
import json
def main(number):
'''
:param number: 歌曲的id
:return:
'''
node = execjs.get()
file = './demo.js'
ctx = node.compile(open(file).read(), cwd='C:\\Users\\Administrator\\AppData\\Roaming\\npm') # 读取文件
num = 0 # 计数
while True:
a = {
"rid": "A_PL_0_%s" % number, "offset": "%s" % num, "total": "true", "limit": "20", "csrf_token": ""}
b = "010001"
c = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
d = "0CoJUm6Qyw8W8jud"
result = ctx.eval('d("%s","%s","%s","%s")' % (a, b, c, d)) # 调用d方法
params = result['encText'] # 获取encText
encSecKey = result['encSecKey'] # 获取encSecKey
print(params)
print(encSecKey)
url = "https://music.163.com/weapi/v1/resource/comments/A_PL_0_%s" % number
headers = {
"Referer": 'https://music.163.com/',
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
formdata = {
'params': params, 'encSecKey': encSecKey}
resp = requests.post(url, headers=headers, data=formdata)
resp = json.loads(resp.text)['comments']
if len(resp) <= 0:
break
for comments in resp:
print(comments['content'])
num += 20
if __name__ == '__main__':
main(308240095)
js代码:
let CryptoJS = require('crypto-js');
function a(a) {
var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
for (d = 0; a > d; d += 1)
e = Math.random() * b.length,
e = Math.floor(e),
c += b.charAt(e);
return c
}
function b(a, b) {
// 1 4
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);
return h.encText = b(d, g), // 1 4
h.encText = b(h.encText, i),
h.encSecKey = c(i, e, f), // 2 3
h
}
function RSAKeyPair(a, b, c) {
this.e = biFromHex(a),
this.d = biFromHex(b),
this.m = biFromHex(c),
this.chunkSize = 2 * biHighIndex(this.m),
this.radix = 16,
this.barrett = new BarrettMu(this.m)
}
function twoDigit(a) {
return (10 > a ? "0" : "") + String(a)
}
function encryptedString(a, b) {
for (var f, g, h, i, j, k, l, c = new Array, d = b.length, e = 0; d > e; )
c[e] = b.charCodeAt(e),
e++;
for (; 0 != c.length % a.chunkSize; )
c[e++] = 0;
for (f = c.length,
g = "",
e = 0; f > e; e += a.chunkSize) {
for (j = new BigInt,
h = 0,
i = e; i < e + a.chunkSize; ++h)
j.digits[h] = c[i++],
j.digits[h] += c[i++] << 8;
k = a.barrett.powMod(j, a.e),
l = 16 == a.radix ? biToHex(k) : biToString(k, a.radix),
g += l + " "
}
return g.substring(0, g.length - 1)
}
function decryptedString(a, b) {
var e, f, g, h, c = b.split(" "), d = "";
for (e = 0; e < c.length; ++e)
for (h =