美文网首页
网易云音乐热评爬虫(一): 反编译加密参数

网易云音乐热评爬虫(一): 反编译加密参数

作者: 剧与 | 来源:发表于2021-01-10 18:28 被阅读0次

        由于网易云音乐大部分请求做了混淆加密处理,因此直接用requests请求是行不通。本文以获取歌曲全部评论为例,通过分析网易云的加密过程,来反编译构造加密参数。下面主要介绍下用Python实现加密参数的构造,并获取歌曲的全部评论。

一、了解加密过程

    1.1 通过观察网络请求

        发现如下接口包含所要的数据。包含该页最新评论,以及所有热评

    再看它评论的参数,着实让人有点懵逼

  很多人看见这一串字符,可能简单看下前后接口有没数据就放弃了。好了,不卖关子。这章分析下整个参数的加密过程,实现最终模拟请求拿到数据。

 1.2 找请求参数

可以看的到,它的参数有两个,一个是params,一个是encSecKey并且都是经过加密的,我们就要分析它在js中的位置F12打开source搜索encSecKey)

进入js内部,经过断点调试发现这里就是生成最终参数的地方

    1.3 观察参数变化

    模拟不同分页请求,观察变化。效果如下图:

对比参数变化,得出参数构成:

csrf_token:始终空字符串

cursor:第一页默认-1;请求下一页时该参数为上一页最后一条评论时间戳(防止数据重复)

offset:移动评论数

orderType:默认1

pageNo:页码

pageSize:默认20

rid:R_SO_4_+ 歌曲ID

threadId:R_SO_4_+ 歌曲ID

自此,我们已经知道整个参数的构成,接下来就看下网易云是如何进行参数加密的

二、分析加密函数

            通过断点已经知道,下面这段js代码为我们分析的重点

          var bZj0x = window.asrsea(JSON.stringify(i2x), bkk0x(["流泪", "强"]), bkk0x(YS7L.md), bkk0x(["爱心", "女孩", "惊恐", "大笑"]));

            e2x.data = j2x.cr3x({

                params: bZj0x.encText,

                encSecKey: bZj0x.encSecKey

            })

           只要把bZj0x解出来就ok了,这里主要分析几个函数

2.1JSON.stringify(i2x)

        断点调制找到i2x返回内容

就是上文分析的整个字典参数

csrf_token: ""

cursor: "1610076350235"

offset: "40"

orderType: "1"

pageNo: "2"

pageSize: "20"

rid: "R_SO_4_1807537867"

threadId: "R_SO_4_1807537867"

 2.2 bkk0x函数

 bkk0x(["流泪", "强"]), bkk0x(YS7L.md), bkk0x(["爱心", "女孩", "惊恐", "大笑"]),都用到了同一个函数,这里就看下bkk0x函数内部实现是怎样的

var bkk0x = function(cJj8b) {

        var m2x = [];

        j2x.bf2x(cJj8b, function(cJi8a) {

            m2x.push(YS7L.emj[cJi8a])

        });

        return m2x.join("")

    }

等同于python写法

def get_bq_n1x(keys):

    m0x = []

    for key in keys:

        m0x.append(emj[key])

    return ''.join(m0x)

    YS7L.emj是一个固定的字典

YS7L.emj = {

        "色": "00e0b",

        "流感": "509f6",

        "这边": "259df",

        "弱": "8642d",

        "嘴唇": "bc356",

        "亲": "62901",

        "开心": "477df",

        "呲牙": "22677",

        "憨笑": "ec152",

        "猫": "b5ff6",

        "皱眉": "8ace6",

        "幽灵": "15bb7",

        "蛋糕": "b7251",

        "发怒": "52b3a",

        "大哭": "b17a8",

        "兔子": "76aea",

        "星星": "8a5aa",

        "钟情": "76d2e",

        "牵手": "41762",

        "公鸡": "9ec4e",

        "爱意": "e341f",

        "禁止": "56135",

        "狗": "fccf6",

        "亲亲": "95280",

        "叉": "104e0",

        "礼物": "312ec",

        "晕": "bda92",

        "呆": "557c9",

        "生病": "38701",

        "钻石": "14af6",

        "拜": "c9d05",

        "怒": "c4f7f",

        "示爱": "0c368",

        "汗": "5b7a4",

        "小鸡": "6bee2",

        "痛苦": "55932",

        "撇嘴": "575cc",

        "惶恐": "e10b4",

        "口罩": "24d81",

        "吐舌": "3cfe4",

        "心碎": "875d3",

        "生气": "e8204",

        "可爱": "7b97d",

        "鬼脸": "def52",

        "跳舞": "741d5",

        "男孩": "46b8e",

        "奸笑": "289dc",

        "猪": "6935b",

        "圈": "3ece0",

        "便便": "462db",

        "外星": "0a22b",

        "圣诞": "8e7",

        "流泪": "01000",

        "强": "1",

        "爱心": "0CoJU",

        "女孩": "m6Qyw",

        "惊恐": "8W8ju",

        "大笑": "d"

    }

   YS7L.md是一个固定的数据

