本文转自:https://blog.csdn.net/qq_42730750/article/details/108415551
前言
"text-align: center">
"text-align: left">"text-align: center">
1. 请求分析
"htmlcode">
var bVZ8R = window.asrsea(JSON.stringify(i0x), bqN0x(["流泪", "强"]), bqN0x(Wx5C.md), bqN0x(["爱心", "女孩", "惊恐", "大笑"])); e0x.data = j0x.cs1x({ params: bVZ8R.encText, encSecKey: bVZ8R.encSecKey })
"htmlcode">
>JSON.stringify(i0x) <"{"csrf_token":""}" >bqN0x(["流泪", "强"]) <"010001" >bqN0x(Wx5C.md) <"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7" >bqN0x(["爱心", "女孩", "惊恐", "大笑"]) <"0CoJUm6Qyw8W8jud"
"{"csrf_token":""}"、"010001"
、"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
、"0CoJUm6Qyw8W8jud"
,如果没有猜错的话第三个参数是十六进制的形式,其实也就是如此。通过几次刷新,这几个值不变。
3. 加密分析
"htmlcode">
d = "{\"csrf_token\":\"\"}" e = "010001" f = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7" g = "0CoJUm6Qyw8W8jud"
"htmlcode">
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 }
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"中随机生成长度为a
的字符串。
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() }
"htmlcode">
function c(a, b, c) { var d, e; return setMaxDigits(131), d = new RSAKeyPair(b,"",c), e = encryptedString(d, a) }
"htmlcode">
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 }
"htmlcode">
class EncryptText: def __init__(self): self.character = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' self.iv = '0102030405060708' self.public_key = '010001' self.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b' '5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417' '629ec4ee341f56135fccf695280104e0312ecbda92557c93' '870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b' '424d813cfe4875d3e82047b97ddef52741d546b8e289dc69' '35b3ece0462db0a22b8e7' self.nonce = '0CoJUm6Qyw8W8jud'
"mEXyqHtNW5dxT5IK"。
"htmlcode">
def create16RandomBytes(self): """ # 产生16位随机字符, 对应函数a :return: """ generated_string = get_random_bytes(16) return generated_string
"htmlcode">
def create16RandomBytes(self): """ # 产生16位随机字符, 对应函数a :return: """ generate_string = random.sample(self.character, 16) generated_string = ''.join(generate_string) return generated_string
"text-align: center">"text-align: left">程序执行到函数b
处,传入的参数d
和g
的值我们已经知道,看一下加密后的结果:
"eHhjXckqrtZkqcwCalCMx0QuU6Lj9L7Wxouw1iMCnB4=",下面来用官方的API来模拟一下:
def AESEncrypt(self, clear_text, key): """ AES加密, 对应函数b :param clear_text: 需要加密的数据 :return: """ # 数据填充 clear_text = pad(data_to_pad=clear_text.encode(), block_size=AES.block_size) key = key.encode() iv = self.iv.encode() aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv) cipher_text = aes.encrypt(plaintext=clear_text) # 字节串转为字符串 cipher_texts = base64.b64encode(cipher_text).decode() return cipher_texts
"{"csrf_token":""}"传入到该函数中,看一下模拟的结果:
"JWuA4mdNsTdrLdDkD9UWs8ShPCZNK0n4BLpdQEDSAaD/kFKKih8XQp8W/mICYPlN",然后对比一下自己模拟的结果:
"d58e873a2e908c0599b497456f1842d1734e1d17e834a221ed84d828b06b149d0bac2ddd449e38b7e5e9ce53dcb1aa43a241742a2b273434b67825743fbca6371aa143a4460477704ba3fd33b517619386daf8da4c7fe8d67a604ea0e461aedee5ae2698400a6c7340ab250c97622aa221d871b7352d81ea09262978facf5480"
"htmlcode">
def RSAEncrypt(self, session_key): """ RSA加密的结果每次都不一样 :param session_key: :return: """ # n和e构成公钥 # (n, e) # key = RSA.RsaKey(n=int(self.modulus, 16), e=int(self.public_key, 16)) key = RSA.construct(rsa_components=(int(self.modulus, 16), int(self.public_key, 16))) public_key = key.publickey() rsa = PKCS1_OAEP.new(key=public_key) cipher_text = rsa.encrypt(message=session_key).hex() return cipher_text
"htmlcode">
def RSAEncrypt(self, i, e, n): """ RSA加密, 对应函数c :param i: :return: """ # num = pow(x, y) % z # 加密C=M^e mod n num = pow(int(i[::-1].encode().hex(), 16), int(e, 16), int(n, 16)) result = format(num, 'x') return result
"{"ids":"[35440198]","level":"standard","encodeType":"aac","csrf_token":""}",最后我们看一下最终的结果:
"text-align: center">"text-align: left">歌曲的文件对应的 u r l url url 我们已经找到,根据结果可知,它是一个字符串,准确来说是个json
格式的,而且里面只有一条数据是我们需要的,所以直接提取:
"text-align: left">然后再去用代码请求该 u r l url url,将请求到的内容以二进制形式进行保存,文件名后缀为.mp3
。
5. 获取ID
"{"hlpretag":"<span class=\"s-fc7\">","hlposttag":"</span>","s":"本兮","type":"1","offset":"0","total":"true","limit":"30","csrf_token":""}"
"text-align: center">"text-align: left">这里简单分析一下参数d
,关键字s
表示你要搜索的内容,关键字type
表示搜索的类型(见下面的表格),如果需要下载其他歌手的歌曲,只需要将参数d
中的关键字s
的值改一下即可,为了方便,可以用input()
方法传递这个值。
type
含义
1
单曲
100
歌手
10
专辑
1014
视频
1006
歌词
1000
歌单
1009
主播电台
1002
用户
6. 代码框架
# -*- coding: utf-8 -*- # @Time : 2020/9/2 11:23 # @Author : XiaYouRan # @Email : youran.xia@foxmail.com # @File : wangyiyun_music2.py # @Software: PyCharm import requests from Crypto.Cipher import AES, PKCS1_OAEP from Crypto.Util.Padding import pad from Crypto.PublicKey import RSA from Crypto.Random import get_random_bytes import random import base64 import json import os class EncryptText: def __init__(self): self.character = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' self.iv = '0102030405060708' self.public_key = '010001' self.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b' '5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417' '629ec4ee341f56135fccf695280104e0312ecbda92557c93' '870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b' '424d813cfe4875d3e82047b97ddef52741d546b8e289dc69' '35b3ece0462db0a22b8e7' self.nonce = '0CoJUm6Qyw8W8jud' def create16RandomBytes(self): def AESEncrypt(self, clear_text, key): def RSAEncrypt(self, i, e, n): def resultEncrypt(self, input_text): """ 对应函数d :param input_text: :return: """ i = self.create16RandomBytes() encText = self.AESEncrypt(input_text, self.nonce) encText = self.AESEncrypt(encText, i) encSecKey = self.RSAEncrypt(i, self.public_key, self.modulus) from_data = { 'params': encText, 'encSecKey': encSecKey } return from_data class WangYiYunMusic(object): def __init__(self): self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'} def get_html(self, url, method='GET', from_data=None): try: if method == 'GET': response = requests.get(url, headers=self.headers) else: response = requests.post(url, from_data, headers=self.headers) response.raise_for_status() response.encoding = 'utf-8' return response.text except Exception as err: print(err) return '请求异常' def parse_text(self, text): ids_list = json.loads(text)['result']['songs'] count = 0 info_list = [] print('{:*^80}'.format('搜索结果如下')) print('{0:{5}<5}{1:{5}<20}{2:{5}<10}{3:{5}<10}{4:{5}<20}'.format('序号', '歌名', '歌手', '时长(s)', '专辑', chr(12288))) print('{:-^84}'.format('-')) for id_info in ids_list: song_name = id_info['name'] id = id_info['id'] time = id_info['dt'] // 1000 album_name = id_info['al']['name'] picture_url = id_info['al']['picUrl'] singer = id_info['ar'][0]['name'] info_list.append([id, song_name, singer]) print('{0:{5}<5}{1:{5}<20}{2:{5}<10}{3:{5}<10}{4:{5}<20}'.format(count, song_name, singer, time, album_name, chr(12288))) count += 1 if count == 8: # 为了测试方便, 这里只显示了9条数据 break print('{:*^80}'.format('*')) return info_list def save_file(self, song_text, download_info): filepath = './download' if not os.path.exists(filepath): os.mkdir(filepath) filename = download_info[1] + '-' + download_info[2] music_url = json.loads(song_text)['data'][0]['url'] response = requests.get(music_url, headers=self.headers) with open(os.path.join(filepath, filename) + '.mp3', 'wb') as f: f.write(response.content) print("下载完毕!") if __name__ == '__main__': id_url = 'https://music.163.com/weapi/cloudsearch/get/web"hlpretag": "<span class=\"s-fc7\">", "hlposttag": "</span>", "s": input("请输入歌名或歌手: "), "type": "1", "offset": "0", "total": "true", "limit": "30", "csrf_token": "" } encrypt = EncryptText() id_from_data = encrypt.resultEncrypt(str(id_d)) wyy = WangYiYunMusic() id_text = wyy.get_html(id_url, method='POST', from_data=id_from_data) info_list = wyy.parse_text(id_text) while True: input_index = eval(input("请输入要下载歌曲的序号(-1退出): ")) if input_index == -1: break download_info = info_list[input_index] song_d = { "ids": str([download_info[0]]), "level": "standard", "encodeType": "aac", "csrf_token": "" } song_from_data = encrypt.resultEncrypt(str(song_d)) song_text = wyy.get_html(song_url, method='POST', from_data=song_from_data) wyy.save_file(song_text, download_info)
"在这里插入图片描述" src="/UploadFiles/2021-04-08/2020120410130464.png">
结束语
",“hlposttag”:"",“s”:"你要搜索的信息",“type”:"1",“offset”:“0”,“total”:“true”,“limit”:“30”,“csrf_token”:""}" https://music.xxx.com/weapi/cloudsearch/get/web",“level”:"standard",“encodeType”:“aac”,“csrf_token”:""}" https://music.xxx.com/weapi/song/enhance/player/url/v1",“lv”:-1,“tv”:-1,“csrf_token”:""}" https://music.xxx.com/weapi/song/lyric",“offset”:“0”,“orderType”:“1”,“csrf_token”:""}" https://music.xxx.com/weapi/comment/resource/comments/get"external nofollow" target="_blank" href="https://github.com/xiayouran/Musicer">GitHub仓库点个Star哦!ヾ(≧∇≦*)ヾ