【爬虫实战】使用Python和JS两种方式逆向网X云音乐接口并下载歌曲

前言

目前绝大部分网站只提供在线听歌的功能,几乎都无法下载,就算能下载也不能直接播放或者需要使用专有的播放器。那么如何获取歌曲的源文件呢?接下来以网易云为例下载某一首歌。本文使用两种JS逆向的方式,便于不同场景的学习和使用。

一、目标分析

在网易云音乐的首页可以搜索也可以直接点击播放,然后就会进入一个歌曲的单独页面。

以当前歌曲为例,可以看到当点击播放的时候会出现很多请求的接口,接下来一个接口一个接口的看,直到发现和歌曲下载有关的那一个为止。

最后定位到了/weapi/song/enhance/player/url/v1?csrf_token=这个接口。并且接口返回值里有一个URL为http://m801.music.126.net/20231116170739/11fc00fb99b8cc89ffd1e1bad7201477/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/31248364361/113a/77e3/a0a5/c2a90b87a91199babf8ea42f08a0d8b8.m4a,这是一个m4a文件。其实这就是一个音频文件,类似于之前常见的mp3格式等等。

M4A 是一种音频文件格式,它是 MPEG-4 Part 14 标准的一部分。M4A 文件通常包含以 Advanced Audio Coding (AAC) 编码的音频数据,但它也可以包含其他音频编码类型。这个格式的名称 “M4A” 是由 Apple 公司引入的,用于标识其在 iTunes 中使用的音频文件。

M4A 文件通常具有较高的音频质量,并且相对于其他一些音频格式,文件大小较小。它是一种无损音频格式,可以提供高质量的音频体验。由于其高效的压缩算法,M4A 文件经常用于存储音乐、音效和其他类型的音频内容。

尽管 M4A 格式在音质和文件大小上有一些优势,但它可能不如其他格式如 MP3 在广泛的多平台兼容性上好。不过,许多现代设备和音频播放器都支持 M4A 格式,特别是在苹果的生态系统中,它是一种常见的音频文件格式。

接下来可以请求一下这个链接的文件是否能播放,如果能播放就是我们的目标。

的确这就是我们要的文件。

至此,已经找到了最重要的目标,获取m4a文件。

二、接口分析

前文中找到了获取文件的接口,那么这个接口是如何工作的呢。

1. 查看请求头

可以看到并没有加密的参数,唯一有可能影响的就是Cookie,但是我们可以试试先不带Cookie,其他的UA和Referer最好先带上。

2. 查看参数

参数有两个,都是加密后的,所以重点就是这个两个参数。

三、 获取加密方式

1. 定位功能函数

定位加密函数有多种方式,这里以全局搜索为例,可以搜索encSecKey这样的特殊字段,大概率能搜到。其实这个关键字还是搜索到了很多条记录,但是可以都打上断点用排除的方式进行判断,最终定位到了core_xxx.js这个文件。

可以看到有两个字段parms和encSecKey,都是密文,但是不确定是不是我们要的结果。所以可以等这次执行完和浏览器上的值对比一下。需要注意前面有个x4b的值是一个URL,得确保这个URL是刚才查到的那个接口,否则就走错路了。

等请求跑完,对比后确认这个就是我们要找的加密函数。所以关键点就在下面的代码中:

1
2
var bVg5l = window.asrsea(JSON.stringify(i3x), bsk0x(["流泪", "强"]), bsk0x(WH9y.md), bsk0x(["爱心", "女孩", "惊恐", "大笑"]));

可以在控制台中依次看一下这几个参数的输出是什么

函数中的第一个参数是由歌曲id拼接的,其他的参数有的是固定值,有的不确定,可以检查一下。

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

在代码中搜索可以看到第三个参数确定也是固定的。

接下来就重点关注window.asrsea()这个函数了,然后定位到这个d函数

d函数也调用了当前作用域下的其他几个函数。接下来就可以使用JS的方式进行逆向了。

2. 方式1、逐个补函数

主要思想就是用到哪个函数就复制哪个函数,优点就是减少了代码量和减少思考;缺点就是得一个个补,耗时较长。

首先补d函数所在的作用域里的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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

由于需要将d函数输出,所以可以简单改造一下,return一下。

1
2
3
4
5
6
7
8
9
10
11
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
}

const s1 = '{"ids":"[2095868608]","level":"standard","encodeType":"aac","csrf_token":""}'
const s2 = "010001"
const s3 = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
const s4 = "0CoJUm6Qyw8W8jud"

console.log(d(s1, s2, s3, s4))

接下来就运行当前js文件,报错缺哪个函数就补哪个函数。最后代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
const CryptoJS = require('crypto-js');

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 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 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 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 biHighIndex(a) {
for (var b = a.digits.length - 1; b > 0 && 0 == a.digits[b];) --b;
return b
}

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 biCopy(a) {
var b = new BigInt(!0);
return b.digits = a.digits.slice(0), b.isNeg = a.isNeg, b
}

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 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 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
}

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 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 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 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 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 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 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 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 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
}

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 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 biToHex(a) {
var d, b = "";
for (biHighIndex(a), d = biHighIndex(a); d > -1; --d) b += digitToHex(a.digits[d]);
return b
}

