當前位置:
首頁 > 科技 > 爬取網易雲音樂的評論後,竟有這種發現!

爬取網易雲音樂的評論後,竟有這種發現!

作者 | 志穎

責編 | 胡巍巍

用過網易雲音樂聽歌的朋友都知道,網易雲音樂每首歌曲後面都有很多評論,熱門歌曲的評論更是接近百萬或者是超過百萬條。

現在筆者就來分享一下如何爬取網易雲音樂歌曲的全部評論,由於網易雲音樂的評論都做了混淆加密處理,因此我們需要深入了解它的加密過程之後,才能爬取到網易雲音樂歌曲的全部評論。

首先分析數據的請求方式

網易雲音樂歌曲頁面的URL形式為https://music。163。com/#/song?id=歌曲ID號,這裡我用Delacey的Dream it possible 為例進行講解,它的URL為https://music。163。com/#/song?id=38592976。接下來開始分析數據的請求方式。

由於網易雲音樂的評論是通過Ajax傳輸,我們打開瀏覽器的開發者工具(檢查元素),選中控制面板中的Network,再點擊XHR(捕獲AJAX數據),然後點擊左上角的重新載入,會看到下面圖片中的數據請求列表。

現我們所需要的數據就在這JSON格式的數據中,其中Comments中是第一頁的全部評論,一共20條,Hot Comments是精彩評論一共有15條,每首歌曲只有第一頁評論才有精彩評論。接著看一下它的請求頭,點擊Headers。

我們發現的它是個Post請求,向下滑你會發現這個Post請求還帶有數據。

這些數據都是經過加密處理的,因此我們需要分析它的加密過程來生成相應的參數,然後把加密後的參數加到Post請求中才能獲取到我們需要的評論數據。


分析加密過程

通過斷點調試發現params和encSecKey是由JS腳本中的Window.asrsea()函數生成的。

我們發現Window.asrsea()函數有4個參數,在瀏覽器的JS控制台分別對這四個參數進行調試:

後面三個參數是定值,只有第一個參數是控制評論頁面偏移量的參數,它是一個變數。筆者經過分析發現第一個參數的形式是:

{"rid":"R_SO_4_38592976","offset":"0","total":"True","limit":"20","csrf_token":""}

下面我來詳細講解這個變數的發現過程:

首先找到core_dfe56728795d119e4d476fd09ea2dc51。JS這個JS腳本,然後將斷點打在第12973行,點擊第一頁評論,頁面載入到斷點處便停止了。

然後按下電腦的Esc鍵打開JS控制台,輸入i1x,查看第一個變數:

這是第一頁的i1x的值,接下來看第二頁的(需要點擊第2頁,然後輸入i1x的值):

再看第3頁:

再看第4頁:

通過這幾頁的分析,我們可以得到i1x值的變化規律,且可以得到它的一般形式:

{"rid":"R_SO_4_38592976","offset":"0","total":"True","limit":"20","csrf_token":""}

offset和limit是必選參數,其他參數是可選的,其他參數不影響data數據的生成,offset (頁面偏移量) = (頁數-1) * 20, 注意limit最大值為100,當設為100時,獲取第二頁時,默認前一頁是20個評論,也就是說第二頁最新評論有80個,有20個是第一頁顯示的。因此我們可以構造第一個參數為:


接下來,我們來看一下window.asrsea()函數的整個加密過程:

!function(){

// 函數a生成長度為16的隨機字元串

functiona(a){

vard, e, b ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c ="";

for(d =; a > d; d +=1)

e =Math.random() * b.length,

e =Math.floor(e),

c += b.charAt(e);

returnc

}

// 函數b實現AES加密

functionb(a, b){

varc = 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

});

returnf.toString()

}

// 函數c實現RSA加密

functionc(a, b, c){

vard, e;

returnsetMaxDigits(131),

d =newRSAKeyPair(b,"",c),

e = encryptedString(d, a)

}

functiond(d, e, f, g){

varh = {}

, i = a(16);

returnh.encText = b(d, g),

h.encText = b(h.encText, i),

h.encSecKey = c(i, e, f),

h

}

functione(a, b, d, e){

varf = {};

returnf.encText = c(a + e, b, d),

f

}

window.asrsea = d,

window.ecnonasr = e

}();

window.asrsea()函數就是上面的d函數,現在我們來看函數d:

functiond(d, e, f, g){

varh = {}

, i = a(16);

returnh.encText = b(d, g),// 第一次AES加密

h.encText = b(h.encText, i),// 第二次AES加密

h.encSecKey = c(i, e, f),// RSA加密

h

}

參數h。encText是經過兩次AES加密得到的,h。encSecKey是經過一次RSA加密得到的,其中i是隨機生成的長度為16的隨機字元串。


生成加密參數

首先我們需要生成長度為16的隨機字元串,這裡我們仿照上面的javascript的實現,用Python生成16位長的隨機字元串:

# 生成隨機字元串

defgenerate_random_strs(length):

string="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

# 控制次數參數i

i =

# 初始化隨機字元串

random_strs =""

whilei

e = random.random() * len(string)

# 向下取整

e = math.floor(e)

