python爬虫之网易云歌曲下载(需要js分析) -- 2020.06.20更新

背景

目的 >>> 通过python + kivy打包一个简易的网易云会员歌曲下载软件;

初试 >>> 用you-get库里面的网易云接口来实现下载;

失败 >>> you-get里面的接口已无法下载会员歌曲;

原因 >>> 网易云2016年之前就改了接口;

结果 >>> 自己抓包分析;

实践

抓包 >>> 工具: fiddler;

网址 >>> 以周深的<<不想睡>>为例: https://music.163.com/#/song?id=1453342107;

筛选 >>> 很快可以筛选出第三个请求: https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=477c1bd99fddedb3adc074f47fee2d35,因为它的响应为以下json字符串,其中的url即为歌曲的真实url,可以直接下载;

{
    "data":
        [
            {
                "id": 1453342107,
                "url": "http://m10.music.126.net/20200620200931/d98d82fd40dbecc4db66b64e777beb9d/yyaac/obj/wonDkMOGw6XDiTHCmMOi/2772009410/337e/4f27/7b95/4a1462f7b4b11c94e7e0bef696506bf2.m4a",
                "br": 96000,
                "size": 4095931,
                "md5": "4a1462f7b4b11c94e7e0bef696506bf2",
                "code": 200,
                "expi": 1200,
                "type": "m4a",
                "gain": 0.0,
                "fee": 8,
                "uf": null,
                "payed": 0,
                "flag": 68,
                "canExtend": false,
                "freeTrialInfo": null,
                "level": "standard",
                "encodeType": "aac"
            }
        ],
    "code": 200
}

观察 >>> 如下图:

python爬虫之网易云歌曲下载(需要js分析) -- 2020.06.20更新_第1张图片

发现 >>> 三个参数: (1) csrf_token; (2)params; (3)encSecKey;

搜索 >>> 百度一下"网易云encSecKey";

收获 >>> 大佬的博客一篇: https://blog.csdn.net/weixin_44530979/article/details/87925950, 这篇博客已经基本解决了此问题,果    然是需要分析js来解密的,可我没学过js......只能一步一步来;

调试1 >>> 浏览器打开上面抓包的网址(https://music.163.com/#/song?id=1453342107) --> 开发者模式 --> Network --> 刷新网页 --> 找到上面筛选出来的url(直接在filter里面输入"v1"即可) --> 如下图:python爬虫之网易云歌曲下载(需要js分析) -- 2020.06.20更新_第2张图片

调试2 >>> 如上图 --> 找到v1?csrf_token=...对应的initiator,按照上面提及的博客里面的操作,找到生成params和encSecKey的js函数如下:

!
function() {
    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.ecnonasr = e
} (); (function() {
    var c7f = NEJ.P,
    et9k = c7f("nej.g"),
    v7o = c7f("nej.j"),
    k7d = c7f("nej.u"),
    Yb5g = c7f("nm.x.ek");
    Yb5g.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"
    };
    Yb5g.md = ["色", "流感", "这边", "弱", "嘴唇", "亲", "开心", "呲牙", "憨笑", "猫", "皱眉", "幽灵", "蛋糕", "发怒", "大哭", "兔子", "星星", "钟情", "牵手", "公鸡", "爱意", "禁止", "狗", "亲亲", "叉", "礼物", "晕", "呆", "生病", "钻石", "拜", "怒", "示爱", "汗", "小鸡", "痛苦", "撇嘴", "惶恐", "口罩", "吐舌", "心碎", "生气", "可爱", "鬼脸", "跳舞", "男孩", "奸笑", "猪", "圈", "便便", "外星", "圣诞"]
})(); (function() {
    var c7f = NEJ.P,
    et9k = c7f("nej.g"),
    v7o = c7f("nej.j"),
    k7d = c7f("nej.u"),
    Yb5g = c7f("nm.x.ek"),
    l7e = c7f("nm.x");
    if (v7o.be8W.redefine) return;
    window.GEnc = true;
    var bqL0x = function(cyu4y) {
        var m7f = [];
        k7d.bf8X(cyu4y,
        function(cyt4x) {
            m7f.push(Yb5g.emj[cyt4x])
        });
        return m7f.join("")
    };
    var cyr4v = v7o.be8W;
    v7o.be8W = function(Y8Q, e7d) {
        var i7b = {},
        e7d = NEJ.X({},
        e7d),
        mg2x = Y8Q.indexOf("?");
        if (window.GEnc && /(^|\.com)\/api/.test(Y8Q) && !(e7d.headers && e7d.headers[et9k.zP8H] == et9k.Kd1x) && !e7d.noEnc) {
            if (mg2x != -1) {
                i7b = k7d.hb0x(Y8Q.substring(mg2x + 1));
                Y8Q = Y8Q.substring(0, mg2x)
            }
            if (e7d.query) {
                i7b = NEJ.X(i7b, k7d.fQ0x(e7d.query) ? k7d.hb0x(e7d.query) : e7d.query)
            }
            if (e7d.data) {
                i7b = NEJ.X(i7b, k7d.fQ0x(e7d.data) ? k7d.hb0x(e7d.data) : e7d.data)
            }
            i7b["csrf_token"] = v7o.gR0x("__csrf");
            Y8Q = Y8Q.replace("api", "weapi");
            e7d.method = "post";
            delete e7d.query;
            var bVV8N = window.asrsea(JSON.stringify(i7b), bqL0x(["流泪", "强"]), bqL0x(Yb5g.md), bqL0x(["爱心", "女孩", "惊恐", "大笑"]));
            e7d.data = k7d.cv8n({
                params: bVV8N.encText,
                encSecKey: bVV8N.encSecKey
            })
        }
        cyr4v(Y8Q, e7d)
    };
    v7o.be8W.redefine = true
})();

  分析 >>> 可以看到:

var bVV8N = window.asrsea(JSON.stringify(i7b), bqL0x(["流泪", "强"]), bqL0x(Yb5g.md), bqL0x(["爱心", "女孩", "惊恐", "大笑"]));
e7d.data = k7d.cv8n({
    params: bVV8N.encText,
    encSecKey: bVV8N.encSecKey
})

所以encText(即params)和encSecKey都由函数window.asrsea生成,而:

//..............
function e(a, b, d, e) {
    var f = {};
    return f.encText = c(a + e, b, d),
    f
}
// 从这里可以看出, window.asrsea就是d函数
window.asrsea = d,
window.ecnonasr = e
//..............

所以window.asrsea就是d函数,而d函数:

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
}

所以encText就是两次调用b函数生成的,encSecKey是直接由c函数生成的,接下来进入正题;

1、encSecKey的生成:

先来看encSecKey的生成,观察c函数:

function c(a, b, c) {
    var d, e;
    return setMaxDigits(131),
    d = new RSAKeyPair(b, "", c),
    e = encryptedString(d, a)
}

是一个RSA加密过程,传入了三个参数a,b,c,再观察一次d函数:

function d(d, e, f, g) {
    //.................
    i = a(16);
    //.................
    h.encSecKey = c(i, e, f),
    //.................
}

然后再观察一次window.asrsea函数:

var bVV8N = window.asrsea(JSON.stringify(i7b), bqL0x(["流泪", "强"]), bqL0x(Yb5g.md), bqL0x(["爱心", "女孩", "惊恐", "大笑"]));

所以传入的参数存在等量关系:

a = i = a(16)

b = e = bqL0x(["流泪", "强"])

c = f = bqL0x(Yb5g.md)

(1) i参数:

看i参数,它由a函数生成:

i = a(16)
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
}

不难看出,a函数即生成一个随机的a位字符串,

字符串元素在"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"中随机选取,

而传给a函数的值为16,

所以i参数即为一个随机的16位字符串;

(2) e和f参数:

可以看到e和f参数的生成都和函数bqL0x有关:

e = bqL0x(["流泪", "强"])

f = bqL0x(Yb5g.md)
var bqL0x = function(cyu4y) {
    var m7f = [];
    k7d.bf8X(cyu4y,
    function(cyt4x) {
        m7f.push(Yb5g.emj[cyt4x])
    });
    return m7f.join("")
};

由 m7f = [] 、m7f.push(Yb5g.emj[cyt4x]) 和 m7f.join("") 我们可以猜测函数bqL0x的作用:

