网易云音乐关键字搜索并生成下载url
一、获取搜索的歌单信息
通过在网易云音乐进行关键字搜索,并通过F12检测,发现网页给网站https://music.163.com/weapi/cloudsearch/get/web?csrf_token=发送了一条post请求(经测试发现csrf_token=后的数值与登录账号有关),post的data中包含了params和encSecKey两条数据(因此无需再使用selenium来提取iframe内的数据):
通过检查其preview我们可以发现,其中包含了我们所需要搜索歌曲的信息,如下所示:
由于post的data参数为params及encSecKey,因此为了获取preview的json数据,需要对params及encSecKey进行解密,而解密需要获取解密的key,因此,在开发者工具中搜索encSecKey,如图所示:
然后点开core开头的文件,通过阅读可以发现加密函数:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MXZ3bLOQ-1597906857705)(C:\Users\wangyu\Desktop\博客\网易云搜索5.png)]
var bVZ7S = window.asrsea(JSON.stringify(i7b), bqN2x(["流泪", "强"]), bqN2x(Wx5C.md), bqN2x(["爱心", "女孩", "惊恐", "大笑"]));
i7b为加密前的数据,[“流泪”, “强”]查函数上方的表可知为“010001”,Wx5C.md根据上方的表可知为Wx5C.md = [“色”, “流感”, “这边”, “弱”, “嘴唇”, “亲”, “开心”, “呲牙”, “憨笑”, “猫”, “皱眉”, “幽灵”, “蛋糕”, “发怒”, “大哭”, “兔子”, “星星”, “钟情”, “牵手”, “公鸡”, “爱意”, “禁止”, “狗”, “亲亲”, “叉”, “礼物”, “晕”, “呆”, “生病”, “钻石”, “拜”, “怒”, “示爱”, “汗”, “小鸡”, “痛苦”, “撇嘴”, “惶恐”, “口罩”, “吐舌”, “心碎”, “生气”, “可爱”, “鬼脸”, “跳舞”, “男孩”, “奸笑”, “猪”, “圈”, “便便”, “外星”, “圣诞”],查表拼接为"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7",[“爱心”, “女孩”, “惊恐”, “大笑”]同样查表可知为“0CoJUm6Qyw8W8jud”
而函数window.asrsea()根据上方function可知为d函数:
!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
}();
因此我们可以通过在d函数处设置断点查看post发送的原始数据是什么:
具体数据如下:d: “{“hlpretag”:”<span class=“s-fc7”>",“hlposttag”:"",“s”:“浅唱”,“type”:“1”,“offset”:“0”,“total”:“true”,“limit”:“30”,“csrf_token”:""}"
因此,搜索不同的信息时仅需修改“s”对应的数值即可,且整个d的数据格式为str,我们仅需将其整体打包为str即可。
二、实现加密函数
-
function a(a):
函数a的主要功能是为了实现从字符串b中随机采样a个不同字符,由于这里调用时默认是16位,因此可以通过以下python程序简单实现:
def get_i(): txt = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' return ''.join(random.sample(txt, 16))
-
function b(a, b):
函数b的主要功能对post的data以及加密的key进行encode,然后进行偏移量为iv的加密。
def AES_encrypt(text, key, iv): bs = AES.block_size pad2 = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs) encryptor = AES.new(to_16(key), AES.MODE_CBC,to_16(iv)) encrypt_aes = encryptor.encrypt(str.encode(pad2(text))) encrypt_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8') return encrypt_text
-
function c(a, b, c):
函数c的主要功能为了生成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').zfill(256)
params参数是通过两次b函数得到。
三、生成下载url
类似于获取歌单信息,首先需要找到post请求的网站地址,依然是通过F12检查,通过删选信息,可以发现这条post信息中包含了歌曲下载的url。
post所发送的data依然是params和encSecKey,post的目标网站为https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=,同理我们可以设置断点来检查所post的加密前的data,如图:
data的数据格式:{“ids”:str([id]),“level”:“standard”,“encodeType”:“aac”, “csrf_token”: “”},其中id表示歌曲的id号,level是音乐品质,经我测试标准为standard,较高音质为higher,极高音质没测试出关键词,无损音质关键词为lossless。
另外还发现一个有趣的事情:
对vip歌曲,普通用户是无法发送post请求,因此需要使用vip账号的cookies登录才可以下载vip歌曲。所以说还是支持正版吧。
四、完整代码
import urllib.request,os,json
import requests,random
import base64,codecs