最近想写一个QQ音乐的API接口,为了省事先到网上查了资料,发现很多都是不能用的,估计官方进行了升级,算了,还是自己来吧。

1. 如何下载付费歌曲

打开QQ音乐,任意搜索一首歌曲,发现歌曲需要收费:

python下载歌 python爬虫下载歌曲_json

但我们可是优秀的爬虫工程师,要看底层的东西。打开网页版QQ音乐,搜索该歌曲会显示如下页面:

python下载歌 python爬虫下载歌曲_python 歌曲相似度 音乐推荐_02

点击第一首:

python下载歌 python爬虫下载歌曲_搜索_03

这样就来到了播放页面,然后打开开发者选项,在network选项卡中按ctrl+R展示所有的交互文件。然后第一步当然是检查网页源代码啦,因为歌曲URL通常会包含在源代码中,如图所示

python下载歌 python爬虫下载歌曲_json_04

在源代码中查找URL可能比较繁琐,但只要细心就能发现一些蛛丝马迹,如图:

python下载歌 python爬虫下载歌曲_v8_05

这个节点中包含了一串m4a文件的URL,实际上QQ音乐下载的文件大多数是m4a格式的,当我们打开这个URL之后,果然就是歌曲文件:

python下载歌 python爬虫下载歌曲_搜索_06

那么我们只要得到任意歌曲的URL就能通过请求库直接下载了。但是在那之前我们需要取得播放页面的URL,因为歌曲URL是包含在播放页面的源代码中的,我们可以看到播放页面URL为:

https://i.y.qq.com/v8/playsong.html?ADTAG=newyqq.song&songmid=001OyHbk2MSIi4

其中ADTAG是固定参数,需要注意的是songmid,它指定每首歌曲的唯一id,如果要寻找songmid,我们可能要前往搜索页面,因为这个参数很可能包含在我们按下搜索键后服务器的返回值中。

但遗憾的是,我们并没有在搜索页面源代码中发现任何歌曲信息,这说明歌曲信息很可能是以XHR格式传输的,这也很符合网站设计的规范。

终于在一个XHR文档中我们发现了搜索结果:

python下载歌 python爬虫下载歌曲_搜索_07

list中的每个值都是一个搜索结果,展开之后就能看到歌曲的mid:

python下载歌 python爬虫下载歌曲_搜索_08

然后再看看页面的URL:

https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=txt.yqq.song&searchid=64979681270102176&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=1&n=10&w=%E5%8D%81%E5%B9%B4&g_tk_new_20200303=5381&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0

其中有很多参数,但是我经过多次验证发现,只有w参数是必须的,如图:

python下载歌 python爬虫下载歌曲_python下载歌_09

这其实给我们提供了很多方便。

那么我们先梳理一下流程,我们需要传递一个w歌曲名参数到搜索页面中,在搜索结果中找到歌曲mid,获得歌曲页面源代码,在源码的audio节点中找到歌曲URL,最后下载保存到文件中。

import requestsfrom urllib.parse import unquote, quotefrom pyquery import PyQuery as pqclass Music():    def __init__(self):        self.headers = {            'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Mobile Safari/537.36'        }    def download_music(self, sone_name):        '''定义下载文件方法'''        self.get_sonmid(sone_name)    def get_sonmid(self, sone_name):        '''获取文件ID'''        url = 'https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=txt.yqq.song&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=1&n=10&w={0}&g_tk_new_20200303=5381&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0'.format(quote(sone_name))        #搜索页面URL        resp = requests.get(url, headers=self.headers)        #返回搜索列表        resp_dic = resp.json()        self.get_songloc(resp_dic['data']['song']['list'][0]['mid'])        #获得结果中第一首歌曲ID    def get_songloc(self, mid):        '''获得歌曲文件链接并下载'''        url = 'https://i.y.qq.com/v8/playsong.html?ADTAG=newyqq.song&songmid={0}'.format(mid)        #通过mid构建播放页面URL        resp = requests.get(url, headers=self.headers)        #返回HRML文档树        doc = pq(resp.text)        audio = doc('audio')        #获得audio节点        medio_url = audio.attr('src')        #获得歌曲文件URL        print(medio_url)        resp = requests.get(medio_url, headers=self.headers)        with open('music.m4a', 'wb+') as f:            f.write(resp.content)#保存二进制文件if __name__ == '__main__':    music = Music()    song_name = input('请输入歌曲名:')    music.download_music(song_name)

运行以上代码后,在控制台输入“十年”,我们可以看到控制台成功输出了歌曲文件的URL:

http://aqqmusic.tc.qq.com/amobile.music.tc.qq.com/C400002AIxAT3HZwiA.m4a?guid=7223831426&vkey=B2EF54EF4C8F2FA2B6544984A54B071B623702999E1DAE7DD355884D595CAACC79D4C54C4AA8890CB3BE98760092825D9E46CEF4139DDFB6&uin=0&fromtag=38

同时在本目录下将歌曲保存了下来:

python下载歌 python爬虫下载歌曲_python 歌曲相似度 音乐推荐_10

用QQ音乐播放器打开后:

python下载歌 python爬虫下载歌曲_json_11

成功播放!

但是该方法只能下载大部分的付费歌曲和免费歌曲,还有少部分的试听歌曲会解析失败,因为官方重构了这部分歌曲的播放页面,需要登陆并且传递用户信息。在此记录一下,解决个问题之后再重新发张帖子。