YS7L.md = ["色", "流感", "这边", "弱", "嘴唇", "亲", "开心", "呲牙", "憨笑", "猫", "皱眉", "幽灵", "蛋糕", "发怒", "大哭", "兔子", "星星", "钟情", "牵手", "公鸡", "爱意", "禁止", "狗", "亲亲", "叉", "礼物", "晕", "呆", "生病", "钻石", "拜", "怒", "示爱", "汗", "小鸡", "痛苦", "撇嘴", "惶恐", "口罩", "吐舌", "心碎", "生气", "可爱", "鬼脸", "跳舞", "男孩", "奸笑", "猪", "圈", "便便", "外星", "圣诞"]

所以整个window.asrsea的参数都是可以得到的了,下面看下window.asrsea()查看这个函数执行了什么操作

2.3window.asrsea()

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) {

        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),

        h.encText = b(h.encText, i),

        h.encSecKey = c(i, e, f),

        h

    }

    function e(a, b, d, e) {

        var f = {};

        return f.encText = c(a + e, b, d),

        f

    }

    window.asrsea = d

可以看到window.asrsea = d,所以我们要执行的就是d这个函数,主要执行3个操作

a(16),生成16位随机数

进行两次AES加密得到h.encText

通过位移等一系列运算生成h.encSecKey

三、Python实现相同的加密算法

"""

网易云请求参数反编译工具

    :主要断点观察js,改为python实现

"""

emj = {

    "色": "00e0b",

    "流感": "509f6",

    "这边": "259df",

    "弱": "8642d",

    "嘴唇": "bc356",

    "亲": "62901",

    "开心": "477df",

    "呲牙": "22677",

    "憨笑": "ec152",

    "猫": "b5ff6",

    "皱眉": "8ace6",

    "幽灵": "15bb7",

    "蛋糕": "b7251",

    "发怒": "52b3a",

    "大哭": "b17a8",

    "兔子": "76aea",

    "星星": "8a5aa",

    "钟情": "76d2e",

    "牵手": "41762",

    "公鸡": "9ec4e",

    "爱意": "e341f",

    "禁止": "56135",

    "狗": "fccf6",

    "亲亲": "95280",

    "叉": "104e0",

    "礼物": "312ec",

    "晕": "bda92",

    "呆": "557c9",

    "生病": "38701",

    "钻石": "14af6",

    "拜": "c9d05",

    "怒": "c4f7f",

    "示爱": "0c368",

    "汗": "5b7a4",

    "小鸡": "6bee2",

    "痛苦": "55932",

    "撇嘴": "575cc",

    "惶恐": "e10b4",

    "口罩": "24d81",

    "吐舌": "3cfe4",

    "心碎": "875d3",

    "生气": "e8204",

    "可爱": "7b97d",

    "鬼脸": "def52",

    "跳舞": "741d5",

    "男孩": "46b8e",

    "奸笑": "289dc",

    "猪": "6935b",

    "圈": "3ece0",

    "便便": "462db",

    "外星": "0a22b",

    "圣诞": "8e7",

    "流泪": "01000",

    "强": "1",

    "爱心": "0CoJU",

    "女孩": "m6Qyw",

    "惊恐": "8W8ju",

    "大笑": "d"

}

md = ["色", "流感", "这边", "弱", "嘴唇", "亲", "开心", "呲牙", "憨笑", "猫", "皱眉", "幽灵", "蛋糕", "发怒", "大哭", "兔子", "星星", "钟情", "牵手",

      "公鸡", "爱意", "禁止", "狗", "亲亲", "叉", "礼物", "晕", "呆", "生病", "钻石", "拜", "怒", "示爱", "汗", "小鸡", "痛苦", "撇嘴", "惶恐", "口罩",

      "吐舌", "心碎", "生气", "可爱", "鬼脸", "跳舞", "男孩", "奸笑", "猪", "圈", "便便", "外星", "圣诞"]

