【爬虫分享】某云音乐下载
本文最后更新于184 天前,其中的信息可能已经过时,如有错误请发送邮件到lysun26@163.com

前言

国庆假期马上就要来了,于是现在虽然人在工位上,但是心早就飘到外面去了。而不想科研的我,就想找点其他事情做,而恰好想到了之前看到过爬取音乐的文章,但是当时也没看懂,就不了了之了。然后今天我就准备重新将其拾起来,认真的学了学,收获还是不少的。

今天最大的收获主要是由两点,一是了解了一些加密算法(但是具体的原理我没看,这个我倒不是很感兴趣),一是对 Fiddler 抓包软件更熟悉了一些,而且还感到了 Fiddler 软件的强大,成功的吸引了我的兴趣,准备去 B 站看看相关视频了。好了,接下来进入正题。

寻找url

首先第一步,就是找到 url 所在的位置。我们随便搜索一首歌(非 vip),按 F12,打开开发工具,然后进入到 Network 选项卡,点击 Fetch/XHR(一般都是要到这里找),然后我们刷新界面,最后点击播放按钮,可以看到有一系列的文件出现,如下图所示:

找到 v1 开头的那个文件,然后点击 preview,展开后,就可以看到这个音乐对应的 url 了。

这样我们就定位到了 url 所在的位置了,接下来就是如何发出请求了。点 Headers,可以看到请求的 URL,我们分析一下这个请求,显然这个 csrf_token 是网易云音乐采取的一定的措施,防止爬虫的(具体用处我也不知道),请求类型为 POST。

然后点开 Payload,

可以看到需要提交两个参数,分别是 params 和 encSecKey,显然这是加密过的,接下来就是分析这两个是怎么得到的了。

如何找到加密函数

我们回到这个界面,看 Initiator 这一列。

initiate 是初始化的意思,所以这个里面的 js 文件应该就是请求这个 url 前,要执行的 js 文件。因此,我们就去这里面找。我们把鼠标放到对应的 initiator 文件上,可以看到显示出来了一堆命令,如下图所示(我就截了一部分):

观察右边的 js 文件,可以发现大概就四五个文件,我们可以挨个去里面寻找,不过我们可以发现 core 开头的那个文件出现的频率很高,就可以首先去这里面寻找。

进入到这里面之后,我们按 ctrl+F,搜索 encSeckey,可以发现有结果!

可以发现这两个数据储存在 bKC6w 内,而 bKC6w 是由 window.asrsea 函数产生的,好了,这时我们已经基本上找到这两个参数是怎么加密出来的了。

var bKC6w = window.asrsea(JSON.stringify(i8a), bvh7a(["流泪", "强"]), bvh7a(Re1x.md), bvh7a(["爱心", "女孩", "惊恐", "大笑"]));

我们可以将这个文件导出,方便我们后续调整。我们直接搜索这个函数。可以发现,这个函数有四个参数,而且有两个参数看着很奇怪。为了先搞清楚这些参数是什么,我们可以先把这些参数对应的值给打印出来。那么这个要怎么实现呢?这就可以使用 Fiddler 了(注:这个也可以使用网站的调试工具,打断点,看输出,但是目前我不会)。

asrsea 函数解析

我们首先在这行代码上添加几行输出代码,即:

window.console.info(i8a);
window.console.info(bvh7a(["流泪", "强"]));
window.console.info(bvh7a(Re1x.md));
window.console.info(bvh7a(["爱心", "女孩", "惊恐", "大笑"]));

即:

那么要如何让网站运行我们改好的这个 js 文件呢?在 Fiddler 中,有一个功能,叫 AutoResponder。它的一个作用就是,当检测到你的网站运行了某个文件之后,它可以按照你的设置,替换成另一个文件代替它执行。比如我添加一个规则,原地址为 " https://www.baidu.com" ,替换的地址为:" https://www.corrain.top" ,那么当我在浏览器中输入百度的官网后,就可以发现打开的却是我的网站,感兴趣的可以试一下。说回这个,我们进行下面的设置:

