今天你網(wǎng)易云了么
很久之前曾經(jīng)寫過一篇聽什么歌都像是在唱自己的推送,寫的是網(wǎng)易云的歌,可以點一以下鏈接穿越。聽什么歌,都像在唱自己今天我們不是來談音樂情懷的,而是python爬蟲技術(shù)分享。接上一篇下廚房的爬蟲文章,這次給大家?guī)淼氖蔷W(wǎng)易云爬蟲。利用爬蟲爬取我喜歡的音樂前1000個并做成詞云圖。
分解一下項目,首先需要找到對應(yīng)的鏈接,然后把歌曲名字,演唱者,專輯圖片下載下來。如下圖所示,可以看到,網(wǎng)易云我喜歡的音樂里面有1000多個,這里我們?nèi)∏?000個進(jìn)行分析。

說干就干,request搞起來
import requestsurl = 'https://music.163.com/#/my/m/music/playlist?id=127497555'res = requests.get(url=url)print(res.text)
一頓操作之后,你會發(fā)現(xiàn),雖然你get到了正確的響應(yīng)碼200,頁獲取了網(wǎng)頁的內(nèi)容,但是內(nèi)容確是重定向到music.163.com,看返回的內(nèi)容就知道是登錄界面。這時候猜想,因為沒有登錄所以返回讓你登錄的頁面。這時候就要想辦法模擬登錄。模擬登錄需要找到登錄網(wǎng)址。登錄的時候按F12查看Network選項下的記錄,刷新的網(wǎng)頁比較多,可以加個過濾,只看XHR(可以簡單理解為post請求)可以參考下圖。b對應(yīng)的網(wǎng)站是提交登錄按鈕的時候發(fā)送的請求,返回的只是一個狀態(tài),沒有內(nèi)容。下面一個cellphone像是手機(jī)登錄的實際入口,返回的都是和個人賬戶相關(guān)的內(nèi)容。#返回的內(nèi)容里有手機(jī)號登錄

研究這兩個請求,嘗試向這兩個網(wǎng)址提交賬號密碼 數(shù)據(jù)https://ac.dun.163yun.com/v3/b , https://music.163.com/weapi/user/grabed/status/get?csrf_token=4a2d3da0210ddb2618018149c4c4e824,使用post方法,帶上需要提交的賬戶密碼,結(jié)果得不到正確的響應(yīng)。在data里加上'remember':'True'后,還是得不到200響應(yīng)。
這時候百度搜了一下先驅(qū)們的做法和思路。原來網(wǎng)易云進(jìn)行了js加密。你提交的數(shù)據(jù)需要經(jīng)過算法加密了之后提交。而且同一個輸入,每一次加密的結(jié)果是不同的。data = {'username':'',????'password':'',????'remember':''}res = requests.post(url=url,headers=headers,data=data)

這個加密過程是通過研究網(wǎng)易云刷新頁面的時候調(diào)用的js文件,然后搜索關(guān)鍵字encSecKey得到,這幾個abcde來回倒騰,捋清楚他們之間的關(guān)系,解密算法就可以寫出來了。# 生成16個隨機(jī)字符def generate_random_strs(length):string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"# 控制次數(shù)參數(shù)ii = 0# 初始化隨機(jī)字符串random_strs = ""while i < length:e = random.random() * len(string)# 向下取整e = math.floor(e)random_strs = random_strs + list(string)[e]i = i + 1return random_strs# AES加密def AESencrypt(msg, key):# 如果不是16的倍數(shù)則進(jìn)行填充(paddiing)padding = 16 - len(msg) % 16# 這里使用padding對應(yīng)的單字符進(jìn)行填充msg = msg + padding * chr(padding)# 用來加密或者解密的初始向量(必須是16位)=?'0102030405060708'cipher = AES.new(bytearray(key,'utf-8'),AES.MODE_CBC, bytearray(iv,'utf-8'))# 加密后得到的是bytes類型的數(shù)據(jù)encryptedbytes = cipher.encrypt(bytearray(msg,'utf-8'))# 使用Base64進(jìn)行編碼,返回byte字符串encodestrs = base64.b64encode(encryptedbytes)# 對byte字符串按utf-8進(jìn)行解碼=?encodestrs.decode('utf-8')return enctext# RSA加密def RSAencrypt(randomstrs, key, f):# 隨機(jī)字符串逆序排列string = randomstrs[::-1]# 將隨機(jī)字符串轉(zhuǎn)換成byte類型數(shù)據(jù)text = bytes(string, 'utf-8')seckey = int(codecs.encode(text, encoding='hex'), 16)**int(key, 16) % int(f, 16)return format(seckey, 'x').zfill(256)# 獲取參數(shù):msg = textkey = '0CoJUm6Qyw8W8jud'f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'e = '010001'enctext = AESencrypt(msg, key)# 生成長度為16的隨機(jī)字符串i = generate_random_strs(16)# 兩次AES加密之后得到params的值encText = AESencrypt(enctext, i)# RSA加密之后得到encSecKey的值encSecKey = RSAencrypt(i, e, f)return encText, encSecKey