def get_bq_n1x(keys):

    m0x = []

    for key in keys:

        m0x.append(emj[key])

    return ''.join(m0x)

def __get_random_str():

    """

    Returns:16位的随机字符串

    """

    str_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

    random_str = ""

    for i in range(16):

        index = random.randint(0, len(str_set) - 1)

        random_str += str_set[index]

    return random_str

arg2 = get_bq_n1x(["流泪", "强"])

arg3 = get_bq_n1x(md)

arg4 = get_bq_n1x(["爱心", "女孩", "惊恐", "大笑"])

random_str = __get_random_str()

def __aes_encrypt(text, key):

    """

    获取到ASW加密后的数据

    Args:

        text: 首先CBC加密方法,text必须位16位数据

        key: 加密的key

    Returns:加密后的字符串

    """

    # 加密或者解密的初始向量(16位)

    iv = "0102030405060708"

    # 不是16的倍数则填充

    pad = 16 - len(text) % 16

    if isinstance(text, str):

        text = text + pad * chr(pad)

    else:

        text = text.deocde("utf-8") + pad * chr(pad)

    aes = AES.new(key=bytes(key, encoding="utf-8"), mode=2, iv=bytes(iv, encoding="utf-8"))

    res = aes.encrypt(bytes(text, encoding="utf-8"))

    res = base64.b64encode(res).decode("utf-8")

    return res

def __get_enc_text(arg1):

    """

    对称加密后的参数

    Args:

        arg1:加密参数

    Returns:

    """

    enc_text = __aes_encrypt(arg1, arg4)

    enc_text = __aes_encrypt(enc_text, random_str)

    return enc_text

def __get_enc_sec_key():

    """

    对称加密密钥

    通过查看js代码,获取encSecKey

    """

    #  随机字符串逆序排列

    text = random_str[::-1]

    rs = int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16) ** int(arg2, 16) % int(arg3, 16)

    return format(rs, 'x').zfill(256)

def linux_encrypt(text):

    # print(text)

    return text

def get_form_data(text, method=''):

    """

    反编译生成

    请求的form-data参数

    Args:

        text: 跟踪js,自己组装参数

        method: 方法

    Returns:form-data参数

    """

    """

    """

    if method == 'linux':

        return linux_encrypt(text)

    text = str(text)

    return {"params": __get_enc_text(text), "encSecKey": __get_enc_sec_key()}

四、模拟请求歌曲评论

 只要加密过程知道了,其实很多接口都可以模拟请求,此文仅供学习。

最终执行效果如下图:

相关文章

  • 网易云音乐热评爬虫(一): 反编译加密参数

    由于网易云音乐大部分请求做了混淆加密处理,因此直接用requests请求是行不通。本文以获取歌曲全部评论为例,通过...

  • 网易云热评墙-规范严谨精益求精

    网易云热评墙 网易云热评 网易云音乐热评墙 网易云热评语录 网易云热评在哪看 网易云热评5000

  • 网易云音乐热评精选

    网易云音乐热评精选

  • 网易云热评2

    每日更新网易云热评,所有更新的文章都会收录在《网易云热评》专题里。热评内容均来自网络站点评论或网易云音乐的评论,如...

  • 网易云热评4

    每日更新网易云热评,所有更新的文章都会收录在《网易云热评》专题里。热评内容均来自网络站点评论或网易云音乐的评论,如...

  • 网易云热评12

    每日更新网易云热评,所有更新的文章都会收录在《网易云热评》专题里。热评内容均来自网络站点评论或网易云音乐的评论,如...

  • 网易云热评15

    每日更新网易云热评,所有更新的文章都会收录在《网易云热评》专题里。热评内容均来自网络站点评论或网易云音乐的评论,如...

  • 网易云热评6

    每日更新网易云热评,所有更新的文章都会收录在《网易云热评》专题里。热评内容均来自网络站点评论或网易云音乐的评论,如...

  • 网易云热评7

    每日更新网易云热评,所有更新的文章都会收录在《网易云热评》专题里。热评内容均来自网络站点评论或网易云音乐的评论,如...

  • 网易云热评119

    每日更新网易云热评,所有更新的文章都会收录在《网易云热评》专题里。热评内容均来自网络站点评论或网易云音乐的评论,如...

网友评论

      本文标题:网易云音乐热评爬虫(一): 反编译加密参数

      本文链接:https://www.haomeiwen.com/subject/lvndaktx.html