【Python】爬虫-----爬取网易云音乐评论信息

目录

现在要做的是找加密代码所在的js脚本

这一步开始是找哪行代码开始加密

补齐加密参数 

看看加密代码

 拿到encSecKey的数据

 拿到params的数据

在 Python中实现爬取


声明:本文章案例只用于学习不用于商业用途。

这个案例是爬取某页面的评论信息,评论信息不在页面源代码并且请求链接的请求参数被加密了,这种情况该如何入手进行爬取呢?

 由图可以看到,发到服务器上的数据被加密了,我们需要把它解密。

现在要做的是找加密代码所在的js脚本

点击启动器,里面是网页刷新时执行过的js脚本,最底下的是第一个执行的js脚本,我们选着最后一个也就是最上面这一个。

打开后选着美观输出,然后在标黄处设置断点,接着F5刷新页面,点击“继续运行脚本”,直到出现要找的链接。

 

 这个是我们请求的网址,设置断点就是为了拦截这个网址,查看data的数据在哪个脚本开始被加密。

 

 展开“调用堆栈”,选择第二个js脚本,查看d0x这个参数里的data,发现它的data也是被加密的,那么就要以此类推继续找到未加密的data所在的js脚本。

 到了第5个js脚本,我们看到了参数Ci9Z里的data的内容不是加密的,那就说明了在第4个js脚本里进行了加密操作,接着我们回到第4个js脚本。

 在第4个脚本的断点处data已被加密,说明了加密操作很可能在红色方框2的函数里(已确定了加密代码所在的js脚本)。在方框3中可以看到i0x这个参数里的内容是第5个脚本里data的内容,应该就是要被加密的参数,但是还需要进一步的证明。


这一步开始是找哪行代码开始加密

接着依然在第4个脚本里操作,取消旧的断点,在加密方法的第二行设置新的断点。F5刷新网页,点击“继续运行脚本”,直到出现要找的链接。找到之后就点“跳过下一个函数调用”(4),观察data的数据是否被加密,若没有就继续按(4)。

注意这个地方

图2

 执行到这行代码时看到了params和encSeckey的加密数据,也就说明了在上一行代码里加密了数据,观察图二中的方框1里面的代码可推断加密的数据应该就是图2中的方框2里的内容(已找到了加密代码和要被加密的数据)。注意此时的data数据还没有被加密。我们继续执行一下看看。

到这行代码发现了它把加密的数据添加到了data的数据里面,所有我们要找的params其实是encText,encSeckey就是encSeckey。


补齐加密参数 

我们搜索查找 window.asrsea,发现d赋值给了window.asrsea。我们找到了d方法,发现它的参数有d、e、f、g,我们下一步就是把它的参数拿到手。

因为 window.asrsea = d 所以我们看回这段代码:

 var bUM2x = window.asrsea(JSON.stringify(i0x), bsG5L(["流泪", "强"]), bsG5L(WW8O.md), bsG5L(["爱心", "女孩", "惊恐", "大笑"]));
  • 参数d:可以看到参数d,就是i0x,i0x就是那段要被加密的数据:
data = {
    "csrf_token": "",
    "cursor": "-1",
    "offset": "0",
    "orderType": "1",
    "pageNo": "1",
    "pageSize": "20",
    "rid": "R_SO_4_1381414166",
    "threadId": "R_SO_4_1381414166"
}
  • 参数e:复制 bsG5L(["流泪", "强"]) 到控制台并发送。控制台只返回010001。所以参数e是个定值 010001

  •  参数f:参数f和参数e一样,复制 bsG5L(WW8O.md) 到控制台并发送。控制台只返回一段很长的数据。
00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7
  • 参数g:以此类推,得到的数据是 0CoJUm6Qyw8W8jud

看看加密代码

 

