鱼C论坛

 找回密码
 立即注册
查看: 1589|回复: 0

[技术交流] 【爬虫】网易云热评

[复制链接]
发表于 2021-9-1 15:37:08 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

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"])
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-10-6 01:43

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表