本次练习的主要内容:
通过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. 可将本次代码作为批量爬取酷我音乐项目的一部分。
本文仅供学习交流使用,请勿将其用于违法目的。