function a(a) {
        var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
        for (d = 0; a > d; d += 1) #循环16次
            e = Math.random() * b.length, #生成随机数
            e = Math.floor(e),    #取整
            c += b.charAt(e);    #取字符串b中某个位置的字符
        return c #返回16个随机字符的字符串
}
    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) { #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); #i为一个16个字符的字符串
        return h.encText = b(d, g),
        h.encText = b(h.encText, i), #返回的就是params
        h.encSecKey = c(i, e, f),  #返回的就是encSecKey
        h
}

 拿到encSecKey的数据

方法d在python中可以理解为:

h.encText = b(d, g),
h.encText = b(h.encText, i), #返回的就是params
h.encSecKey = c(i, e, f), #e,f是定死的,如果我们把i也定死,那c也是定死的。
return h

由于方法c加密破解比较难,所以只能把i作为定值来处理,这样c返回的数就是定值。

 在1处设置断点,按F5刷新,观察2处的参数是否为参数d的数据,如果是就可以复制3处的参数i的数据了。

  在1处设置断点,点击2处继续执行代码,在3处拿到参数encSecKey的数据。

 拿到params的数据

function b(a, b) {
        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, {
            iv: d, #偏移量
            mode: CryptoJS.mode.CBC #CBC模式加密
        });
        return f.toString()
    }
    function d(d, e, f, g) {
        var h = {}
          , i = a(16); #i为一个16个字符的字符串
        return h.encText = b(d, g),
        h.encText = b(h.encText, i), #返回的就是params
        h.encSecKey = c(i, e, f),  #返回的就是encSecKey
        h
}

可以看到encText进行了两次加密,第一次加密是调用方法b,传入的参数是参数d(定值)参数g(定值),第二次加密依然调用了方法b,传入了方法b返回的结果和参数i(定值)。方法b其实是进行了CBC加密,在Python中也有实现该功能的模块。进行了两次加密的结果就是params的值了。


在 Python中实现爬取

import requests
from Crypto.Cipher import AES
from base64 import  b64encode
import json
url = "https://music.163.com/weapi/comment/resource/comments/get?csrf_token="
data = {
    "csrf_token": "",
    "cursor": "-1",
    "offset": "0",
    "orderType": "1",
    "pageNo": "1",
    "pageSize": "20",
    "rid": "R_SO_4_1381414166",
    "threadId": "R_SO_4_1381414166"
}
e = '010001'
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
g = '0CoJUm6Qyw8W8jud'
i ='0z11pIXbegqpbqmU'

def get_encSecKey():
    return "06fafa18bb4ea2047a7d868306967a350133076f895ffe42a577cea9c60290a070dfeb3bfffc2cdd0032f2581f6d31831bf2c4d229a5584ed7c8a92bec7835d5efcabba6318e270535ac5fceb74f46c7984251a4d69886b18a35534f543e607bb60a653706d5bb31826892dbb9c9346837d3a5f726fba7bee3e86ab83a8dfb4c"
#以上都是参数

#进行一个保证data的长度是16的倍数的算法
def toString16(data):
    pad = 16 - len(data) %16
    data += chr(pad) * pad
    return data

#进行两次加密
def get_params(data):#设data默认是字符串
    first = enc_params(data, g)
    second = enc_params(first, i)
    return second

#加密过程
def enc_params(data, key):
    iv = "0102030405060708"
    data = toString16(data)
    aes = AES.new(key=key.encode("utf-8"), IV=iv.encode("utf-8"), mode=AES.MODE_CBC) #创建加密器
    bs = aes.encrypt(data.encode("utf-8")) #加密,加密的内容的长度必须是16的倍数
    return str(b64encode(bs), "utf-8") #转化成字符串返回

#发送请求
resp = requests.post(url,data={
    "params": get_params(json.dumps(data)),#把字典转化成json字符串
    "encSecKey":get_encSecKey()
})
print(resp.text)

输出结果:

 成功爬取到了评论。

Guess you like

Origin blog.csdn.net/qq_26082507/article/details/121448509