传入一个列表,遍历列表中的元素,在Yb5g.emj中寻找每个元素对应的值,最后拼接成字符串;

而Yb5g.emj即:

Yb5g.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"
};

所以e参数即为"流泪"、"强"对应的值的拼接,即e参数为常量:

e = "010001"

而f参数为遍历Yb5g.md获得,而Yb5g.md为:

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

所以f参数也为常量:

f = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"

至此,传给c函数的三个参数都解决了,encSecKey的生成应该没有问题了;

2、encText的生成:

回顾一下生成encText的js代码:

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

再看一下d函数:

function d(d, e, f, g) {
//..............

再看一下window.asrsea函数:

var bVV8N = window.asrsea(JSON.stringify(i7b), bqL0x(["流泪", "强"]), bqL0x(Yb5g.md), bqL0x(["爱心", "女孩", "惊恐", "大笑"]));

(1)第一次加密:

参数等量关系:

d = JSON.stringify(i7b)

g = bqL0x(["爱心", "女孩", "惊恐", "大笑"])

由encSecKey的生成我们已经知道,

g参数即为"爱心"、"女孩"、 "惊恐"、 "大笑"对应的值的拼接,即g参数为常量:

g = "0CoJUm6Qyw8W8jud"

而d参数是一个由i7b转换成的json字符串,观察i7b:

v7o.be8W = function(Y8Q, e7d) {
    var i7b = {},
//.........
        i7b["csrf_token"] = v7o.gR0x("__csrf");
//.........

所以i7b应该就是我们需要加密的信息,里面应该要包含歌曲的相关信息 ,即https://music.163.com/#/song?id=1453342107里面的id值,分析js貌似有点困难,这次我们通过浏览器开发者模式的断点调试进行观察,如下图,

设置断点在cyr4v(Y8Q, e7d)[直接点击对应的行序数即可设置],

右边的watch设置为window.asrsea[点击"+",输入"window.asrsea"即可设置]:

python爬虫之网易云歌曲下载(需要js分析) -- 2020.06.20更新_第3张图片

然后刷新页面,请求会被冻结在断点处,然后我们就可以观察变量值了,然后我们就会踩到一个坑......如下图:

python爬虫之网易云歌曲下载(需要js分析) -- 2020.06.20更新_第4张图片

点开右边的Scope,观察Local变量值,我们可以看到,i7b的确为字典,但.......只有一个csrf_token的键???

竟然没有和歌曲相关的信息???

不过如果我们再仔细观察一下,可以发现变量Y8Q的值为"/weapi/cdns",

这个东西太眼熟了!

我们回到fiddler的抓包数据:

python爬虫之网易云歌曲下载(需要js分析) -- 2020.06.20更新_第5张图片

可以发现我们在请求需要的url(https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=)之前,先请求了url

"https://music.163.com/weapi/cdns?csrf_token="......所以在这个断点冻结时的i7b并不是我们需要的i7b......

于是,在这个断点之后,我们需要一次一次地按下F10,并对应地观察Local里面的变量的变化,

直到我们观察到变量Y8Q的值变成"/weapi/song/enhance/player/url/v1",如下图:

python爬虫之网易云歌曲下载(需要js分析) -- 2020.06.20更新_第6张图片

大功告成!我们这次得到的i7b为:

i7b =  {
        csrf_token: "f6bcc4d47e1f7affcb1eb2d5ed1f7a37",
        encodeType: "aac",
        ids: "[1453342107]",
        level: "standard"
}

里面的id为"1453342107"就是https://music.163.com/#/song?id=1453342107里面的id!这次准没错了!

而d = JSON.stringify(i7b)即将i7b转换为了json字符串,

所以要加密的信息即d参数格式为:

d = '{"ids":"[%s]","level":"standard","encodeType":"aac","csrf_token":""}' % id

到这里需要纠正一下上面提及的博客,因为里面说到的的i7b格式为'{'ids': "[" + str(id) + "]", 'br': 128000, 'csrf_token': ""}'是错误的,

以此格式发送请求,会收到"参数错误"的响应,应该是网易云的js代码曾经改过一次......

至此,第一次加密成功!

(2) 第二次加密:

非常简单,传入的参数i就是我们分析encSecKey时分析到的16位随机字符串,

不过需要注意的是,生成encText和encSecKey所用的i必须相同,不然会解码错误!

而需要加密的信息就是第一次加密后的字符串!

至此,第二次加密也成功完成!

--------------------------------------------------------不知道哪里来的分割线-----------------------------------------------------------

通过以上js分析,我们已经知道了encText(即params)和encSecKey是怎样生成的了,

而网上的博客都说csrf_token是不需要的,所以我们不用管(实践也证明了这的确是不需要的),

所以,我们可以开始在python中写代码,实现上述过程了,

然后代码的话也基本是参照上面提及的那篇博客完成的......

再发一次这篇博客地址:https://blog.csdn.net/weixin_44530979/article/details/87925950,作者是:我的ID是局长大人;

下面是我完善后的代码,有几个版本,基本上都添加了注释,应该更好懂:

版本1: 基本上借鉴了我的ID是局长大人的博客

# coding=utf-8
from Crypto.Cipher import AES
import base64
import codecs
import random
import requests
import math


# AES加密,需调用两次,得到encText
def AES_encrypt(text, key, iv):
    bs = 16
    pad2 = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
    encryptor = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
    encrypt_aes = encryptor.encrypt(str.encode(pad2(text)))
    encrypt_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8')
    return encrypt_text


# RSA加密,得到encSecKey
def RSA_encrypt(text, pubKey, modulus):
    text = text[::-1]
    rs = int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16) ** int(pubKey, 16) % int(modulus, 16)
    return format(rs, 'x')


# 生成16位随机字符串
def a():
    h = ''
    l = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    for i in range(16):
        e = math.floor(random.uniform(0, 1) * len(l))
        h += l[e]
    return h


# 获取params
def get_params(song_id):
    encText = '{"ids":"[%s]","level":"standard","encodeType":"aac","csrf_token":""}' % song_id
    return AES_encrypt(AES_encrypt(encText, g, iv), i, iv)


# 获取encSecKey
def get_encSecKey():
    return RSA_encrypt(i, b, c)


# 一些常量
g = '0CoJUm6Qyw8W8jud'
b = "010001"
c = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
iv = "0102030405060708"

# 生成16位随机字符串
i = a()
# 歌曲id
song_id = input('请输入歌曲id:')  # 例:'1401790402'
# 生成params
params = get_params(song_id)
# 生成encSecKey
encSecKey = get_encSecKey()

print(params)
print(encSecKey)

# 构建请求头
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36',
    # 这里需要传入会员账户的cookie才能访问会员歌曲,暂时无法破解,否则只能访问免费歌曲
    'Cookie': 'MUSIC_U=f52f220df171da480dbf33ce89947961585a7fdf08c89a2a4bdd6efebd86544233a649814e309366;',
}
# 请求url
url = 'https://music.163.com/weapi/song/enhance/player/url/v1'
# 构建data
data = {
    'params': params,
    'encSecKey': encSecKey
}
# 发送请求
request = requests.post(url=url, headers=headers, data=data)
# 返回信息
print(request.text)

版本2: 通过调用js代码实现

js代码【这是从网易云的js筛选出来的函数,亲测独立可用】