random_strs = random_strs + list(string)[e]

i = i +1

returnrandom_strs

接著用Python實現AES加密,這裡要用到PyCrypto庫,先安裝好這個庫:

pipinstall pycrypto

然後導入加密模塊:

fromCrypto.CipherimportAES

由於AES加密的明文長度必須是16的倍數,因此我們需要對明文進行必要的填充,以滿足它的長度是16的倍數:

# msg是需要加密的明文,如果不是16的倍數則進行填充(paddiing)

padding=16- len(msg) %16

# 這裡使用padding對應的單字元進行填充

msg= msg + padding * chr(padding)

# AES加密

defAESencrypt(msg, key):

# 如果不是16的倍數則進行填充(paddiing)

padding =16- len(msg) %16

# 這裡使用padding對應的單字元進行填充

msg = msg + padding * chr(padding)

# 用來加密或者解密的初始向量(必須是16位)

iv ="0102030405060708"

cipher = AES.new(key, AES.MODE_CBC, iv)

# 加密後得到的是bytes類型的數據

encryptedbytes = cipher.encrypt(msg)

# 使用Base64進行編碼,返回byte字元串

encodestrs = base64.b64encode(encryptedbytes)

# 對byte字元串按utf-8進行解碼

enctext = encodestrs.decode("utf-8")

returnenctext

然後是RSA加密。首先我簡單介紹一下RSA的加密過程。在RSA中,明文,密鑰和密文都是數字。RSA的加密過程可以用下列的公式來表達,這個公式非常的重要,你只有理解了這個公式,才能用Python實現RSA加密。

密文 = 明文EmodN (RSA加密)

RSA的密文是對代表明文的數字的E次方求mod N的結果, 通俗地講就是將明文和自己做E次乘法,然後將其結果除以N求餘數,這個餘數就是密文。

下面來看具體的RSA加密代碼實現:

# RSA加密

def RSAencrypt(randomstrs,key, f):

# 隨機字元串逆序排列

string= randomstrs[::-1]

# 將隨機字元串轉換成byte類型數據

text= bytes(string,"utf-8")

seckey = int(codecs.encode(text, encoding="hex"), 16)**int(key, 16) % int(f, 16)

# 返回整數的小寫十六進位形式

returnformat(seckey,"x").zfill(256)

RSA加密後得到的字元串長為256,如果不夠長則進行填充(不足部分在左側添0)。

最後就是獲取那兩個加密參數:

# 獲取參數

defget_params(page):

# msg也可以寫成msg= {"offset":"頁面偏移量=(頁數-1) * 20","limit":"20"},offset和limit這兩個參數必須有(js)

# limit最大值為100,當設為100時,獲取第二頁時,默認前一頁是20個評論,也就是說第二頁最新評論有80個,有20個是第一頁顯示的

# 偏移量

offset = (page-1) *20

# offset和limit是必選參數,其他參數是可選的,其他參數不影響data數據的生成,最好還是保留

msg ="{"offset":"+ str(offset) +","total":"True","limit":"20","csrf_token":""}"

key ="0CoJUm6Qyw8W8jud"

f ="00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"

e ="010001"

enctext = AESencrypt(msg, key)

# 生成長度為16的隨機字元串

i = generate_random_strs(16)

# 兩次AES加密之後得到params的值

encText = AESencrypt(enctext, i)

# RSA加密之後得到encSecKey的值

encSecKey = RSAencrypt(i, e, f)

returnencText, encSecKey


獲取全部評論

上面我們獲取到了兩個參數encText和encSecKey,利用這兩個參數來構造post表單數據(Form Data),即data的值:

params, encSecKey = get_params(page)

data = {"params":params,"encSecKey": encSecKey}

歌曲評論的URL為:

url="https://music.163.com/weapi/v1/resource/comments/R_SO_4_"+ str(songid) +"?csrf_token="

然後把data加到post的參數中去就能獲取到json格式的評論數據。

html= requests.post(url, headers=headers, data=data)

至此,獲取網易雲音樂全部評論的Python爬蟲實現原理分析全部完成!

作者:志穎,一名狂熱的python爬蟲愛好者。

本文系作者投稿,不代表CSDN立場。

微信改版了,

想快速看到CSDN的熱乎文章,

趕快把CSDN公眾號設為星標吧,

打開公眾號,點擊「設為星標」就可以啦!

2018 AI開發者大會

AI工程師必備大會

2018 AI開發者大會是一場由中美人工智慧技術高手聯袂打造的AI技術與產業的年度盛會!我們只講技術,拒絕空談!

這裡有10場技術專題論壇:計算機視覺、數據分析、機器學習、知識圖譜、智慧金融、智能駕駛、語音技術、智慧醫療、機器學習工具、自然語言處理。

還有15+矽谷實力講師團、80+AI領軍企業技術核心人物、100+技術&大眾實力媒體、1500+AI專業開發者

點擊下方「海報,快速獲取大會更多信息,並獲得最低折扣票!


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 CSDN 的精彩文章:

放棄 802.11 命名方式,Wi-Fi 6 標準公布,速度快 37%
優秀程序員寫代碼一定會用的 11 條經驗!

TAG:CSDN |