理解這個加密解密過程并不難,重點是需要自己動手實踐一遍。博主大神們想要爬取的網(wǎng)站跟我的并不相同,網(wǎng)上的參考資料也不全對,需要自己動手實踐。動手實踐步驟可參考參考鏈接?'糕糕python'的博文?https://www.jianshu.com/p/a45714d16294?網(wǎng)易云音樂爬蟲(JS破解全過程)
知道了js解密之后,還需要知道對什么數(shù)據(jù)進(jìn)行加密,就是你加密的數(shù)據(jù)格式是怎樣的,這一步需要抓包工具。這里我們使用fiddler。梳理加密過程實踐安裝fiddler軟件,fiddler所有權(quán)使用說明以fiddler官網(wǎng)為準(zhǔn)
安裝好之后熟悉界面操作,我們主要使用左邊的界面來關(guān)注所有的收發(fā)消息和右邊工具欄下的AutoResponder(fiddler需要安裝一下認(rèn)證證書,瀏覽器和網(wǎng)頁的通信會出問題)
因為登錄提交數(shù)據(jù)的時候,是經(jīng)過js加密的,我們更改一下fiddler的默認(rèn)配置,使得訪問js的消息會黃色高亮

在network里面Initiator里面可以查看對應(yīng)的js文件,在瀏覽器中可以將js保存到本地查看。我們需要關(guān)注的是core.js(網(wǎng)站上core后面會帶一串?dāng)?shù)字)
有點代碼基礎(chǔ)的小伙伴可以把這個js的調(diào)用關(guān)系捋一下,捋完了就可以看懂博主們寫的代碼
js文件里面是加密過程的破解,這個加密是有一個輸入的,這個輸入是需要通過打印的方式得到的,這個是我們提交數(shù)據(jù)的格式。在本地的js文件里加上這兩句打印。

在fiddler中,設(shè)置,當(dāng)訪問數(shù)據(jù)請求core.js的時候,就使用本地的js來做響應(yīng)。(相當(dāng)于截胡)這樣就可以順利的看到請求的數(shù)據(jù)格式,見下圖

可以看到請求播放列表的時候提交的數(shù)據(jù)格式為'id': 'offset': "",'total': "",'limit': "",'n': "",'csrf_token': ""知道提交格式后,填進(jìn)去個人信息,然后就可以歡快的使用request.post了
defcreate_form_data(self):text = json.dumps(self.text)params, encSecKey = get_params(text)form_data = {'params': params, 'encSecKey': encSecKey}return form_datadef get_song_list(self):data = self.create_form_data()res = requests.post(self.link, headers= self.header, data=data)with open(r'data/wyy.json', 'wb') as f:f.write(res.content)if(res.status_code == 200):print('歌曲列表網(wǎng)頁下載成功')else:print('歌曲列表網(wǎng)頁下載失敗,響應(yīng)碼為{}'.format(res.status_code))
同樣的方式可以得到登錄界面的數(shù)據(jù)格式為'phone': '','password': '','rememberLogin': '','checkToken' : '','csrf_token' : '',其中password是hash之后的數(shù)據(jù)?
爬取不同的界面,需要提交的數(shù)據(jù)格式不同,所以一定要學(xué)會使用fiddler截胡各個頁面
接下來是完善request請求
完善請求時候的請求頭,還有cookie信息,目前網(wǎng)易云對于csrf_token沒有啟用校驗如果開啟的話,還需要把cookie里面的信息放到請求頭里使用