// AES加密所用到的所有函数
(function() {})();
var CryptoJS = CryptoJS ||
function(u, p) {
    var d = {},
    l = d.lib = {},
    s = function() {},
    t = l.Base = {
        extend: function(a) {
            s.prototype = this;
            var c = new s;
            a && c.mixIn(a);
            c.hasOwnProperty("init") || (c.init = function() {
                c.$super.init.apply(this, arguments)
            });
            c.init.prototype = c;
            c.$super = this;
            return c
        },
        create: function() {
            var a = this.extend();
            a.init.apply(a, arguments);
            return a
        },
        init: function() {},
        mixIn: function(a) {
            for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]);
            a.hasOwnProperty("toString") && (this.toString = a.toString)
        },
        clone: function() {
            return this.init.prototype.extend(this)
        }
    },
    r = l.WordArray = t.extend({
        init: function(a, c) {
            a = this.words = a || [];
            this.sigBytes = c != p ? c: 4 * a.length
        },
        toString: function(a) {
            return (a || v).stringify(this)
        },
        concat: function(a) {
            var c = this.words,
            e = a.words,
            j = this.sigBytes;
            a = a.sigBytes;
            this.clamp();
            if (j % 4) for (var k = 0; k < a; k++) c[j + k >>> 2] |= (e[k >>> 2] >>> 24 - 8 * (k % 4) & 255) << 24 - 8 * ((j + k) % 4);
            else if (65535 < e.length) for (k = 0; k < a; k += 4) c[j + k >>> 2] = e[k >>> 2];
            else c.push.apply(c, e);
            this.sigBytes += a;
            return this
        },
        clamp: function() {
            var a = this.words,
            c = this.sigBytes;
            a[c >>> 2] &= 4294967295 << 32 - 8 * (c % 4);
            a.length = u.ceil(c / 4)
        },
        clone: function() {
            var a = t.clone.call(this);
            a.words = this.words.slice(0);
            return a
        },
        random: function(a) {
            for (var c = [], e = 0; e < a; e += 4) c.push(4294967296 * u.random() | 0);
            return new r.init(c, a)
        }
    }),
    w = d.enc = {},
    v = w.Hex = {
        stringify: function(a) {
            var c = a.words;
            a = a.sigBytes;
            for (var e = [], j = 0; j < a; j++) {
                var k = c[j >>> 2] >>> 24 - 8 * (j % 4) & 255;
                e.push((k >>> 4).toString(16));
                e.push((k & 15).toString(16))
            }
            return e.join("")
        },
        parse: function(a) {
            for (var c = a.length,
            e = [], j = 0; j < c; j += 2) e[j >>> 3] |= parseInt(a.substr(j, 2), 16) << 24 - 4 * (j % 8);
            return new r.init(e, c / 2)
        }
    },
    b = w.Latin1 = {
        stringify: function(a) {
            var c = a.words;
            a = a.sigBytes;
            for (var e = [], j = 0; j < a; j++) e.push(String.fromCharCode(c[j >>> 2] >>> 24 - 8 * (j % 4) & 255));
            return e.join("")
        },
        parse: function(a) {
            for (var c = a.length,
            e = [], j = 0; j < c; j++) e[j >>> 2] |= (a.charCodeAt(j) & 255) << 24 - 8 * (j % 4);
            return new r.init(e, c)
        }
    },
    x = w.Utf8 = {
        stringify: function(a) {
            try {
                return decodeURIComponent(escape(b.stringify(a)))
            } catch(c) {
                throw Error("Malformed UTF-8 data")
            }
        },
        parse: function(a) {
            return b.parse(unescape(encodeURIComponent(a)))
        }
    },
    q = l.BufferedBlockAlgorithm = t.extend({
        reset: function() {
            this.i7b = new r.init;
            this.sQ6K = 0
        },
        uV6P: function(a) {
            "string" == typeof a && (a = x.parse(a));
            this.i7b.concat(a);
            this.sQ6K += a.sigBytes
        },
        ld1x: function(a) {
            var c = this.i7b,
            e = c.words,
            j = c.sigBytes,
            k = this.blockSize,
            b = j / (4 * k),
            b = a ? u.ceil(b) : u.max((b | 0) - this.Gm0x, 0);
            a = b * k;
            j = u.min(4 * a, j);
            if (a) {
                for (var q = 0; q < a; q += k) this.re5j(e, q);
                q = e.splice(0, a);
                c.sigBytes -= j
            }
            return new r.init(q, j)
        },
        clone: function() {
            var a = t.clone.call(this);
            a.i7b = this.i7b.clone();
            return a
        },
        Gm0x: 0
    });
    l.Hasher = q.extend({
        cfg: t.extend(),
        init: function(a) {
            this.cfg = this.cfg.extend(a);
            this.reset()
        },
        reset: function() {
            q.reset.call(this);
            this.lh1x()
        },
        update: function(a) {
            this.uV6P(a);
            this.ld1x();
            return this
        },
        finalize: function(a) {
            a && this.uV6P(a);
            return this.mT2x()
        },
        blockSize: 16,
        lS2x: function(a) {
            return function(b, e) {
                return (new a.init(e)).finalize(b)
            }
        },
        uK6E: function(a) {
            return function(b, e) {
                return (new n.HMAC.init(a, e)).finalize(b)
            }
        }
    });
    var n = d.algo = {};
    return d
} (Math); (function() {
    var u = CryptoJS,
    p = u.lib.WordArray;
    u.enc.Base64 = {
        stringify: function(d) {
            var l = d.words,
            p = d.sigBytes,
            t = this.bA8s;
            d.clamp();
            d = [];
            for (var r = 0; r < p; r += 3) for (var w = (l[r >>> 2] >>> 24 - 8 * (r % 4) & 255) << 16 | (l[r + 1 >>> 2] >>> 24 - 8 * ((r + 1) % 4) & 255) << 8 | l[r + 2 >>> 2] >>> 24 - 8 * ((r + 2) % 4) & 255, v = 0; 4 > v && r + .75 * v < p; v++) d.push(t.charAt(w >>> 6 * (3 - v) & 63));
            if (l = t.charAt(64)) for (; d.length % 4;) d.push(l);
            return d.join("")
        },
        parse: function(d) {
            var l = d.length,
            s = this.bA8s,
            t = s.charAt(64);
            t && (t = d.indexOf(t), -1 != t && (l = t));
            for (var t = [], r = 0, w = 0; w < l; w++) if (w % 4) {
                var v = s.indexOf(d.charAt(w - 1)) << 2 * (w % 4),
                b = s.indexOf(d.charAt(w)) >>> 6 - 2 * (w % 4);
                t[r >>> 2] |= (v | b) << 24 - 8 * (r % 4);
                r++
            }
            return p.create(t, r)
        },
        bA8s: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
    }
})(); (function(u) {
    function p(b, n, a, c, e, j, k) {
        b = b + (n & a | ~n & c) + e + k;
        return (b << j | b >>> 32 - j) + n
    }
    function d(b, n, a, c, e, j, k) {
        b = b + (n & c | a & ~c) + e + k;
        return (b << j | b >>> 32 - j) + n
    }
    function l(b, n, a, c, e, j, k) {
        b = b + (n ^ a ^ c) + e + k;
        return (b << j | b >>> 32 - j) + n
    }
    function s(b, n, a, c, e, j, k) {
        b = b + (a ^ (n | ~c)) + e + k;
        return (b << j | b >>> 32 - j) + n
    }
    for (var t = CryptoJS,
    r = t.lib,
    w = r.WordArray,
    v = r.Hasher,
    r = t.algo,
    b = [], x = 0; 64 > x; x++) b[x] = 4294967296 * u.abs(u.sin(x + 1)) | 0;
    r = r.MD5 = v.extend({
        lh1x: function() {
            this.cO9F = new w.init([1732584193, 4023233417, 2562383102, 271733878])
        },
        re5j: function(q, n) {
            for (var a = 0; 16 > a; a++) {
                var c = n + a,
                e = q[c];
                q[c] = (e << 8 | e >>> 24) & 16711935 | (e << 24 | e >>> 8) & 4278255360
            }
            var a = this.cO9F.words,
            c = q[n + 0],
            e = q[n + 1],
            j = q[n + 2],
            k = q[n + 3],
            z = q[n + 4],
            r = q[n + 5],
            t = q[n + 6],
            w = q[n + 7],
            v = q[n + 8],
            A = q[n + 9],
            B = q[n + 10],
            C = q[n + 11],
            u = q[n + 12],
            D = q[n + 13],
            E = q[n + 14],
            x = q[n + 15],
            f = a[0],
            m = a[1],
            g = a[2],
            h = a[3],
            f = p(f, m, g, h, c, 7, b[0]),
            h = p(h, f, m, g, e, 12, b[1]),
            g = p(g, h, f, m, j, 17, b[2]),
            m = p(m, g, h, f, k, 22, b[3]),
            f = p(f, m, g, h, z, 7, b[4]),
            h = p(h, f, m, g, r, 12, b[5]),
            g = p(g, h, f, m, t, 17, b[6]),
            m = p(m, g, h, f, w, 22, b[7]),
            f = p(f, m, g, h, v, 7, b[8]),
            h = p(h, f, m, g, A, 12, b[9]),
            g = p(g, h, f, m, B, 17, b[10]),
            m = p(m, g, h, f, C, 22, b[11]),
            f = p(f, m, g, h, u, 7, b[12]),
            h = p(h, f, m, g, D, 12, b[13]),
            g = p(g, h, f, m, E, 17, b[14]),
            m = p(m, g, h, f, x, 22, b[15]),
            f = d(f, m, g, h, e, 5, b[16]),
            h = d(h, f, m, g, t, 9, b[17]),
            g = d(g, h, f, m, C, 14, b[18]),
            m = d(m, g, h, f, c, 20, b[19]),
            f = d(f, m, g, h, r, 5, b[20]),
            h = d(h, f, m, g, B, 9, b[21]),
            g = d(g, h, f, m, x, 14, b[22]),
            m = d(m, g, h, f, z, 20, b[23]),
            f = d(f, m, g, h, A, 5, b[24]),
            h = d(h, f, m, g, E, 9, b[25]),
            g = d(g, h, f, m, k, 14, b[26]),
            m = d(m, g, h, f, v, 20, b[27]),
            f = d(f, m, g, h, D, 5, b[28]),
            h = d(h, f, m, g, j, 9, b[29]),
            g = d(g, h, f, m, w, 14, b[30]),
            m = d(m, g, h, f, u, 20, b[31]),
            f = l(f, m, g, h, r, 4, b[32]),
            h = l(h, f, m, g, v, 11, b[33]),
            g = l(g, h, f, m, C, 16, b[34]),
            m = l(m, g, h, f, E, 23, b[35]),
            f = l(f, m, g, h, e, 4, b[36]),
            h = l(h, f, m, g, z, 11, b[37]),
            g = l(g, h, f, m, w, 16, b[38]),
            m = l(m, g, h, f, B, 23, b[39]),
            f = l(f, m, g, h, D, 4, b[40]),
            h = l(h, f, m, g, c, 11, b[41]),
            g = l(g, h, f, m, k, 16, b[42]),
            m = l(m, g, h, f, t, 23, b[43]),
            f = l(f, m, g, h, A, 4, b[44]),
            h = l(h, f, m, g, u, 11, b[45]),
            g = l(g, h, f, m, x, 16, b[46]),
            m = l(m, g, h, f, j, 23, b[47]),
            f = s(f, m, g, h, c, 6, b[48]),
            h = s(h, f, m, g, w, 10, b[49]),
            g = s(g, h, f, m, E, 15, b[50]),
            m = s(m, g, h, f, r, 21, b[51]),
            f = s(f, m, g, h, u, 6, b[52]),
            h = s(h, f, m, g, k, 10, b[53]),
            g = s(g, h, f, m, B, 15, b[54]),
            m = s(m, g, h, f, e, 21, b[55]),
            f = s(f, m, g, h, v, 6, b[56]),
            h = s(h, f, m, g, x, 10, b[57]),
            g = s(g, h, f, m, t, 15, b[58]),
            m = s(m, g, h, f, D, 21, b[59]),
            f = s(f, m, g, h, z, 6, b[60]),
            h = s(h, f, m, g, C, 10, b[61]),
            g = s(g, h, f, m, j, 15, b[62]),
            m = s(m, g, h, f, A, 21, b[63]);
            a[0] = a[0] + f | 0;
            a[1] = a[1] + m | 0;
            a[2] = a[2] + g | 0;
            a[3] = a[3] + h | 0
        },
        mT2x: function() {
            var b = this.i7b,
            n = b.words,
            a = 8 * this.sQ6K,
            c = 8 * b.sigBytes;
            n[c >>> 5] |= 128 << 24 - c % 32;
            var e = u.floor(a / 4294967296);
            n[(c + 64 >>> 9 << 4) + 15] = (e << 8 | e >>> 24) & 16711935 | (e << 24 | e >>> 8) & 4278255360;
            n[(c + 64 >>> 9 << 4) + 14] = (a << 8 | a >>> 24) & 16711935 | (a << 24 | a >>> 8) & 4278255360;
            b.sigBytes = 4 * (n.length + 1);
            this.ld1x();
            b = this.cO9F;
            n = b.words;
            for (a = 0; 4 > a; a++) c = n[a],
            n[a] = (c << 8 | c >>> 24) & 16711935 | (c << 24 | c >>> 8) & 4278255360;
            return b
        },
        clone: function() {
            var b = v.clone.call(this);
            b.cO9F = this.cO9F.clone();
            return b
        }
    });
    t.MD5 = v.lS2x(r);
    t.HmacMD5 = v.uK6E(r)
})(Math); (function() {
    var u = CryptoJS,
    p = u.lib,
    d = p.Base,
    l = p.WordArray,
    p = u.algo,
    s = p.EvpKDF = d.extend({
        cfg: d.extend({
            keySize: 4,
            hasher: p.MD5,
            iterations: 1
        }),
        init: function(d) {
            this.cfg = this.cfg.extend(d)
        },
        compute: function(d, r) {
            for (var p = this.cfg,
            s = p.hasher.create(), b = l.create(), u = b.words, q = p.keySize, p = p.iterations; u.length < q;) {
                n && s.update(n);
                var n = s.update(d).finalize(r);
                s.reset();
                for (var a = 1; a < p; a++) n = s.finalize(n),
                s.reset();
                b.concat(n)
            }
            b.sigBytes = 4 * q;
            return b
        }
    });
    u.EvpKDF = function(d, l, p) {
        return s.create(p).compute(d, l)
    }
})();
CryptoJS.lib.Cipher ||
function(u) {
    var p = CryptoJS,
    d = p.lib,
    l = d.Base,
    s = d.WordArray,
    t = d.BufferedBlockAlgorithm,
    r = p.enc.Base64,
    w = p.algo.EvpKDF,
    v = d.Cipher = t.extend({
        cfg: l.extend(),
        createEncryptor: function(e, a) {
            return this.create(this.Gw0x, e, a)
        },
        createDecryptor: function(e, a) {
            return this.create(this.bql0x, e, a)
        },
        init: function(e, a, b) {
            this.cfg = this.cfg.extend(b);
            this.Ye5j = e;
            this.J8B = a;
            this.reset()
        },
        reset: function() {
            t.reset.call(this);
            this.lh1x()
        },
        process: function(e) {
            this.uV6P(e);
            return this.ld1x()
        },
        finalize: function(e) {
            e && this.uV6P(e);
            return this.mT2x()
        },
        keySize: 4,
        ivSize: 4,
        Gw0x: 1,
        bql0x: 2,
        lS2x: function(e) {
            return {
                encrypt: function(b, k, d) {
                    return ("string" == typeof k ? c: a).encrypt(e, b, k, d)
                },
                decrypt: function(b, k, d) {
                    return ("string" == typeof k ? c: a).decrypt(e, b, k, d)
                }
            }
        }
    });
    d.StreamCipher = v.extend({
        mT2x: function() {
            return this.ld1x(!0)
        },
        blockSize: 1
    });
    var b = p.mode = {},
    x = function(e, a, b) {
        var c = this.sO6I;
        c ? this.sO6I = u: c = this.Ez9q;
        for (var d = 0; d < b; d++) e[a + d] ^= c[d]
    },
    q = (d.BlockCipherMode = l.extend({
        createEncryptor: function(e, a) {
            return this.Encryptor.create(e, a)
        },
        createDecryptor: function(e, a) {
            return this.Decryptor.create(e, a)
        },
        init: function(e, a) {
            this.uB6v = e;
            this.sO6I = a
        }
    })).extend();
    q.Encryptor = q.extend({
        processBlock: function(e, a) {
            var b = this.uB6v,
            c = b.blockSize;
            x.call(this, e, a, c);
            b.encryptBlock(e, a);
            this.Ez9q = e.slice(a, a + c)
        }
    });
    q.Decryptor = q.extend({
        processBlock: function(e, a) {
            var b = this.uB6v,
            c = b.blockSize,
            d = e.slice(a, a + c);
            b.decryptBlock(e, a);
            x.call(this, e, a, c);
            this.Ez9q = d
        }
    });
    b = b.CBC = q;
    q = (p.pad = {}).Pkcs7 = {
        pad: function(a, b) {
            for (var c = 4 * b,
            c = c - a.sigBytes % c,
            d = c << 24 | c << 16 | c << 8 | c,
            l = [], n = 0; n < c; n += 4) l.push(d);
            c = s.create(l, c);
            a.concat(c)
        },
        unpad: function(a) {
            a.sigBytes -= a.words[a.sigBytes - 1 >>> 2] & 255
        }
    };
    d.BlockCipher = v.extend({
        cfg: v.cfg.extend({
            mode: b,
            padding: q
        }),
        reset: function() {
            v.reset.call(this);
            var a = this.cfg,
            b = a.iv,
            a = a.mode;
            if (this.Ye5j == this.Gw0x) var c = a.createEncryptor;
            else c = a.createDecryptor,
            this.Gm0x = 1;
            this.eS9J = c.call(a, this, b && b.words)
        },
        re5j: function(a, b) {
            this.eS9J.processBlock(a, b)
        },
        mT2x: function() {
            var a = this.cfg.padding;
            if (this.Ye5j == this.Gw0x) {
                a.pad(this.i7b, this.blockSize);
                var b = this.ld1x(!0)
            } else b = this.ld1x(!0),
            a.unpad(b);
            return b
        },
        blockSize: 4
    });
    var n = d.CipherParams = l.extend({
        init: function(a) {
            this.mixIn(a)
        },
        toString: function(a) {
            return (a || this.formatter).stringify(this)
        }
    }),
    b = (p.format = {}).OpenSSL = {
        stringify: function(a) {
            var b = a.ciphertext;
            a = a.salt;
            return (a ? s.create([1398893684, 1701076831]).concat(a).concat(b) : b).toString(r)
        },
        parse: function(a) {
            a = r.parse(a);
            var b = a.words;
            if (1398893684 == b[0] && 1701076831 == b[1]) {
                var c = s.create(b.slice(2, 4));
                b.splice(0, 4);
                a.sigBytes -= 16
            }
            return n.create({
                ciphertext: a,
                salt: c
            })
        }
    },
    a = d.SerializableCipher = l.extend({
        cfg: l.extend({
            format: b
        }),
        encrypt: function(a, b, c, d) {
            d = this.cfg.extend(d);
            var l = a.createEncryptor(c, d);
            b = l.finalize(b);
            l = l.cfg;
            return n.create({
                ciphertext: b,
                key: c,
                iv: l.iv,
                algorithm: a,
                mode: l.mode,
                padding: l.padding,
                blockSize: a.blockSize,
                formatter: d.format
            })
        },
        decrypt: function(a, b, c, d) {
            d = this.cfg.extend(d);
            b = this.JC0x(b, d.format);
            return a.createDecryptor(c, d).finalize(b.ciphertext)
        },
        JC0x: function(a, b) {
            return "string" == typeof a ? b.parse(a, this) : a
        }
    }),
    p = (p.kdf = {}).OpenSSL = {
        execute: function(a, b, c, d) {
            d || (d = s.random(8));
            a = w.create({
                keySize: b + c
            }).compute(a, d);
            c = s.create(a.words.slice(b), 4 * c);
            a.sigBytes = 4 * b;
            return n.create({
                key: a,
                iv: c,
                salt: d
            })
        }
    },
    c = d.PasswordBasedCipher = a.extend({
        cfg: a.cfg.extend({
            kdf: p
        }),
        encrypt: function(b, c, d, l) {
            l = this.cfg.extend(l);
            d = l.kdf.execute(d, b.keySize, b.ivSize);
            l.iv = d.iv;
            b = a.encrypt.call(this, b, c, d.key, l);
            b.mixIn(d);
            return b
        },
        decrypt: function(b, c, d, l) {
            l = this.cfg.extend(l);
            c = this.JC0x(c, l.format);
            d = l.kdf.execute(d, b.keySize, b.ivSize, c.salt);
            l.iv = d.iv;
            return a.decrypt.call(this, b, c, d.key, l)
        }
    })
} (); (function() {
    for (var u = CryptoJS,
    p = u.lib.BlockCipher,
    d = u.algo,
    l = [], s = [], t = [], r = [], w = [], v = [], b = [], x = [], q = [], n = [], a = [], c = 0; 256 > c; c++) a[c] = 128 > c ? c << 1 : c << 1 ^ 283;
    for (var e = 0,
    j = 0,
    c = 0; 256 > c; c++) {
        var k = j ^ j << 1 ^ j << 2 ^ j << 3 ^ j << 4,
        k = k >>> 8 ^ k & 255 ^ 99;
        l[e] = k;
        s[k] = e;
        var z = a[e],
        F = a[z],
        G = a[F],
        y = 257 * a[k] ^ 16843008 * k;
        t[e] = y << 24 | y >>> 8;
        r[e] = y << 16 | y >>> 16;
        w[e] = y << 8 | y >>> 24;
        v[e] = y;
        y = 16843009 * G ^ 65537 * F ^ 257 * z ^ 16843008 * e;
        b[k] = y << 24 | y >>> 8;
        x[k] = y << 16 | y >>> 16;
        q[k] = y << 8 | y >>> 24;
        n[k] = y;
        e ? (e = z ^ a[a[a[G ^ z]]], j ^= a[a[j]]) : e = j = 1
    }
    var H = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54],
    d = d.AES = p.extend({
        lh1x: function() {
            for (var a = this.J8B,
            c = a.words,
            d = a.sigBytes / 4,
            a = 4 * ((this.ZK7D = d + 6) + 1), e = this.bqB0x = [], j = 0; j < a; j++) if (j < d) e[j] = c[j];
            else {
                var k = e[j - 1];
                j % d ? 6 < d && 4 == j % d && (k = l[k >>> 24] << 24 | l[k >>> 16 & 255] << 16 | l[k >>> 8 & 255] << 8 | l[k & 255]) : (k = k << 8 | k >>> 24, k = l[k >>> 24] << 24 | l[k >>> 16 & 255] << 16 | l[k >>> 8 & 255] << 8 | l[k & 255], k ^= H[j / d | 0] << 24);
                e[j] = e[j - d] ^ k
            }
            c = this.bqF0x = [];
            for (d = 0; d < a; d++) j = a - d,
            k = d % 4 ? e[j] : e[j - 4],
            c[d] = 4 > d || 4 >= j ? k: b[l[k >>> 24]] ^ x[l[k >>> 16 & 255]] ^ q[l[k >>> 8 & 255]] ^ n[l[k & 255]]
        },
        encryptBlock: function(a, b) {
            this.EJ9A(a, b, this.bqB0x, t, r, w, v, l)
        },
        decryptBlock: function(a, c) {
            var d = a[c + 1];
            a[c + 1] = a[c + 3];
            a[c + 3] = d;
            this.EJ9A(a, c, this.bqF0x, b, x, q, n, s);
            d = a[c + 1];
            a[c + 1] = a[c + 3];
            a[c + 3] = d
        },
        EJ9A: function(a, b, c, d, e, j, l, f) {
            for (var m = this.ZK7D,
            g = a[b] ^ c[0], h = a[b + 1] ^ c[1], k = a[b + 2] ^ c[2], n = a[b + 3] ^ c[3], p = 4, r = 1; r < m; r++) var q = d[g >>> 24] ^ e[h >>> 16 & 255] ^ j[k >>> 8 & 255] ^ l[n & 255] ^ c[p++],
            s = d[h >>> 24] ^ e[k >>> 16 & 255] ^ j[n >>> 8 & 255] ^ l[g & 255] ^ c[p++],
            t = d[k >>> 24] ^ e[n >>> 16 & 255] ^ j[g >>> 8 & 255] ^ l[h & 255] ^ c[p++],
            n = d[n >>> 24] ^ e[g >>> 16 & 255] ^ j[h >>> 8 & 255] ^ l[k & 255] ^ c[p++],
            g = q,
            h = s,
            k = t;
            q = (f[g >>> 24] << 24 | f[h >>> 16 & 255] << 16 | f[k >>> 8 & 255] << 8 | f[n & 255]) ^ c[p++];
            s = (f[h >>> 24] << 24 | f[k >>> 16 & 255] << 16 | f[n >>> 8 & 255] << 8 | f[g & 255]) ^ c[p++];
            t = (f[k >>> 24] << 24 | f[n >>> 16 & 255] << 16 | f[g >>> 8 & 255] << 8 | f[h & 255]) ^ c[p++];
            n = (f[n >>> 24] << 24 | f[g >>> 16 & 255] << 16 | f[h >>> 8 & 255] << 8 | f[k & 255]) ^ c[p++];
            a[b] = q;
            a[b + 1] = s;
            a[b + 2] = t;
            a[b + 3] = n
        },
        keySize: 8
    });
    u.AES = p.lS2x(d)
})();

