天天看點

關于爬網易雲音樂的一些坑和一些方法

最近,心血來潮的我準備去爬爬網易雲音樂(https://music.163.com/)的評論,對于這個網站,我在開始學習爬蟲的時候也試過,但是當時沒有堅持下去,最後放棄了,現在為了工作項目的準備,準備将這些網站慢慢的進行爬取,好了,廢話不多說。

我用了3天,将評論爬取下來,暫時隻是将其下載下傳到我的磁盤上,沒有上傳資料庫

1第一步我準備先将熱門的歌手爬取下來,然後進入歌手的首頁将歌曲爬取,在接着爬取歌曲對應的評論,

https://music.163.com/#/discover/artist這個是歌手的首頁

剛開始我就用一般爬蟲最基礎的方法,requests請求,結果請求下載下傳的不是在網頁上看到的東西,我嘗試着加上headers,結果還是一如既往的糟糕,傳回的一直一堆沒有用的資料,(有一個小問題,我掉進了這個坑,就是網頁連結中的#,參考這篇部落格https://www.cnblogs.com/kaituorensheng/p/3776527.html)

然後我就更換了url,https://music.163.com/discover/artist去掉了#号後,果然爬取出來的資料不一樣了,資料中出現了幾個歌手的名字,但是我想要的熱門歌手的名字卻沒有,我之後打開了控制台,在裡面進行找尋,最後發現了幾個post請求的url,通過抓包工具,fiddler,我擷取到了想要的url,但是這個請求中的兩個參數卻是加密的,看下圖

關于爬網易雲音樂的一些坑和一些方法

之後我通過百度,知道了這個是一種js的一種加密方式,然後我準備按照js的加密方式,找到資料的加密參數,模拟浏覽器的post請求來實作我的目的。

2.第二步:

   分析網頁js,打開控制台(F12),找到Sources,按ctrl+f,搜尋encSecKey,找到後發現在某個函數當中,

關于爬網易雲音樂的一些坑和一些方法

然後準備進行單步調試,設定斷點,重新整理網頁,進入單步調試模式

關于爬網易雲音樂的一些坑和一些方法

在調試的過程中,我發現,i參數是一個随機的16位的字元,efg三個參數不變,分别為:

e:"010001"
f:"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g:"0CoJUm6Qyw8W8jud"
           

隻有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
}();
           

這個是js進行的加密方式,具體分析:如下圖:

關于爬網易雲音樂的一些坑和一些方法

廢話不多說,看代碼:

from Crypto.Cipher import AES
import base64
from binascii import hexlify
import json,os,requests,headers

class Encrypyed():
    def __init__(self):
        self.pub_key = '010001'
        self.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
        self.nonce = '0CoJUm6Qyw8W8jud'

    def create_secret_key(self, size):
        return hexlify(os.urandom(size))[:16].decode('utf-8')

    def aes_encrypt(self,text, key):
        iv = b'0102030405060708'
        pad = 16 - len(text) % 16
        text = text + pad * chr(pad)
        text=text.encode('utf-8')
        encryptor = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv)

        result = encryptor.encrypt(text)
        result_str = base64.b64encode(result).decode('utf-8')
        return result_str

    def rsa_encrpt(self,text, pubKey, modulus):
        text = text[::-1]
        rs = pow(int(hexlify(text.encode('utf-8')), 16), int(pubKey, 16), int(modulus, 16))
        return format(rs, 'x').zfill(256)

    def work(self,text):
        text = json.dumps(text)
        i=self.create_secret_key(16)

        encText =self.aes_encrypt(text, self.nonce)
        encText=self.aes_encrypt(encText,i)
        encSecKey=self.rsa_encrpt(i,self.pub_key,self.modulus)
        data = {'params': encText, 'encSecKey': encSecKey}
        return data
           

我将其封裝為一個類class Encrypyed()如下圖分析:

關于爬網易雲音樂的一些坑和一些方法

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

上述的Crypto要進行安裝,安裝指令:

pip install pyCryptodome

這個我在2018.12.26日親測有效,導入的時候如果導入失敗,進入python安裝目錄下的Lib\site-packages找到crypto目錄,将名字改為Crypto然後進行導入,因為我是首字母大寫的,預設安裝時小寫的

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

生成參數後,看下面代碼:

def parse_data(self,song_id,offset=0,limit=20):
        data={"rid":"R_SO_4_"+str(song_id),"offset":str(offset),"total":"true","limit":str(limit),"csrf_token":""}
        return self.work(data)

    def parse(self,song_id,offset,limit):
        url = "https://music.163.com/weapi/v1/resource/comments/R_SO_4_"+str(song_id)+"?csrf_token="
        data=self.parse_data(song_id,offset,limit)
        res = requests.post(url=url, data=data, headers=headers.headers_discuss, verify=False)
        return res
           

在對需要參數的網頁解析時,要先找到post請求的url和對應的d參數,才能生成對應的data參數

關于爬網易雲音樂的一些坑和一些方法

通過上述的操作,我們就完成了對于某一個歌曲的評論的爬取。

完整代碼在上面,隻需要添加headers,然後對類進行執行個體化,調用parse傳遞相應參數即可得到json格式的評論

如上就講清楚了一點,對于網易雲音樂的評論的加密的方法

之後我在說說如何找到對應參數,我将這個寫到另一篇部落格中《爬取網易雲音樂評論的d參數的分析方式》

然後我們就得到了相應的url和data了

看代碼:

url = "https://music.163.com/weapi/v1/resource/comments/R_SO_4_"+str(song_id)+"?csrf_token="
        data=self.parse_data(song_id,offset,limit)
        res = requests.post(url=url, data=data, headers=headers.headers_discuss, verify=False)
           

将其整合進行請求

在對歌手的首頁進行爬取時,發現并沒有歌曲的資料post請求,然後就可以進行普通的方法,對網頁url進行拼接,發送resquests請求,用xpath進行解析,或者美麗湯。

以上的就是我這次爬取網易雲遇到的坑,如果還有其他問題可以給我留言,我一定盡心解答。

源代碼的github位址:https://catherineangel.github.io/music_163/

繼續閱讀