本次練習的主要内容:
通過python爬蟲,破解酷我音樂的api,下載下傳音樂檔案到本地。
(一)項目概況
1. 這裡酷我音樂的首頁:

2. 點選左上角的榜單,進去之後再點選具體歌名,跳轉到音樂播放頁面,可以線上聽音樂:
3. 本次項目的主要目标,就是通過音樂具體的播放頁面url,
即這樣的連結:http://www.kuwo.cn/yinyue/42484721?catalog=yueku2016
下載下傳該音樂到本地。
(二)網頁分析
1. 在音樂播放頁面打開開發者工具,檢視網絡請求:
在衆多網絡請求中,可以找到一個以.aac結尾的請求,猜測該請求就是目标音樂的請求。
2. 分析可知,該請求具體情況如下:
請求類型:get
請求url:
http://ip.h5.nf03.sycdn.kuwo.cn/ee3a8426c1def769b22e5c23639d6220/5b2b12a1/resource/a1/65/1/2669068103.aac
是以,隻要模拟該get請求,就能拿到aac檔案。
但是該url裡面有許多陌生的字段,具體是從哪裡來的呢?
3. 經查找,可以在下面的請求中的response的headers裡面,找到上面的url:
4. 經分析該請求的情況如下:
請求類型:get
請求url:
http://antiserver.kuwo.cn/anti.s?format=aac|mp3&rid=MUSIC_42484721&type=convert_url&response=res
注意該請求傳回的狀态碼是302。
仔細觀察該請求的參數,發現隻有“MUSIC_42484721”這個字段是動态變更的,其他都是固定的。
那麼該MUSIC字段是從哪裡來的呢?
仔細想想,原來就是音樂播放頁面的url中包含的一個字段:http://www.kuwo.cn/yinyue/42484721?catalog=yueku2016
5. 這樣就很明确了,下面梳理下思路:
- 通過音樂播放頁面url,拿到類似這樣的字段 “42484721”
- 通過該字段,構造第4步的get請求,拿到response的headers的location字段
- 通過location字段,發送get請求,拿到aac檔案
(三)核心代碼實作
1. 擷取音樂播放url中特定字段
import requests
import re
s = requests.session()
filename = input("請輸入音樂檔案儲存的本地路徑:").strip()
base_url = input("請輸入音樂播放頁面url:").strip()
base_number = re.findall(r"(?:http://www.kuwo.cn/yinyue/)(\d+)(?:\?catalog=yueku2016)", base_url)[0]
2. 通過上面的字段,構造第一個get請求,擷取傳回頭中的location字段
def get_url():
headers = {
'Accept': '*/*',
'Accept-Encoding': 'identity;q=1, *;q=0',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Connection': 'keep-alive',
'Host':'antiserver.kuwo.cn',
'Range': 'bytes=0-',
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Referer':base_url
}
url = "http://antiserver.kuwo.cn/anti.s?format=aac|mp3&rid=MUSIC_"+ base_number +"&type=convert_url&response=res"
r = s.get(url, headers=headers, allow_redirects=False)
return r.headers['Location']
3. 通過location字段中的url,擷取到aac檔案資料
注意在網絡分析時的截圖,可以看到該請求的傳回的狀态碼是206,這是由于該請求的請求頭中添加了Range字段,代表僅請求部分内容,我這裡設定為了'Range': 'bytes=0-',意思是請求從0位元組之後的所有内容。
def get_aac(url):
base_host = re.findall(r"(?:http://)(.*)", url)[0]
base_host = base_host.split('/')[0]
headers ={
'Accept': '*/*',
'Accept-Encoding': 'identity;q=1, *;q=0',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Connection': 'keep-alive',
'Host': base_host,
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36',
'Referer':base_url,
'Range': 'bytes=0-'
}
r = s.get(url, headers=headers, stream=True)
return r
4. 将aac檔案資料寫入到本地
def save_aac(filename, res):
with open(filename, 'wb') as fd:
for chunk in res.iter_content(chunk_size=128):
fd.write(chunk)
5. 代碼中的注意事項
實際寫代碼的過程中,發現酷我對請求頭的檢查非常嚴格,是以請求頭務必根據實際情況編寫準确,否則無法正确擷取資料。
(四)項目結果
通過給定的音樂播放url,就可以将該音樂下載下傳到指定的本地檔案存儲路徑中。
(五)下一步
1. 代碼中增加相應的錯誤檢查
2. 可将本次代碼作為批量爬取酷我音樂項目的一部分。
本文僅供學習交流使用,請勿将其用于違法目的。