function digitToHex(a) {
var b = 15, c = "";
for (i = 0; 4 > i; ++i) c += hexToChar[a & b], a >>>= 4;
return reverseStr(c)
}

function reverseStr(a) {
var c, b = "";
for (c = a.length - 1; c > -1; --c) b += a.charAt(c);
return b
}

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
}

const s1 = '{"ids":"[2095868608]","level":"standard","encodeType":"aac","csrf_token":""}'
const s2 = "010001"
const s3 = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
const s4 = "0CoJUm6Qyw8W8jud"

console.log(d(s1, s2, s3, s4))

输出结果:

1
2
3
4
5
{
encText: '2r5EJr1vZXnDnGpLKshUlqM8EA0pnIeVo8iVD0OmWDXz7eY2u5UND8iokynDdOiedX7OiYOKLCaaTKtA1v26dfPtU0cmc5b0U97R4UgkcROf1zylysKZC+26YLxD8h6KQrhVVksEoWZQ/wOKVgZr5w==',
encSecKey: '5054917c52ea0cc59563831d6d4691da2b323e11509496023d612e8787d9b472cec3e0d4c74e2731f8c97f224b9e700560b3e745c81d1576b7e9f9eb796606bc9475a812f2de941fc34baccf7e854153ebd377486b718ab8c22cac8ca88e165a2a6e463804b0cec3a0b3e60e9939d0281f2cc48815489103b1ea00f5d2bfade2'
}

虽然结果出来了,但是和刚才获取的不一样,这两个值是否有效呢?其实每次获取的都不会一样的,因为a函数中有个random获取的获取的是随机值。至于是否有效,可以将数据请求一下就知道是否可以了。

3. 方式2、根据作用域补函数

首先补充几个知识点。

匿名函数

JavaScript 中的匿名函数是指没有名字的函数,通常被用作一次性的、临时的函数。匿名函数有两种主要的形式:函数表达式和箭头函数。

函数表达式

函数表达式是指将函数赋值给一个变量或者常量,而不是声明一个具有名称的函数。这个函数可以是匿名的。

1
2
3
4
5
6
7
8
// 匿名函数表达式
const add = function(x, y) {
return x + y;
};

// 使用匿名函数
console.log(add(2, 3)); // 输出 5

箭头函数

箭头函数是 ES6 引入的一种更简洁的函数写法。它可以更方便地定义匿名函数。

1
2
3
4
5
6
7
8
9
10
11
12
// 匿名箭头函数
const add = (x, y) => {
return x + y;
};

// 如果函数体只有一行,可以省略大括号和 return
const multiply = (x, y) => x * y;

// 使用匿名箭头函数
console.log(add(2, 3)); // 输出 5
console.log(multiply(2, 3)); // 输出 6

匿名函数通常在需要临时处理某些逻辑的情况下使用,或者作为函数的参数传递。它们在创建回调函数、事件处理函数等场景中非常常见。由于匿名函数没有名字,因此不能通过名字调用,必须通过变量或者直接在表达式中使用。

在 JavaScript 中,每个函数都创建了一个新的作用域。函数内部的变量在函数外部是不可访问的,这被称为函数作用域。而匿名函数也遵循这一规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 全局作用域
var globalVar = 'I am global';

function exampleFunction() {
// 函数作用域
var localVar = 'I am local';
console.log(globalVar); // 可以访问全局变量
console.log(localVar); // 可以访问本地变量
}

exampleFunction();
// console.log(localVar); // 错误,localVar 不在此作用域可见

匿名函数通常与闭包密切相关。闭包是指一个函数能够记住并访问它被创建时所处的作用域,即使该函数在不同的作用域被调用。匿名函数经常被用作闭包。

1
2
3
4
5
6
7
8
9
10
11
function outerFunction() {
var outerVar = 'I am outer';

// 返回一个匿名函数,形成闭包
return function() {
console.log(outerVar); // 访问 outerFunction 的局部变量
};
}

var closureFunction = outerFunction();
closureFunction(); // 输出 'I am outer'

在上述例子中,closureFunction 是一个匿名函数,它被赋值为 outerFunction 的返回值。由于匿名函数被返回并在外部作用域执行,它形成了一个闭包,可以访问外部函数 outerFunction 的局部变量 outerVar

匿名函数在闭包中经常用于保持某个函数内的状态,并在以后的调用中保留这个状态。

总的来说,匿名函数和作用域之间的关系表现在匿名函数遵循函数作用域规则,并且匿名函数常常用于创建闭包,以保留函数创建时的作用域。

整体思路

  1. 首先复制全局变量
  2. 然后复制重点功能函数

那么具体怎么做呢?举个例子,刚才d函数嵌套使用了c函数,c函数又使用了RSAKeyPair函数,但是RSAKeyPair这个函数明显和c不在一个作用域里,所以就要把这个函数所在的作用域的代码全部复制过来。

