|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 大马强 于 2021-9-1 15:36 编辑
爬取网易云的评论,关键是获得两个加密参数 params, enSeckey
一、事前准备
以这首歌THE ANSWER为例,打开Chrome浏览器的调试工具, 点击newwork,重新刷新一下看看浏览器请求回来的数据包,找有评论信息的数据包,点击Initiator js脚本调用栈,点击最上方的也是最后一次执行的js脚本
二、找到未加密的数据
进入之后发现当前的内容是“不堪入目”的,先点击左下角的 “{}” ,让其阅读性好一点,然后将当前位置设置一个断点,浏览器不单单只请求评论的数据包,所以我们要观察旁边的Scope栏中变量,一步一步放掉不需要的请求,直到当浏览器请求https://music.163.com/weapi/comm ... nts/get?csrf_token=
然后再通过js栈中调用的js来寻找到未加密前的数据第一次出现的js,那么它的下一次js就是将参数加密,之后再重新设置一个断点,一步步调试找到该js加密参数的具体地方
目标锁定 window.asrsea函数, ctrl + F 寻找该函数具体实现的地方,找到了d函数,加密的过程就是再d函数里实现的了
三、找到加密原理
观察d函数可以发现,d函数有四个参数,与window.asrsea 函数的四个参数一一对应,利用浏览器的调试工具就可以得到参数的值。d函数中调用了两次b函数,一次a函数和一次c函数,再进一步观察其他函数,发现a函数作用时返回一个16为随机数给i变量,b函数是加密的params参数,c函数是加密的encSecKey参数
b,c函数分别用到了AES加密和RSA加密,前者AES加密的第一次加密是将d作为明文,g作为密钥,第二次加密是将第一次加密的密文再进行加密,i作为第二次加密的密钥,最终的带加密的params。后者用到三个参数 i,e,f,三个值都是定值,所以参数encSecKey可以固定下来function a(a) {a =16
var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
for (d = 0; a > d; d += 1) 循环16次,创建一个16位随机字符串
e = Math.random() * b.length,
e = Math.floor(e),
c += b.charAt(e);
return c
}
function b(a, b) { a = 数据, g:定值, 经过AES加密 原文,密钥,偏移量,模式
var c = CryptoJS.enc.Utf8.parse(b) b
, d = CryptoJS.enc.Utf8.parse("0102030405060708")
, e = CryptoJS.enc.Utf8.parse(a)
, f = CryptoJS.AES.encrypt(e, c, { 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) <-公钥加密 d:公钥 ,a
}
function d(d, e, f, g) { d: 数据data, e:"010001", f:一个很长的字符串, g:"0CoJUm6Qyw8W8jud"
var h = {}
, i = a(16); -生成随机数
return h.encText = b(d, g),d=数据, g密钥
h.encText = b(h.encText, i), param :
encText两层加密
-- 可以将i定死,encSecKey就是一个定值
h.encSecKey = c(i, e, f), i :16位随机值,e定值,f定值 encSecKey
h
}
四、实现代码from Crypto.Cipher import AES
from base64 import b64encode
import requests
import json
import random
url = "https://music.163.com/weapi/comment/resource/comments/get?csrf_token="
data = {
"cursor": "-1",
"offset": "0",
"orderType": "1",
"pageNo": "1",
"pageSize": "20",
"rid": "R_SO_4_1855995767",
"threadId": "R_SO_4_1855995767"
}
# 函数d四个参数
def get_i():
seq = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
str1 = ""
for i in range(16):
str1 += random.choice(seq)
print(str1)
return str1
e = "010001"
d = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g = "0CoJUm6Qyw8W8jud"
i = "p6xB1rx37AU8O0KO"
def get_encSeckey():
return "b742226290709f51abf9ad9a4e9136bd89036078368545a3f9a5ab938068d7a7be4d3f3fc2ea184090def069b85f75e4b443a43b1a872710c22ce983a3785fc074e799bd90d3e22a7a14d8a4864d647a72dc3a0de05a10ac61ef8114b631bf0079708ff79e0c2901ce0db1c26be78503550f65be6490ad08125ebf9744cc41cf"
# 补位
def to_16(data):
pd = 16 - len(data) % 16
data += chr(pd) * pd
return data
def get_param(data): # 字典1是加密不了的,需要处理成字符串
# param 是经过两次加密的
first = enc_param(data, g)
second = enc_param(first, i)
return second
def enc_param(data, key):
iv = "0102030405060708"
# 创建一个AES加密器, 设置密钥,偏移量以及模式,前两者要为字符串格式
# AEs加密的数据格式一定是16的倍数,不到就用chr(x)补齐 格式为 "123456788chr(7)chr(7)"
data = to_16(data)
aes = AES.new(key=key.encode("utf-8"),
iv=iv.encode("utf-8"), mode=AES.MODE_CBC)
# 加密后返回结果为字节,但需要的是字符串,并且该返回不能被 utf-8 格式所识别,还需要借助其他的库将其转换
bs = aes.encrypt(data.encode("utf-8"))
return str(b64encode(bs), "utf-8")
# 将字典转换成字符串 ,需要json
req = requests.post(url, data={
"params": get_param(json.dumps(data)),
"encSecKey": get_encSeckey()
})
res = req.json()["data"]["hotComments"]
for content in res:
print(content["content"])
|
|