Python爬蟲之網易雲音樂歌曲下載

Python爬蟲之網易雲音樂下載

目標

用Python根據網易雲音樂的ID,下載音樂,保存到本地MP3格式前端

能夠下載歌曲的範圍:全部可以聽的歌曲

配置基礎

  • Python 3.5

模塊

  • pycrypto
  • base64
  • requests
  • json
  • sys(可選)
  • progressbar(可選)

pycrypto

這是哈希函數(如 SHA256RIPEMD160)和各類加密算法( AESDESRSAElGamal等)的集合。主要是用來加密解密,爲什麼要用這個呢,稍後分析

安裝

pip install pycrypto

base64

python 自帶的模塊,主要是配合pycrypto模塊使用python

requests、json

requests主要用來發送網絡請求, json主要用於解析網絡請求的responsegit

分析

爲了不麻煩,咱們選擇網頁版的網易雲音樂而不用客戶端的,省去抓包的麻煩
咱們隨便選擇一首歌(這裏選擇:一千零一晚上 )而後打開網頁,獲得以下界面:github

https://note.youdao.com/favicon.ico

而後打開瀏覽器網絡面板,點擊播放按鈕,而後查看網絡請求,以下:算法

圖片描述

一共發送了四個網絡請求,仔細一點,咱們發現了一個有趣的請求,就是上圖最後一個,帶有.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實現

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

相關文章
相關標籤/搜索