用Python根据网易云音乐的ID,下载音乐,保存到本地MP3格式前端
能够下载歌曲的范围:全部可以听的歌曲
这是哈希函数(如SHA256
和RIPEMD160
)和各类加密算法(AES
,DES
,RSA
,ElGamal
等)的集合。主要是用来加密解密,为什么要用这个呢,稍后分析
pip install pycrypto
python
自带的模块,主要是配合pycrypto
模块使用python
requests
主要用来发送网络请求, json
主要用于解析网络请求的response
git
为了不麻烦,咱们选择网页版的网易云音乐而不用客户端的,省去抓包的麻烦
咱们随便选择一首歌(这里选择:一千零一晚上 )而后打开网页,获得以下界面:github
而后打开浏览器网络面板,点击播放按钮,而后查看网络请求,以下:算法
一共发送了四个网络请求,仔细一点,咱们发现了一个有趣的请求,就是上图最后一个,带有.mp3
后缀的那个,很明显,这里是将一千零一晚上
这首歌缓存了下来,复制该网络请求到浏览器地址栏打开,而后浏览器就开始下载一千零一晚上
这首歌。到此,好像前面提的所谓的目标完成了,可是我不开心,身为一个开发人员,这么没有技术含量的东西,是否是能够考虑用技术去实现呢。能不能输入一个歌曲的ID,而后就把歌曲下回来呢。
咱们播放多几首歌曲,很容易发现,每一首歌曲都会有一个独立的连接,仔细看看这个连接(http://m10.music.126.net/2018...),显然是通过处理的,这个处理有多是前端直接处理的,也有多是后端处理的(是否是说了等于白说。。。),后端处理会有多种状况,其中一种就是另外一个网络请求返回来对应的东西,反正网络请求很少,咱们先看看网咯请求,一看吓一跳,还真蒙着了,上图中第一个网络请求返回来的数据json
再看一下请求的组成后端
只要模拟这个请求,就能够获得歌曲的连接,只要获得连接就能下载歌曲。在该请求的参数中,params
以及encSecKey
都是一个通过加密的数据,在反复分析点击播放按钮的事件后,获得JavaScript
进行了以下操做浏览器
var bPc2x = window.asrsea(JSON.stringify(j4n), buv7o(["流泪", "强"]), buv7o(Tg9X.md), buv7o(["爱心", "女孩", "惊恐", "大笑"])); e4i.data = k4o.cE5J({ params: bPc2x.encText, encSecKey: bPc2x.encSecKey })
其中,window.asrsea
函数代码以下缓存
!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 }();
由上得知,window.asrsea
一共传递了四个参数(假设为window.asrsea(a, b, c, d)),而这四个参数中,只有 a
是一个跟歌曲id相关的参数,其余三个都是一个常量网络
b = "010001"; c = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7" d = "0CoJUm6Qyw8W8jud"
再研究window.asrsea
的代码,发现请求的两个参数params
以及encSecKey
都在这里加密了,其中params
通过了两次AES
加密,第一次加密的时候,传入了两个参数,一个是a,一个是d,第二个加密的两个参数,第一个是第一次加密的结果,第二个是一个16位的随机字符串,由于是一个随机的字符串,因此咱们能够随便用一个16位的字符串就好了,因为这里这个随机的字符串固定了,那第二个参数encSecKey
就是一个固定的值
至此,咱们的分析完成,也获得了须要的信息
Python想要模拟请求,那就须要进行AES
加密,所以咱们就用到了开始所说的pycrypto
模块
from Crypto.Cipher import AES import base64 def aes_encrypt(text, key): iv = "0102030405060708" pad = 16 - len(text) % 16 text = text + pad * chr(pad) encryptor = AES.new(key, AES.MODE_CBC, iv) result = encryptor.encrypt(text) result_str = base64.b64encode(encrypt_text) return result_str
QQ交流群: 173318043
项目地址:lmissy.cn若是本文对你有所帮助,请点个赞,这是我努力下去的无限动力,谢谢(。・ω・。)