上面的地址就是那个 core 文件所在的地址,我们可以直接在浏览器中右键这个文件名,然后就可以看到 copy link address 的选项,直接复制即可。下面的地址就是我们刚才改的那个 js 文件的路径。完成设置后,我们将抓包开关打开(点击软件左下角,点击后会显示“Capturing”),

然后我们回到刚才网易云音乐的界面,刷新界面,点击播放,然后去开发工具里,打开 console,滑到最下面,可以看到有很多输出:

显然,这四行,就分别是之前的那四个参数:

以这四个为一组,可以看到第一个参数是不断变化的,有 ids 开头的,有 logs 的,往上滑,还能看到 rid 的。这是因为网页中不同的信息,是需要访问不同的 url 的,比如音乐信息要访问刚才 v1 开头的,而比如评论,就需要访问另一个 url。而这些 url,使用的加密方法都是一样的,只不过输入有所不同。所以这第一个,就对应着不同的 url。而看上图里的这个,第一个显然是音乐的 id,第二个是音质等级,所以显然这个音乐 url 所对应的。

对于后三个参数,我们可以看到,对于所有的,都是相同的。如果切换歌曲,这三个参数仍然是相同的,因此,可以推断,bvh7a(["流泪", "强"]) 等这三个,就是对应着下面的那三个字符串,“流泪”这种词就是起误导作用的。我们可以直接查找 bvh7a 函数,来看看是怎么转化成字符串的,当然,如果你不想知道 bvh7a 函数是啥,那就直接将 window.asrsea 函数的后三个变量替换成这三个字符串即可。

这样,我们就知道了 window.asrsea 的四个变量分别是什么了,接下来我们就去看看 window.asrsea 函数的内部是什么吧。

window.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,

搜索之后,可以看到,这个函数就是 d 函数。观察 d 函数,首先是一个 a (16),通过观察 a 函数,可以发现就是产生了一个长度为 16 的随机字符串。然后是 b 函数,可以看到用到了 AES 的加密算法,经过了两次 AES 加密后,得到了 encText,这个便是我们所需要的 params。然后 encSecKey 是通过 c 函数得到的,而 c 函数里面用到了 RSA 加密。

好了,到现在,抛开这些加密算法的原理不谈,params 和 encSecKey 两个参数的得到方法已经很清晰了,一个使用 AES 加密方法,一个使用 RSA 加密方法。这两个加密方法的原理我不懂,也不是很感兴趣,感兴趣的可以去网上认真了解了解。在这里,我只介绍如何在不懂 js 的情况下,在 python 中实现这段代码

python 实现

代码是 js,编译器是 python,这时可以想到,python 中有没有库,支持执行 js 代码呢?结果,果然有!当我搜到这个结果的时候,内心是很欣喜的,因为如果将上面的这段 js 代码,转换成 python,是特别困难的,但是有了这个方法之后,就可以黑箱操作了。

实现这个功能的 python 库就是 pyexecjs,其使用方法也很简单,主要分为三步:

  1. 使用 open 函数,作用于 js 文件
  2. 调用 execjs.compile 函数,对文件编译
  3. 使用 call 函数,调用 js 文件中的函数,并传递参数

代码示例:

import execjs

fp = open("./rsa.js", encoding="utf-8").read()
js_res = execjs.compile(fp)
h = js_res.call("d", json.dumps(song_info), arg2, arg3, arg4)

这时候,我们便可以将于 d 函数相关的函数都找出来,放到一个 js 文件里。我这里为了省事,直接把从 RSAKeyPair 函数一直到 d 函数中的全部内容都复制了下来,然后调整一下格式,就 ok 了。如果你想省事,可以直接复制下面的这段。要注意,在第一行要引入依赖库 crypto-js.js,如果没有安装,就需要在 cmd 里,运行 npm install crypto-js。如果还有问题,可以去百度解决。在这里,我使用了绝对路径,如果你不知道安装到了哪里,可以用 everything 软件搜索一下。