headers?=?{"Accept": "*/*","Accept-Encoding": "gzip,deflate,br","Accept-Language": "zh-CN,zh;q=0.9","Connection": "keep-alive","Content-Type": "application/x-www-form-urlencoded","Host": "music.163.com","Referer": "https://music.163.com/my/","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36",'Cookie' : 'NMTID=00OtwnUrt3vyloXqU3CrJQUz0o-yFUAAAF2LNqIrA; JSESSIONID-WYYY=AXdnG8nFpoY6PNi3XDdHgu99GoiK59brvkdEsAWd9Dk4u1089gqezazwWxWhCNNZ9wU7CoCFKXJhAboxaOMPRaVS2kmxgYb03I8th7aD72uXAHpQI6CUZRO6IldW%2FRgqPz1EAj2ozCzEeT957j826t78SS%2FOsKrilkCIue0PlHdK0t%2F%2B%3A1607082653296; _iuqxldmzr_=32; _ntes_nnid=9b95e833cabaf03992755f38d129f286,1607070412949; _ntes_nuid=9b95e833cabaf03992755f38d129f286; WM_NI=awDKKogly2FEWYgqHXRmTkHJIAqvPztlqKzum6mrAwE4kFsMecGBGSLRKKcT4nr5dA3tA6aOcqLjivp4L%2F65CRg1H4ijf6oJ7a67Bw43FuWIGjyz3qzGENdashEyPenNN2o%3D; WM_NIKE=9ca17ae2e6ffcda170e2e6eea8c445edb388d1b770a68e8eb3c54f838f8eabaa6f96a6a5daf85fb391bd92c52af0fea7c3b92aa3a98cacca65a5b4b7d3c54f9587a992cb5492b39c8ccb428b9fbf97d372b8b4bfacf97fafeaa393c47d87eca5a7f247a7bd89a3c659908e85ace260f8b2bed6e74bf1ac8fd7e445a1af848bec7eb4ec84d0b53f818ea9d2cb40abb6aad7ee42f5bffad8c17df8ed8a96d64385b6b79bb27bba998bafe76bb1b88b9ad36fae9282d3ee37e2a3; WM_TID=YeT5mqogqlRAVABRAUdueB3h9rV%2BO86Q; ntes_kaola_ad=1; WEVNSM=1.0.0; WNMCID=xqvafx.1607073379709.01.0; __remember_me=true; MUSIC_U=56131b60258d038b13494f63fb6163f1bf9b6ae5b82252dab58e6be3caa4bce20931c3a9fbfe3df2; __csrf=0251ce2d0e4fad451746504430572ee3'}
請求頭設(shè)置好之后,如果你的數(shù)據(jù)格式,加密算法都沒有問題的話,使用post方法應(yīng)該就可以收到200響應(yīng),同時帶有你想要的數(shù)據(jù)
補(bǔ)充一下,get和post方法的區(qū)別,get就像是一個靜態(tài)頁面,東西已經(jīng)加載好了在那里,你一調(diào)用get,就給你返回數(shù)據(jù)
post方法更像是動態(tài)頁面,東西沒準(zhǔn)備好,你post的時候告訴服務(wù)器你需要什么東西,然后服務(wù)器驗證一下你提交的格式對不對,對的話,再匆匆忙忙把你要的寶貝返回給你
處理返回的數(shù)據(jù)
歌名,演唱者信息可以在返回的數(shù)據(jù)里直接拿到
專輯圖片的鏈接也在返回數(shù)據(jù)里,啟用一個多進(jìn)程將這些圖片下載下來。進(jìn)程數(shù)按照cpu的核數(shù)來設(shè)置。如果你不幸有一個12核的cpu的話,那你下載速度會非常快
def muti_process(self):# urls = urls[0:21] #拿前20個做實驗pool = Pool(cpu_count())with open(r'data/pic_link.txt', 'rb') as f:urls = f.read()urls = urls.decode('utf-8').split()print('多線程啟動')pool.map(self.downlaod_album, urls)pool.close()????pool.join()????def?downlaod_album(self,url):try:with open(r'output/album/{}.png'.format(url[-15:-4]), 'wb') as f:f.write(requests.get(url).content)print('圖片下載成功')# print(url[-10:-4])except ConnectionError:print('Error Occured ', url)finally:print('下載任務(wù)執(zhí)行完畢')
調(diào)用wordcloud模塊生成詞云圖
def draw_wordcloud(self,file,pic):self.pic = picmytext = self.cut_word(file)mask = imread(self.pic, pilmode="RGB")wc = WordCloud(# 設(shè)置字體,不指定就會出現(xiàn)亂碼font_path=r"C:\Windows\Fonts\simhei.ttf",#避免詞的重復(fù)collocations = False,# 設(shè)置背景色background_color='white',# 設(shè)置背景寬width=500,# 設(shè)置背景高height=350,# 最大字體max_font_size=50,# 最小字體min_font_size=10,font_step=4,mode='RGBA',# colormap='pink'mask=mask)# 產(chǎn)生詞云wc.generate(mytext)wc.to_file(r"output/wordcloud.png") # 按照設(shè)置的像素寬高度保存繪制好的詞云圖,比下面程序顯示更清晰print('詞云圖片保存成功')
調(diào)用cv2/pil模塊處理專輯圖片 ->這部分內(nèi)容詳見另一個github,https://github.com/ZhouFall/image_handle
最后的結(jié)果如下:可以看出,喜歡的歌手是陳奕迅和高梨康治(火影的作曲),說明一下,詞云圖只能反映出現(xiàn)的次數(shù)多少,可以用來看個趨勢。喜歡的歌,cover和翻自出現(xiàn)的比較多,大概是因為喜歡聽翻唱的吧(也有因為貧窮,聽不到正版的原因)




參考網(wǎng)站:
https://www.jianshu.com/p/a45714d16294?網(wǎng)易云音樂爬蟲(JS破解全過程)
https://www.cnblogs.com/bcaixl/p/13928629.html?python3爬蟲應(yīng)用--爬取網(wǎng)易云音樂(兩種辦法)
https://blog.csdn.net/weixin_42555080/article/details/90105330?Python爬蟲之網(wǎng)易云音樂數(shù)據(jù)爬取(十五)
https://www.zhihu.com/question/36081767 如何爬網(wǎng)易云音樂的評論數(shù)?
https://www.zhihu.com/question/36081767/answer/65820705 如何爬網(wǎng)易云音樂的評論數(shù)?
https://blog.csdn.net/qq_39138295/article/details/89226990?request保持會話,尋找set-cookie來獲取數(shù)據(jù)