JavaScript 的顶级作用域是指代码中不包含在任何函数内的部分,即全局作用域。在浏览器环境中,顶级作用域通常是整个页面。在 Node.js 等环境中,顶级作用域可以是整个 Node.js 模块。

在顶级作用域中声明的变量和函数具有全局作用域,可以被程序中的任何其他作用域访问。这意味着在全局范围内声明的变量在整个程序中都是可见的。

所以像RSAKeyPair这样类似的代码可以全部复制过来,如果运行报错就接着补。补完全局变量后就再补具体的功能函数。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
const CryptoJS = require('crypto-js');
// 全局变量
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 () {
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
}

}();


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
}

ret = d("{\"ids\":\"[2095868608]\",\"level\":\"standard\",\"encodeType\":\"aac\",\"csrf_token\":\"\"}", "010001", "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7", "0CoJUm6Qyw8W8jud")
console.log(ret)

其实代码并不重要,重要的是根据作用域补函数的方法。如果掌握了这个方法,整体时间绝对会比逐个补函数的方式快上N倍。就算代码量大个几倍又如何,没人在乎你用多少代码来实现的,只关心是否实现了功能。毕竟爬虫的逆向不能完全和企业的业务功能做比较。

4. Python代码调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
@time:2023/11/16
@file:网易云音乐.py
@author:medivh
@mail:admin@econow.cn
@IDE:PyCharm
"""
import json
import execjs
import requests
from faker import Faker


def start(song_id):
headers = {
"User-Agent": Faker().user_agent()
}
with open('网易云音乐_作用域补环境.js') as f:
JSCode = f.read()
js = execjs.compile(JSCode)

song_info = {"ids": f"[{song_id}]", "level": "standard", "encodeType": "aac", "csrf_token": ""}
data = (json.dumps(song_info), "010001",
"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7",
"0CoJUm6Qyw8W8jud")
# data 中后面的三个值都是固定的,如果报错有可能是后面的三个值更新了,重新调试即可
jiamiData = js.call("jiami", *data)
print(jiamiData)
res = requests.post('https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=', headers=headers, data={
"params": jiamiData.get('encText'),
"encSecKey": jiamiData.get('encSecKey')
})
m4a_link = res.json().get('data')[0].get('url')
print(m4a_link)


if __name__ == '__main__':
song_id = 2095868608
start(song_id)

结果输出:

1
2
3
{'encText': 'wAcRjHaYgKYDoxymMZeo0ZOQLImyk+ZF8dfErBBqgkLeSnG5dQB8XcC1YWzeJ0TwlK2RskEJAXlNG9lpUVWAnifu7YN7dXI/X2YQ+68aKLsxt70vgV/+w2HxtIxZyxtTIajiNDg5CtOyFzz2xOB9dg/n1OX3AaMulaZU+xnaAF165V0SshyaeCgxj0TJKSrk', 'encSecKey': '0bd0c2cb8a35e2f5cc78bfb7a7d5502e70f304233d92de12bff5dddc6265ec8f0eaf9bb72803455fa8864a8ee60ad744b0d31ffef80f9bf0d2d13d906fe325ca30db47299176a045e1e16bd550b05098ba6472debab4b8a323aca5ca1a15ed47232d1a41e0203d9515ab738dfbcfab0eb32f0446d0c5ba4ce900ed0370c4926f'}
http://m801.music.126.net/20231116181635/7120abb587b188af0e77fa3b783478e8/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/31248364361/113a/77e3/a0a5/c2a90b87a91199babf8ea42f08a0d8b8.m4a

python调用的代码就很简单了,唯一的变量就是歌曲ID。其实歌曲ID这个获取也很假单,比如有个歌单,是不是就可以批量获取ID了。举个例子,就算在接口中没有找到合适的数据,但是也可以用xpath进行解析啊。方法有很多,有效就行。

四、总结回顾

通过两种JS补函数的方式实现了当前功能的逆向。方法有多种,只要能实现即可。简单的功能简单做,复杂的功能复杂做。

免责声明

  • 教育和研究用途:本文章提供的信息和示例代码仅供教育和研究用途。它们的目的是帮助读者了解爬虫技术的原理和应用。
  • 合法合规性:请注意,网络爬虫可能会侵犯网站的服务条款或法律法规。在实际应用中,你必须确保你的爬虫活动合法、合规,并遵守所有相关法律。
  • 责任限制:作者对于读者使用文章中提供的信息和代码所导致的任何问题或法律纠纷概不负责。读者应自行承担风险并谨慎操作。
  • 合理使用:请在使用网络爬虫时保持谨慎和礼貌。不要对目标网站造成不必要的干扰或侵害他人利益。请在遵守法律的前提下使用爬虫技术。
  • 变动和更新:作者保留随时更改文章内容的权利,以反映新的法规、技术和最佳实践。
  • 资源和参考文献:本文章中的示例代码和信息可能依赖于第三方资源,作者会尽力提供相关参考文献和资源链接。作者不对这些资源的可用性或准确性负责。
  • 协商:如果您有任何关于本文内容或责任声明的疑虑或疑问,请在使用之前与专业法律顾问协商。