关于网易云音乐api的信息在这里

加密代码“参考”此篇


网易云音乐的api是众多非开放api中为数不多需要加密来调取的,参考下图:

image

通过分析(过程见参考文章,在这里就不班门弄斧了)以及其命名可以得知,params对应的是加密的负载,encSecKey对应的是加密的AES第二次加密的key(其实就是encoded Secret Key的缩写),从其特征可以猜测是十六进制值。

但是在运行参考资料的源代码时会有各种各样奇奇怪怪的报错,有的是数据格式,以及参考资料中RSA加密的内容也是让人极其mengbi(当然也可能是我太菜了看不懂),于是我去找了关于用modulus和publicKey进行RSA加密的资料

在经过无数次报错后,最终代码修改如下(可以正常运行,环境:python 3.10):(感想:注意数据类型intbytes的转换!)

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
import base64
import json
import random
import requests
import math

from Crypto.Cipher import AES
from string import ascii_letters, digits

_charset = ascii_letters + digits


def rand_char(num=16):
return ''.join(random.choice(_charset) for _ in range(num))


def aes_encrypt(msg, key, iv=b'0102030405060708'):
def padded(msg):
pad = 16 - len(msg) % 16
return msg + pad * chr(pad)

cryptor = AES.new(key=key, IV=iv, mode=AES.MODE_CBC)
msg = padded(msg).encode('utf-8')
text = cryptor.encrypt(msg)
text = base64.b64encode(text)
return text


def gen_params(d, i):
text = aes_encrypt(d, b'0CoJUm6Qyw8W8jud')
text = aes_encrypt(text.decode(), i)
return text


def rsa_encrypt(msg):
e = '010001'
m = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
m = int.from_bytes(bytearray.fromhex(m), byteorder='big')
e = int.from_bytes(bytearray.fromhex(e), byteorder='big')
msg = msg.decode()[::-1].encode()
msg = int.from_bytes(msg, byteorder='big')
crypted_nr = pow(msg, e, m)
keylength = math.ceil(m.bit_length() / 8)
text = crypted_nr.to_bytes(keylength, byteorder='big')
return str(text.hex())


def encrypt(query):
query = json.dumps(query)
rand_i = rand_char(16).encode()
params = gen_params(query, rand_i).decode()
enc_sec_key = rsa_encrypt(rand_i)
data = {
'params': params,
'encSecKey': enc_sec_key
}
return data

if __name__ == '__main__':
music_id = '483671599'
url = 'http://music.163.com/weapi/v1/resource/comments/R_SO_4_{}?csrf_token='.format(music_id)
headers = {
'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded',
'Host': 'music.163.com',
'Origin': 'http://music.163.com',
'Referer': 'http://music.163.com/',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
}
query = {
'rid': 'R_SO_4_{}'.format(music_id),
'offset': '0',
'total': 'true', # 第一页时为true,其他页为false
'limit': '20',
'csrf_token': ''
}
data = encrypt(query)

r = requests.post(url, data=data, headers=headers)
for item in r.json()['comments']:
print(item['content'])

但这只是评论的获取,和歌单并没有什么实际联系。

由于不知道网易云音乐在哪里会调用访问歌单内容的api,所以此处选用监视listen1的抓取网易云歌单的过程来实现(不要问为什么不去看最开始的网易云音乐api列表,问就是才知道有这玩意)。

通过监听网络活动,一共有两次请求:

image

image

可以在第一次请求的返回值中找到歌单音乐id的完整列表:

image

照猫画虎使用断点,得到两次初始负载分别为:

1
2
3
"{"id":"7598486484","offset":0,"total":true,"limit":1000,"n":1000,"csrf_token":""}"

"{"c":"[{\"id\":524259915},{\"id\":1383580248},{\"id\":529659114},{\"id\":1296797860},{\"id\":1873009072},{\"id\":1889103238},{\"id\":574919767},{\"id\":1345485069},{\"id\":34338097},{\"id\":109998},{\"id\":481001412},{\"id\":447281026},{\"id\":442495638},{\"id\":33937527},{\"id\":1873672},{\"id\":565841089},{\"id\":1696373},{\"id\":39227624},{\"id\":424495344},{\"id\":687506},{\"id\":4224657},{\"id\":17867241},{\"id\":496869422},{\"id\":536622304},{\"id\":5308028},{\"id\":586299},{\"id\":1847030559},{\"id\":31654478},{\"id\":451126971},{\"id\":38592976},{\"id\":28560087},{\"id\":3932159},{\"id\":190361},{\"id\":410518453},{\"id\":468176711},{\"id\":1398909242},{\"id\":715785},{\"id\":1396223549},{\"id\":493981},{\"id\":28996630},{\"id\":857896},{\"id\":493903},{\"id\":29803287},{\"id\":1422474648},{\"id\":493911},{\"id\":427606780},{\"id\":39224659},{\"id\":190354},{\"id\":483671599},{\"id\":28907016},{\"id\":461544312},{\"id\":1299293129},{\"id\":569200213},{\"id\":29436904},{\"id\":541131},{\"id\":1430583016},{\"id\":480353},{\"id\":1325388734},{\"id\":1325390370},{\"id\":39224648},{\"id\":1497261752},{\"id\":27336930}]","ids":"[524259915,1383580248,529659114,1296797860,1873009072,1889103238,574919767,1345485069,34338097,109998,481001412,447281026,442495638,33937527,1873672,565841089,1696373,39227624,424495344,687506,4224657,17867241,496869422,536622304,5308028,586299,1847030559,31654478,451126971,38592976,28560087,3932159,190361,410518453,468176711,1398909242,715785,1396223549,493981,28996630,857896,493903,29803287,1422474648,493911,427606780,39224659,190354,483671599,28907016,461544312,1299293129,569200213,29436904,541131,1430583016,480353,1325388734,1325390370,39224648,1497261752,27336930]"}"

(数据格式均为str,即json)

提取一般形式为:

1
2
3
4
5
{"id":xxxx,"offset":0,"total":true,"limit":xxx,"n":xxx,"csrf_token":""}
//https://music.163.com/weapi/v3/playlist/detail

{"c":"[{\"id\":xxxxxx},...]","ids":"[xxxxxx,...]"}
//https://music.163.com/weapi/v3/song/detail

照葫芦画瓢修改以上代码__main__部分:

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
if __name__ == '__main__':
playlist_id = input('playlist id: ')
url1 = 'https://music.163.com/weapi/v3/playlist/detail'
url2 = 'https://music.163.com/weapi/v3/song/detail'
headers = {
'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded',
'Host': 'music.163.com',
'Origin': 'http://music.163.com',
'Referer': 'http://music.163.com/',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
}
query1 = {
'id': playlist_id,
'offset': '0',
'total': 'true', # 第一页时为true,其他页为false
'limit': '1000',
'n': '1000',
'csrf_token': ''
}
data1 = encrypt(query1)
r1 = requests.post(url=url1, data=data1, headers=headers).text
r1 = json.loads(r1)
if r1['code'] == 200:
data = r1['playlist']['trackIds']
#print(r1)
_id,_ids = [],[]
for i in data:
_id.append(json.dumps({'id': i['id']}))
_ids.append(str(i['id']))
data2=json.dumps({"c":"["+','.join(_id)+"]","ids":"["+','.join(_ids)+"]"})
data2 = encrypt(data2)
r2 = requests.post(url=url2, data=data2, headers=headers)
print(r2)

(挖个坑:虽然响应是200,但没有返回任何数据:image

(突然想起来有可能是在网易云音乐app中调用,但是因为ssl证书错误而从未成功抓取过,希望有大佬可以帮忙解决。)