// RSA加密用到的所有函数
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 = 16 == a.radix ? biFromHex(c[e]) : biFromString(c[e], a.radix), g = a.barrett.powMod(h, a.d), f = 0; f <= biHighIndex(g); ++f) d += String.fromCharCode(255 & g.digits[f], g.digits[f] >> 8);
    return 0 == d.charCodeAt(d.length - 1) && (d = d.substring(0, d.length - 1)),
    d
}
function setMaxDigits(a) {
    maxDigits = a,
    ZERO_ARRAY = new Array(maxDigits);
    for (var b = 0; b < ZERO_ARRAY.length; b++) ZERO_ARRAY[b] = 0;
    bigZero = new BigInt,
    bigOne = new BigInt,
    bigOne.digits[0] = 1
}
function BigInt(a) {
    this.digits = "boolean" == typeof a && 1 == a ? null: ZERO_ARRAY.slice(0),
    this.isNeg = !1
}
function biFromDecimal(a) {
    for (var d, e, f, b = "-" == a.charAt(0), c = b ? 1 : 0; c < a.length && "0" == a.charAt(c);)++c;
    if (c == a.length) d = new BigInt;
    else {
        for (e = a.length - c, f = e % dpl10, 0 == f && (f = dpl10), d = biFromNumber(Number(a.substr(c, f))), c += f; c < a.length;) d = biAdd(biMultiply(d, lr10), biFromNumber(Number(a.substr(c, dpl10)))),
        c += dpl10;
        d.isNeg = b
    }
    return d
}
function biCopy(a) {
    var b = new BigInt(!0);
    return b.digits = a.digits.slice(0),
    b.isNeg = a.isNeg,
    b
}
function biFromNumber(a) {
    var c, b = new BigInt;
    for (b.isNeg = 0 > a, a = Math.abs(a), c = 0; a > 0;) b.digits[c++] = a & maxDigitVal,
    a >>= biRadixBits;
    return b
}
function reverseStr(a) {
    var c, b = "";
    for (c = a.length - 1; c > -1; --c) b += a.charAt(c);
    return b
}
function biToString(a, b) {
    var d, e, c = new BigInt;
    for (c.digits[0] = b, d = biDivideModulo(a, c), e = hexatrigesimalToChar[d[1].digits[0]]; 1 == biCompare(d[0], bigZero);) d = biDivideModulo(d[0], c),
    digit = d[1].digits[0],
    e += hexatrigesimalToChar[d[1].digits[0]];
    return (a.isNeg ? "-": "") + reverseStr(e)
}
function biToDecimal(a) {
    var c, d, b = new BigInt;
    for (b.digits[0] = 10, c = biDivideModulo(a, b), d = String(c[1].digits[0]); 1 == biCompare(c[0], bigZero);) c = biDivideModulo(c[0], b),
    d += String(c[1].digits[0]);
    return (a.isNeg ? "-": "") + reverseStr(d)
}
function digitToHex(a) {
    var b = 15,
    c = "";
    for (i = 0; 4 > i; ++i) c += hexToChar[a & b],
    a >>>= 4;
    return reverseStr(c)
}
function biToHex(a) {
    var d, b = "";
    for (biHighIndex(a), d = biHighIndex(a); d > -1; --d) b += digitToHex(a.digits[d]);
    return b
}
function charToHex(a) {
    var h, b = 48,
    c = b + 9,
    d = 97,
    e = d + 25,
    f = 65,
    g = 90;
    return h = a >= b && c >= a ? a - b: a >= f && g >= a ? 10 + a - f: a >= d && e >= a ? 10 + a - d: 0
}
function hexToDigit(a) {
    var d, b = 0,
    c = Math.min(a.length, 4);
    for (d = 0; c > d; ++d) b <<= 4,
    b |= charToHex(a.charCodeAt(d));
    return b
}
function biFromHex(a) {
    var d, e, b = new BigInt,
    c = a.length;
    for (d = c, e = 0; d > 0; d -= 4, ++e) b.digits[e] = hexToDigit(a.substr(Math.max(d - 4, 0), Math.min(d, 4)));
    return b
}
function biFromString(a, b) {
    var g, h, i, j, c = "-" == a.charAt(0),
    d = c ? 1 : 0,
    e = new BigInt,
    f = new BigInt;
    for (f.digits[0] = 1, g = a.length - 1; g >= d; g--) h = a.charCodeAt(g),
    i = charToHex(h),
    j = biMultiplyDigit(f, i),
    e = biAdd(e, j),
    f = biMultiplyDigit(f, b);
    return e.isNeg = c,
    e
}
function biDump(a) {
    return (a.isNeg ? "-": "") + a.digits.join(" ")
}
function biAdd(a, b) {
    var c, d, e, f;
    if (a.isNeg != b.isNeg) b.isNeg = !b.isNeg,
    c = biSubtract(a, b),
    b.isNeg = !b.isNeg;
    else {
        for (c = new BigInt, d = 0, f = 0; f < a.digits.length; ++f) e = a.digits[f] + b.digits[f] + d,
        c.digits[f] = 65535 & e,
        d = Number(e >= biRadix);
        c.isNeg = a.isNeg
    }
    return c
}
function biSubtract(a, b) {
    var c, d, e, f;
    if (a.isNeg != b.isNeg) b.isNeg = !b.isNeg,
    c = biAdd(a, b),
    b.isNeg = !b.isNeg;
    else {
        for (c = new BigInt, e = 0, f = 0; f < a.digits.length; ++f) d = a.digits[f] - b.digits[f] + e,
        c.digits[f] = 65535 & d,
        c.digits[f] < 0 && (c.digits[f] += biRadix),
        e = 0 - Number(0 > d);
        if ( - 1 == e) {
            for (e = 0, f = 0; f < a.digits.length; ++f) d = 0 - c.digits[f] + e,
            c.digits[f] = 65535 & d,
            c.digits[f] < 0 && (c.digits[f] += biRadix),
            e = 0 - Number(0 > d);
            c.isNeg = !a.isNeg
        } else c.isNeg = a.isNeg
    }
    return c
}
function biHighIndex(a) {
    for (var b = a.digits.length - 1; b > 0 && 0 == a.digits[b];)--b;
    return b
}
function biNumBits(a) {
    var e, b = biHighIndex(a),
    c = a.digits[b],
    d = (b + 1) * bitsPerDigit;
    for (e = d; e > d - bitsPerDigit && 0 == (32768 & c); --e) c <<= 1;
    return e
}
function biMultiply(a, b) {
    var d, h, i, k, c = new BigInt,
    e = biHighIndex(a),
    f = biHighIndex(b);
    for (k = 0; f >= k; ++k) {
        for (d = 0, i = k, j = 0; e >= j; ++j, ++i) h = c.digits[i] + a.digits[j] * b.digits[k] + d,
        c.digits[i] = h & maxDigitVal,
        d = h >>> biRadixBits;
        c.digits[k + e + 1] = d
    }
    return c.isNeg = a.isNeg != b.isNeg,
    c
}
function biMultiplyDigit(a, b) {
    var c, d, e, f;
    for (result = new BigInt, c = biHighIndex(a), d = 0, f = 0; c >= f; ++f) e = result.digits[f] + a.digits[f] * b + d,
    result.digits[f] = e & maxDigitVal,
    d = e >>> biRadixBits;
    return result.digits[1 + c] = d,
    result
}
function arrayCopy(a, b, c, d, e) {
    var g, h, f = Math.min(b + e, a.length);
    for (g = b, h = d; f > g; ++g, ++h) c[h] = a[g]
}
function biShiftLeft(a, b) {
    var e, f, g, h, c = Math.floor(b / bitsPerDigit),
    d = new BigInt;
    for (arrayCopy(a.digits, 0, d.digits, c, d.digits.length - c), e = b % bitsPerDigit, f = bitsPerDigit - e, g = d.digits.length - 1, h = g - 1; g > 0; --g, --h) d.digits[g] = d.digits[g] << e & maxDigitVal | (d.digits[h] & highBitMasks[e]) >>> f;
    return d.digits[0] = d.digits[g] << e & maxDigitVal,
    d.isNeg = a.isNeg,
    d
}
function biShiftRight(a, b) {
    var e, f, g, h, c = Math.floor(b / bitsPerDigit),
    d = new BigInt;
    for (arrayCopy(a.digits, c, d.digits, 0, a.digits.length - c), e = b % bitsPerDigit, f = bitsPerDigit - e, g = 0, h = g + 1; g < d.digits.length - 1; ++g, ++h) d.digits[g] = d.digits[g] >>> e | (d.digits[h] & lowBitMasks[e]) << f;
    return d.digits[d.digits.length - 1] >>>= e,
    d.isNeg = a.isNeg,
    d
}
function biMultiplyByRadixPower(a, b) {
    var c = new BigInt;
    return arrayCopy(a.digits, 0, c.digits, b, c.digits.length - b),
    c
}
function biDivideByRadixPower(a, b) {
    var c = new BigInt;
    return arrayCopy(a.digits, b, c.digits, 0, c.digits.length - b),
    c
}
function biModuloByRadixPower(a, b) {
    var c = new BigInt;
    return arrayCopy(a.digits, 0, c.digits, 0, b),
    c
}
function biCompare(a, b) {
    if (a.isNeg != b.isNeg) return 1 - 2 * Number(a.isNeg);
    for (var c = a.digits.length - 1; c >= 0; --c) if (a.digits[c] != b.digits[c]) return a.isNeg ? 1 - 2 * Number(a.digits[c] > b.digits[c]) : 1 - 2 * Number(a.digits[c] < b.digits[c]);
    return 0
}
function biDivideModulo(a, b) {
    var f, g, h, i, j, k, l, m, n, o, p, q, r, s, c = biNumBits(a),
    d = biNumBits(b),
    e = b.isNeg;
    if (d > c) return a.isNeg ? (f = biCopy(bigOne), f.isNeg = !b.isNeg, a.isNeg = !1, b.isNeg = !1, g = biSubtract(b, a), a.isNeg = !0, b.isNeg = e) : (f = new BigInt, g = biCopy(a)),
    new Array(f, g);
    for (f = new BigInt, g = a, h = Math.ceil(d / bitsPerDigit) - 1, i = 0; b.digits[h] < biHalfRadix;) b = biShiftLeft(b, 1),
    ++i,
    ++d,
    h = Math.ceil(d / bitsPerDigit) - 1;
    for (g = biShiftLeft(g, i), c += i, j = Math.ceil(c / bitsPerDigit) - 1, k = biMultiplyByRadixPower(b, j - h); - 1 != biCompare(g, k);)++f.digits[j - h],
    g = biSubtract(g, k);
    for (l = j; l > h; --l) {
        for (m = l >= g.digits.length ? 0 : g.digits[l], n = l - 1 >= g.digits.length ? 0 : g.digits[l - 1], o = l - 2 >= g.digits.length ? 0 : g.digits[l - 2], p = h >= b.digits.length ? 0 : b.digits[h], q = h - 1 >= b.digits.length ? 0 : b.digits[h - 1], f.digits[l - h - 1] = m == p ? maxDigitVal: Math.floor((m * biRadix + n) / p), r = f.digits[l - h - 1] * (p * biRadix + q), s = m * biRadixSquared + (n * biRadix + o); r > s;)--f.digits[l - h - 1],
        r = f.digits[l - h - 1] * (p * biRadix | q),
        s = m * biRadix * biRadix + (n * biRadix + o);
        k = biMultiplyByRadixPower(b, l - h - 1),
        g = biSubtract(g, biMultiplyDigit(k, f.digits[l - h - 1])),
        g.isNeg && (g = biAdd(g, k), --f.digits[l - h - 1])
    }
    return g = biShiftRight(g, i),
    f.isNeg = a.isNeg != e,
    a.isNeg && (f = e ? biAdd(f, bigOne) : biSubtract(f, bigOne), b = biShiftRight(b, i), g = biSubtract(b, g)),
    0 == g.digits[0] && 0 == biHighIndex(g) && (g.isNeg = !1),
    new Array(f, g)
}
function biDivide(a, b) {
    return biDivideModulo(a, b)[0]
}
function biModulo(a, b) {
    return biDivideModulo(a, b)[1]
}
function biMultiplyMod(a, b, c) {
    return biModulo(biMultiply(a, b), c)
}
function biPow(a, b) {
    for (var c = bigOne,
    d = a;;) {
        if (0 != (1 & b) && (c = biMultiply(c, d)), b >>= 1, 0 == b) break;
        d = biMultiply(d, d)
    }
    return c
}
function biPowMod(a, b, c) {
    for (var d = bigOne,
    e = a,
    f = b;;) {
        if (0 != (1 & f.digits[0]) && (d = biMultiplyMod(d, e, c)), f = biShiftRight(f, 1), 0 == f.digits[0] && 0 == biHighIndex(f)) break;
        e = biMultiplyMod(e, e, c)
    }
    return d
}
function BarrettMu(a) {
    this.modulus = biCopy(a),
    this.k = biHighIndex(this.modulus) + 1;
    var b = new BigInt;
    b.digits[2 * this.k] = 1,
    this.mu = biDivide(b, this.modulus),
    this.bkplus1 = new BigInt,
    this.bkplus1.digits[this.k + 1] = 1,
    this.modulo = BarrettMu_modulo,
    this.multiplyMod = BarrettMu_multiplyMod,
    this.powMod = BarrettMu_powMod
}
function BarrettMu_modulo(a) {
    var i, b = biDivideByRadixPower(a, this.k - 1),
    c = biMultiply(b, this.mu),
    d = biDivideByRadixPower(c, this.k + 1),
    e = biModuloByRadixPower(a, this.k + 1),
    f = biMultiply(d, this.modulus),
    g = biModuloByRadixPower(f, this.k + 1),
    h = biSubtract(e, g);
    for (h.isNeg && (h = biAdd(h, this.bkplus1)), i = biCompare(h, this.modulus) >= 0; i;) h = biSubtract(h, this.modulus),
    i = biCompare(h, this.modulus) >= 0;
    return h
}
function BarrettMu_multiplyMod(a, b) {
    var c = biMultiply(a, b);
    return this.modulo(c)
}
function BarrettMu_powMod(a, b) {
    var d, e, c = new BigInt;
    for (c.digits[0] = 1, d = a, e = b;;) {
        if (0 != (1 & e.digits[0]) && (c = this.multiplyMod(c, d)), e = biShiftRight(e, 1), 0 == e.digits[0] && 0 == biHighIndex(e)) break;
        d = this.multiplyMod(d, d)
    }
    return c
}
var maxDigits, ZERO_ARRAY, bigZero, bigOne, dpl10, lr10, hexatrigesimalToChar, hexToChar, highBitMasks, lowBitMasks, biRadixBase = 2,
biRadixBits = 16,
bitsPerDigit = biRadixBits,
biRadix = 65536,
biHalfRadix = biRadix >>> 1,
biRadixSquared = biRadix * biRadix,
maxDigitVal = biRadix - 1,
maxInteger = 9999999999999998;
setMaxDigits(20),
dpl10 = 15,
lr10 = biFromNumber(1e15),
hexatrigesimalToChar = new Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"),
hexToChar = new Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"),
highBitMasks = new Array(0, 32768, 49152, 57344, 61440, 63488, 64512, 65024, 65280, 65408, 65472, 65504, 65520, 65528, 65532, 65534, 65535),
lowBitMasks = new Array(0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535);

