爬取蝦米音樂

在網頁版的蝦米音樂播放器,如何下載音樂。

本次爬蟲使用的語言是python3,模組是requests。筆者只是簡單的示例,程式中還有許多需要改進的地方。

1、蝦米音樂的播放器地址:http://www.xiami.com/play?ids=/song/playlist/id/1774747126#loaded

2、瀏覽器(筆者使用的是360極速瀏覽器),右鍵——審查元素,開啟開發者工具。進入network中的XHR,網頁歌曲資訊是動態載入的。記得要重新重新整理網頁,才會出現左側的檔案。找到如下圖的檔案。這個不是標準的json檔案。把Response中的程式碼,複製出來,進行觀察,會發現把json378()去掉之後,就是一個標準的json檔案。筆者沒有用程式碼處理這一步。

3、具體思路是:大家可以用requests訪問以下網頁:

  1. Request URL:
    http://www.xiami.com/song/playlist-default/cat/json?_ksTS=1511418676536_377&callback=jsonp378
  2. 然後req.text,再使用strip()函式去掉json378()。就可以得到json檔案。再使用json.loads() 提取出location。大家可以使用線上json解析工具,先查詢一下。
  3. 音樂的具體網址是隱藏在location的字典裡面:如下圖所示:
4、筆者是直接複製出location,才開始分析。
首先分析一下location,肯定經過加密,不會輕易得到音樂的下載連結url。筆者於是,上網查詢資料,在這裡要感謝cwyalpha

他的部落格文章有講到如何解密出MP3檔案地址。文章連結:http://blog.csdn.net/cwyalpha/article/details/48110941,大家可以參考一下。
例如:這個location是:
8h28n2325%%5_Fy955f6df3tF.e%%FE258la%2EEba5%t%xt2521FE8.u34–4c85p2i%FE19143mtD4%4cd5E%Fa268%2796ph1%58f%6%3mmF31517533_55E13575A1i842E2464%k1E-edEeE%2.15%%87_43e1%%96836
8代表把location分成8行,如何分。經筆者觀察主要按’h’,’t’,’t’,’p’,’%’,’3′,’A’,’%’,’2′,’F’,’%’,’2′,’F’,’m’這些字元進行分行,因為MP3的下載連結,前面都是http://m128.xiami.net域名。還有,注意的是每一行的長度都是差不多,所以有重複的分割字元,需要往後尋找分割字元,儘量與第一行長度一樣。最後,自己可以計算一下,location總共多少個字元,再除以8行,這樣就知道一行大概有多少字元。
所以筆者的分法如下:
8
h28n2325%%5_Fy955f6df3
tF.e%%FE258la%2EEba5%
t%xt2521FE8.u34–4c85
p2i%FE19143mtD4%4cd5E
%Fa268%2796ph1%58f%6%
3mmF31517533_55E13575
A1i842E2464%k1E-edEeE
%2.15%%87_43e1%%96836
如何用程式碼進行分割字元,筆者想法比較簡單,我會計算兩個分割字元的位置,來進行分割。應該還有更好的分割方法。
5、從上往下一直組合,如下圖所示,得到location的url編碼:筆者的程式碼實現是:以第一行字串長度為準,第一行字串長度-第二行字串長度,得到的差值,就表明需要補齊幾個‘*’ 號。當然,大家可以使用其他字元,並且裡面不會出現的字元,例如加號等。筆者此舉是要保持行數的字元個數一致,便於使用zip()函式,同時取出同一列的字元。
組合成url。最後用replace方法,把‘*’ 號,用空字元代替。
6、使用urllib模組的 parse.unquote()方法解碼,會出現‘^’字元,把‘^’字元變為‘0’字元,這樣就可以得到真正的url。
mix_url=urllib.parse.unquote(mix_url).replace(‘^’,’0′)
7、用requests模組訪問url,with open寫入檔案到電腦中
8、具體程式碼如下:
import urllib
import os
import requests  #下載蝦米音樂 dream it possible 下載地址在json檔案location
split_char=['h','t','t','p','%','3','A','%','2','F','%','2','F','m'] #分割字元標誌
#location
raw_url='8h28n2325%%5_Fy955f6df3tF.e%%FE258la%2EEba5%t%xt2521FE8.u34--4c85p2i%FE19143mtD4%4cd5E%Fa268%2796ph1%58f%6%3mmF31517533_55E13575A1i842E2464%k1E-edEeE%2.15%%87_43e1%%96836'
max_num=len(raw_url)//int(raw_url[0]) 1 #max_num=22 計算一行可以包含多少個字元
new_url=raw_url[1:] #去掉行數8
name=[]         #建立8個list
for k in range(1,int(raw_url[0]) 1):
name.append('string_%d' % k)
#分割字串   
j=1
for i in range(int(raw_url[0])):
count=0
name[j-1]=[]
position_1=new_url.find(split_char[i])
position_2=new_url.find(split_char[j])
if position_1==position_2:
position_2=new_url.find(split_char[j],position_1 1,max_num)
if position_2-position_1>=max_num-1:
name[j-1].append(new_url[count:position_2])
new_url=new_url.replace(new_url[count:position_2],'')
else:
position_3=new_url.find(split_char[j],position_2 1,max_num)
if position_3-position_1>=max_num-1:
name[j-1].append(new_url[count:position_3])
new_url=new_url.replace(new_url[count:position_3],'')
else:
position_4=new_url.find(split_char[j],position_3 1,max_num)
if position_4-position_1>=max_num-1:
name[j-1].append(new_url[count:position_4])
new_url=new_url.replace(new_url[count:position_4],'')
j =1
if j==int(raw_url[0]) 1:
name[j-2]=[]
name[j-2].append(new_url)
print(name)
#補齊,保證每一個list長度都一樣,方便使用zip函式
for i in range(len(name)):
if len(name[i][0]) < max_num:
name[i][0]=name[i][0] '*'*(max_num-len(name[i][0]))
mix_url=''
for a,b,c,d,e,f,g,h in zip(name[0][0],name[1][0],name[2][0],name[3][0],name[4][0],name[5][0],name[6][0],name[7][0]):
mix_url =a b c d e f g h
mix_url=mix_url.replace('*','') #去掉自己新增的‘*’號
mix_url=urllib.parse.unquote(mix_url).replace('^','0') #把'^','0'變為0字元
file_name=mix_url.split('/')[-1].split('?')[0] #mp3檔名
if not os.path.exists('D:\\xiami_music'): #儲存音樂的資料夾
os.makedirs('D:\\xiami_music')
os.chdir('D:\\xiami_music')
req=requests.get(mix_url)
with open('D:\\xiami_music\\' file_name,'wb') as file:  #下載檔案
file.write(req.content)
print('download ok')

歡迎大家不吝賜教。