var CryptoJS = require('C:\\Windows\\System32\\node_modules\\crypto-js\\crypto-js.js');

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

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
}

然后,我们就可以测试一下代码了。首先,我们可以将 js 文件里的 d 函数里的参数换成我们之前抓到的内容,即:

function d(d) {
    var h = {},
    i = a(16);
    return h.encText = b(d, "0CoJUm6Qyw8W8jud"),
    h.encText = b(h.encText, i),
    h.encSecKey = c(i, "010001", "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"),
    h
}

然后我们在主程序里直接执行即可。代码如下:

import execjs
import json
import requests

fp = open("./rsa.js", encoding="utf-8").read()
js_res = execjs.compile(fp)

song_info = {
    "ids": "[1958401118]",
    "level": "standard",
    "encodeType": "aac",
    "csrf_token": ""
}

h = js_res.call("d", json.dumps(song_info))
print(h)

url = "https://music.163.com/weapi/song/enhance/player/url/v1"
headers = {
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
        'referer': 'https://music.163.com'
}

data = {
        'params': h['encText'],
        'encSecKey': h['encSecKey']
}

res = requests.post(url, headers=headers, data=data)
print(res.text)

输出为:

大功告成!如果想要爬取其他歌曲,只需要改变 id 即可。如果想要爬取歌单、评论等等信息,只需要改变第一个参数,即上面的 song_info 即可,其形式可以通过 console 看到。重点的是思路,今天我搞清楚了这个过程之后,我是相信自己能够爬取挺多场景的。

最后,我还是得到无损音乐和 vip 歌曲,这个的核心便是 cookie。这个我也不知道要怎么实现,但幸运的是,我在网上查找资料的时候,看到了一个可以用来爬取 vip 音乐和无损音乐的 cookie,具体原理我也不知道。但是为了安全起见,我就不放到文章里了。

ending~收获慢慢的一天。提前祝大家假期快乐!

参考文章

https://www.jianshu.com/p/bb9ed6ef41b6

有问题可以留言哦~ 觉得有帮助也可以投喂一下博主,感谢~
文章链接:https://www.corrain.top/get-music/
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明文章地址及作者

评论

  1. 雪宝儿
    7 月前
    2023-10-09 9:23:35

    博主~~求更~

    • 博主
      雪宝儿
      7 月前
      2023-10-09 9:46:08

      这周末就更~~

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
( ゜- ゜)つロ
_(:з」∠)_
(⌒▽⌒)
( ̄▽ ̄)
(=・ω・=)
(*°▽°*)八(*°▽°*)♪
✿ヽ(°▽°)ノ✿
(¦3【▓▓】
눈_눈
(ಡωಡ)
_(≧∇≦」∠)_
━━━∑(゚□゚*川━
(`・ω・´)
( ̄3 ̄)
✧(≖ ◡ ≖✿)
(・∀・)
(〜 ̄△ ̄)〜
→_→
(°∀°)ノ
╮( ̄▽ ̄)╭
( ´_ゝ`)
←_←
(;¬_¬)
(゚Д゚≡゚д゚)!?
( ´・・)ノ(._.`)
Σ(゚д゚;)
Σ(  ̄□ ̄||)<
(´;ω;`)
(/TДT)/
(^・ω・^)
(。・ω・。)
(● ̄(エ) ̄●)
ε=ε=(ノ≧∇≦)ノ
(´・_・`)
(-_-#)
( ̄へ ̄)
( ̄ε(# ̄) Σ
(╯°口°)╯(┴—┴
ヽ(`Д´)ノ
("▔□▔)/
(º﹃º )
(๑>؂<๑)
。゚(゚´Д`)゚。
(∂ω∂)
(┯_┯)
(・ω< )★
( ๑ˊ•̥▵•)੭₎₎
¥ㄟ(´・ᴗ・`)ノ¥
Σ_(꒪ཀ꒪」∠)_
٩(๛ ˘ ³˘)۶❤
(๑‾᷅^‾᷅๑)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
小黄脸
热词系列一
tv_小电视
上一篇
下一篇