// 生成params和encSecKey的所有函数(已删减)
// 生成16位随机字符串
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
    };
// AES加密
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()
    };
// RSA加密
function c(a, b, c) {
        var d, e;
        return setMaxDigits(131),
        d = new RSAKeyPair(b, "", c),
        e = encryptedString(d, a)
    };
// window.asrsea函数的等价函数,在此生成encText(即params)和encSecKey
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
    };

python代码

# coding=utf-8
import execjs
import requests

# 读取js代码
with open('网易云params和encSecKey来源.js', 'r', encoding='gb2312') as fp:
    js = fp.read()
# 初始化
func = execjs.compile(js)

# 调用a函数,生成16位随机字符串
i = func.call('a', 16)

# 调用c函数,生成encSecKey
encSecKey = func.call('c', i, "010001",
                      "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7")

# 歌曲id
song_id = input('请输入歌曲id:')  # 例:'1401790402'
# 两次调用b函数,生成encText
# 初始化,格式一定要如下所示,否则会返回'参数错误'
encText1 = '{"ids":"[%s]","level":"standard","encodeType":"aac","csrf_token":"477c1bd99fddedb3adc074f47fee2d35"}' % song_id
# 第一次调用
encText2 = func.call('b', encText1, "0CoJUm6Qyw8W8jud")
# 第二次调用
encText = func.call('b', encText2, i)

