用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若是本文對你有所幫助,請點個贊,這是我努力下去的無限動力,謝謝(。・ω・。)