print(encText)
print(encSecKey)

# 构建请求头
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36',
    # 这里需要传入会员账户的cookie才能访问会员歌曲,暂时无法破解,否则只能访问免费歌曲
    'Cookie': 'MUSIC_U=f52f220df171da480dbf33ce89947961585a7fdf08c89a2a4bdd6efebd86544233a649814e309366;',
}
# 请求url
url = 'https://music.163.com/weapi/song/enhance/player/url/v1'
# 构建data
data = {
    'params': encText,
    'encSecKey': encSecKey
}
# 发送请求
request = requests.post(url=url, headers=headers, data=data)
# 返回信息
print(request.text)

版本3: 完善版【其实还是借鉴了我的ID是局长大人的博客......】

# coding=utf-8
from Crypto.Cipher import AES
import base64
import codecs
import random
import requests
import math
import json
import re


class WangyiyunDownload(object):
    def __init__(self):
        # "爱心", "女孩", "惊恐", "大笑"的值
        # 对应的js --> bqL0x(["爱心", "女孩", "惊恐", "大笑"])
        self.key = '0CoJUm6Qyw8W8jud'
        # "流泪", "强"的值
        # 对应的js --> bqL0x(["流泪", "强"])
        self.public_key = "010001"
        # 一串表情的值(省略,对应的js --> Yb5g.md)
        # 对应的js --> bqL0x(Yb5g.md)
        self.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
        # 偏移量
        self.iv = "0102030405060708"
        # 请求头
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36',
            # 这里需传入登录cookie,并且必须是会员账户才能访问会员曲目,否则只能访问免费曲目
            'Cookie': 'MUSIC_U=f52f220df171da480dbf33ce89947961585a7fdf08c89a2a4bdd6efebd86544233a649814e309366;',
        }
        # 请求url
        self.url = 'https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token='

    # 生成16位随机数字符串
    # 对应的js --> a函数
    def set_random_num(self):
        random_num = ''
        # 从此字符串随机取出16个字符
        string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        for i in range(16):
            # random.uniform(0, 1) * len(string): 生成一个实数,范围在0 <= n < len(string)
            # math.floor(n): 将n向下取整
            n = math.floor(random.uniform(0, 1) * len(string))
            # 从string中取出下标为n的字符拼接到random_num中
            random_num += string[n]
        print('已生成16位随机字符串!')
        # 返回16位随机数字符串
        return random_num

    # 生成encSecKey
    # 对应的js --> c函数
    # 通过public_key和modulus对random_num进行RSA加密
    # random_num: 生成的16位随机数字符串
    def RSA_encrypt(self, random_num):
        # 先将16位随机数字符串倒序并以utf-8编码
        random_num = random_num[::-1].encode('utf-8')
        # 然后再将其以hex(16进制)编码
        random_num = codecs.encode(random_num, 'hex_codec')
        # 加密(三者均要从16进制转换为10进制)
        # int(n, 16) --> 将16进制字符串n转换为10进制
        encryption = int(random_num, 16) ** int(self.public_key, 16) % int(self.modulus, 16)
        # 将加密后的数据转换为16进制字符串
        encryption = format(encryption, 'x')
        # 返回加密后的字符串
        return encryption

    # 生成params
    # 对应的js --> b函数
    # 根据key和iv对msg进行AES加密,需调用两次
    # key:
    #   第一次: key
    #   第二次: random_num
    # iv: 偏移量iv
    def AES_encrypt(self, msg, key, iv):
        # 先将msg按需补全至16的倍数
        # 需补全的位数
        pad = (16 - len(msg) % 16)
        # 补全
        msg = msg + pad * chr(pad)
        # 这里需要将key,iv和msg均以utf-8编码
        key = key.encode('utf-8')
        iv = iv.encode('utf-8')
        msg = msg.encode('utf-8')
        # 根据key和iv生成密钥,模式为CBC模式
        encryptor = AES.new(key, AES.MODE_CBC, iv)
        # 加密
        encrypt_aes = encryptor.encrypt(msg)
        # 先将加密后的值进行base64编码
        encrypt_text = base64.encodebytes(encrypt_aes)
        # 再将其转换为utf-8字符串
        encrypt_text = str(encrypt_text, 'utf-8')
        # 返回加密后的字符串
        return encrypt_text

    # 根据歌曲song_id,生成需要传输的data
    # 其中包括params和encSecKey
    def construct_data(self, song_id):
        # 先生成16位随机数字符串
        random_num = self.set_random_num()
        # 生成encSecKey
        encSecKey = self.RSA_encrypt(random_num=random_num)
        print('已生成encSecKey!')
        # 调用两次AES加密生成params
        # 先初始化歌曲song_info
        song_info = '{"ids":"[%s]","level":"standard","encodeType":"aac","csrf_token":"477c1bd99fddedb3adc074f47fee2d35"}' % song_id
        # 第一次加密,传入encText, key和iv
        first_encryption = self.AES_encrypt(msg=song_info, key=self.key, iv=self.iv)
        # 第二次加密, 传入first_encryption, random_num和iv
        encText = self.AES_encrypt(msg=first_encryption, key=random_num, iv=self.iv)
        print('已生成encText!')
        # 生成data
        data = {
            'params': encText,
            'encSecKey': encSecKey
        }
        # 返回data
        return data

    # 发送请求,获取下载链接
    def get_real_url(self):
        # 输入歌曲song_id
        self.song_id = input('请输入歌曲id:')
        # 获取data
        data = self.construct_data(song_id=self.song_id)
        print('正在发送请求......')
        # 发送请求
        request = requests.post(url=self.url, headers=self.headers, data=data)
        # 初始化real_url
        real_url = ''
        # 处理返回信息
        try:
            js_text = json.loads(request.text)
            data = js_text['data']
            if len(data) != 0:
                code = data[0]['code']
                # 获取成功
                if code == 200:
                    # 歌曲真实地址
                    real_url = data[0]['url']
                    print('下载链接: %s' % real_url)
                # 会员歌曲,cookie过期,无法下载
                elif code == -110:
                    print('这是会员歌曲,而会员账户cookie已过期!')
                # 未搜索到歌曲
                elif code == 404:
                    print('输入的歌曲id有误!')
                else:
                    print('未知错误!')
        except:
            print('生成的params和encSecKey有误!可重试!')
        # 返回real_url
        return real_url

    def download(self):
        # 获取下载链接
        real_url = self.get_real_url()
        if real_url == '':
            print('链接获取失败!')
        else:
            # 获取歌曲名和歌手名
            url = 'https://music.163.com/song?id=%s' % self.song_id
            text = requests.get(url=url, headers=self.headers).text
            pattern = re.compile(r'歌曲名《(.*?)》.*?由 (.*?) 演唱.*?')
            song = pattern.findall(text)[0][0]
            print('歌曲名: %s' % song)
            singer = pattern.findall(text)[0][1]
            print('歌手名: %s' % singer)
            # 拼接文件名
            file = '{}-{}.mp3'.format(singer, song)
            # 开始下载
            print('正在下载......')
            content = requests.get(url=real_url, headers=self.headers).content
            with open(file, 'wb') as fp:
                fp.write(content)
            print('下载成功!')


if __name__ == '__main__':
    WangyiyunDownload().download()

总结

这个总结你们一定想不到......

因为即使那么麻烦破译了params和encSecKey,

要下载会员歌曲,

还是要在请求头headers中传入会员账户登录的cookie......

(这在上面的代码中有注释提及,

要通过抓包或者浏览器开发者模式获取cookie中的"MUSIC_U"值,

再传入请求头才可以获取真实url......)

而且cookie还存在有效期......

所以,

总结是: 支持正版!

你可能感兴趣的:(python爬虫)