本文仅供学习交流使用,请勿不当使用。
首先下载音乐肯定得先找要下载的目标音乐啊,兄弟我自认为自己还算是个乐观的人,所以平常比较少听“网抑云”
啦,于是就自然的打开了QQ音乐的官网,如下:
大致看了看,然后我选择了分类歌单,因为里面的歌单稍微多一点:
来到如下页面,首先找一个歌单进去,这里就选择第二个吧,看起来播放量高一点:
随便点开一首歌,我点开了你,好不好?
,可恶,竟然强迫我登录,好吧好吧,登录就登录,登录完毕后点击歌曲如下:
首先大眼一看,浏览器里面的网址肯定不是歌曲的下载url
啊,我们想下载歌曲肯定得找到歌曲的下载url
,怎么找?按F12
,如下(没有数据记得刷新下页面):
OK,一看最后一个竟然是3.3MB
,不用想,数据肯定在这个里面,点开:
看如图蓝色横条中明明白白的写着:Content-Type: audio/mp4
,那这个就是下载url
了,复制Request URL
粘贴到浏览器中,如下:
得,就是他,那么requests.get()
一下这首歌就下载下来了,OK完事收工。
这时候应该可能有读者不满意了:“你TM在逗我?这么下载一首歌谁不会,老哥要一键下载几百首才行!”
不要慌不要慌嘛,这就来告诉大家怎么把首页那些歌单里面的歌全部下载下来!(VIP的除外,你懂的。。。)
既然想实现批量下载,首先肯定是来观察歌曲的下载url
有什么特征啊,来看看这首歌的Request URL
:
https://isure.stream.qqmusic.qq.com/C400003Frk3c0M7j5N.m4a?guid=5091581860&vkey=E306AEA443CA993C6007CC8E6E8E46AE83C4A665EF3843FB90480C0203B98A08448DCD6DDA534F2E25C821E86C955456597666DE344E0C90&uin=4166&fromtag=66
再来一首用来对比:
https://ws.stream.qqmusic.qq.com/C400001BxqJH2rf6uG.m4a?guid=5091581860&vkey=746525FC77CAB20350FC334CABA1BD3C0C2C50FC0B821A848403D0FFED2DF7E06390CE0CA93AFAAB97ECB18B46694624E2AEE671375A515C&uin=4166&fromtag=66
对比一看我们发现,在C
之前的域名是相同的,guid
是相同的,uin
是相同的,fromtag
是相同的,不同的就只有C到.m4a之间
和vkey
不一样了,那只要搞到这俩参数,下载歌就没啥问题了。那我们找找它们在哪,找啊找啊找朋友,找啊找啊找朋友,哎呀,突然找到一个巨好朋友
:
如图,蓝色条框是purl
参数,啊啊啊啊,这个参数直接就是域名之后的所有东西啊,包含了那两个不一样的vkey和C到.m4a之间
的字符串了。得,那我们要是能找到这个请求的特征,发送这个请求就能得到purl
了。找到purl
就能下载音乐了。
来看看这个请求:
https://u.y.qq.com/cgi-bin/musics.fcg?-=getplaysongvkey11865704078518058&g_tk=652661168&sign=zzanqm3iok901lfce97ee26f87ec8b50b4ffa60462bdef&loginUin=2428022854&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0&data=%7B%22req%22%3A%7B%22module%22%3A%22CDN.SrfCdnDispatchServer%22%2C%22method%22%3A%22GetCdnDispatch%22%2C%22param%22%3A%7B%22guid%22%3A%225091581860%22%2C%22calltype%22%3A0%2C%22userip%22%3A%22%22%7D%7D%2C%22req_0%22%3A%7B%22module%22%3A%22vkey.GetVkeyServer%22%2C%22method%22%3A%22CgiGetVkey%22%2C%22param%22%3A%7B%22guid%22%3A%225091581860%22%2C%22songmid%22%3A%5B%22002xdDqK3GkHVO%22%5D%2C%22songtype%22%3A%5B0%5D%2C%22uin%22%3A%222428022854%22%2C%22loginflag%22%3A1%2C%22platform%22%3A%2220%22%7D%7D%2C%22comm%22%3A%7B%22uin%22%3A2428022854%2C%22format%22%3A%22json%22%2C%22ct%22%3A24%2C%22cv%22%3A0%7D%7D
这…相信一些同学看到这些直接会骂街吧哈哈哈。后面那一串带百分号的参数是什么鬼!
注意看右下角Query String Parameters
,这里其实就是请求后面跟的参数了,第一个带:(冒号)
,我们不用管,然后你再打开另一首歌的这个参数界面,对比对比,我们发现只有sign
标志和最后的data
里面的songmid
是不一样的,OK,再来找sign
和songmid
,找到之后就能知道purl
就能下载歌曲了!
简单分析一下,这个songmid
顾名思义就是歌曲的标识符,我们肯定不能在这个播放页找了,首先我们得找到一群songmid
才能下载一批歌曲啊,那哪里可能有一群songmid
?动动脑子想想,当然是歌单页
!歌单页里面才有不止一首歌,它很有可能包含所有歌曲的songmid
!,OK,来到这儿:
…,又是一个惊喜,简单对比下就可以发现,图中的mid
就是之前要找的songmid
,关键还是**这里面有119首歌的信息!!**而实际上的网页里面只有10首歌,它是这么提示的:查看更多内容,请下载客户端
。不得不说,“它们”真聪明啊!
然后就是找那个sign
了,找啊找啊找啊找,找啊找啊找啊找,最后,我找到脑子爆炸了,然而并没有找到。气急败坏的我就没附带那个参数给浏览器发了个请求,…没想到仍然返回了我们需要的数据。。。又是个惊喜。果然今天1024好运不断吗?(同志们你们看到的这一段话实际中我花了好长好长时间。。。)
OK,到这里我们有了歌单的url
,这个直接复制就可,就能下载这119
首歌了,才119首,说不定还有要VIP的,也没几首嘛,这怎么能满足!,走,想办法能把首页所有歌单的url
特征找到就好了,我们好像开启了套娃之旅。
来看看这个歌单url
的请求参数:
然后再换一个歌单对比对比:
于是我们又发现了!
只有一个dissid
参数不一样,看来这个标签就是各个歌单的标识符了!,那哪里包含很多歌单的dissid
呢?当然是首页!只有首页才显示了各个歌单的信息!该去首页找好朋友了,有了前面的竟然,继续套娃套下去我们也不怕了,这次很容易就找到了包含各个歌单dissid
的地方了:
看!总共19
个歌单,每一个歌单的dissid
都工工整整的在那里,好像一早就知道我们要来在等我们似的。
OK,19个歌单,假如每个几十首歌曲,也起码几百首了吧,够听一阵子了,就不再套娃了,当然同志们有想法的可以去排行榜电台之类的都看看。。。
到这里就完工了,捋一捋,首先我们可以通过首页的url
,来得到各个歌单的dissid
,再通过各个歌单的dissid
得到每个歌单下的songmid
,再通过songmid
来得到各个歌曲的purl
,然后再简单经过url
拼接就能下载歌曲了!
话不多说,直接上代码:
# @Author : goldsunC
# @Date : 2020/10/24/
# @Email : [email protected]
# @Blog : https://blog.csdn.net/weixin_45634606
import requests
import json
import os.path
def parse_dissid():
"""
得到歌单的dissid
"""
url = 'https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg?'
data = {
'picmid': '1',
'rnd': '',
'g_tk_new_20200303':'398486200',
'g_tk': '398486200',
'loginUin': '你的QQ账号',
'hostUin': '0',
'format': 'json',
'inCharset': 'utf8',
'outCharset': 'utf-8',
'notice': '0',
'platform': 'yqq.json',
'needNewCode': '0',
'categoryId': '10000000',
'sortId': '5',
'sin': '0',
'ein': '19'
}
headers = {
'accept':'application/json,text/javascript,*/*;q=0.01',
'accept-encoding':'gzip,deflate,br',
'accept-language': "zh-CN,zh;q=0.9",
'referer': 'https://y.qq.com/portal/playlist.html',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36',
'cookie':'你的cookie'
}
try:
response = requests.get(url=url , params=data , headers = headers)
dissid_list = []
diss_json = json.loads(response.text).get('data').get('list')
for diss_id in diss_json:
id = diss_id.get('dissid')
dissid_list.append(id)
return dissid_list
except requests.RequestException:
print('请求得到dissid有问题')
def parse_mid(dissid):
url = 'https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?'
data = {
'type':'1',
'json':'1',
'utf8':'1',
'onlysong':'0',
'new_format':'1',
'disstid':dissid,
'g_tk_new_20200303':'398486200',
'g_tk':'398486200',
'loginUin':'你的QQ账号',
'hostUin':'0',
'format':'json',
'inCharset':'utf8',
'outCharset':'utf-8',
'notice':'0',
'platform':'yqq.json',
'needNewCode':'0'
}
headers = {
'accept': 'application/json,text/javascript,*/*;q=0.01',
'accept-encoding': 'gzip,deflate,br',
'accept-language': "zh-CN,zh;q=0.9",
'cookie':'你的cookie',
'origin':'https://y.qq.com',
'referer': 'https://y.qq.com/',
'sec-fetch-dest':'empty',
'sec-fetch-mode':'cors',
'sec-fetch-site':'same-site',
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
}
try:
response = requests.get(url=url , params=data , headers = headers)
mid_list = []
name_list = []
mid_json = json.loads(response.text).get('cdlist')[0].get('songlist')
for media_mid in mid_json:
mid = media_mid.get('mid')
mid_list.append(mid)
name = media_mid.get('name')
name_list.append(name)
return zip(mid_list,name_list)
except requests.RequestException:
print('请求得到media_mid出问题')
def parse_purl(mid):
url = 'https://u.y.qq.com/cgi-bin/musicu.fcg?'
data = {
'g_tk': '5381',
'loginUin': '0',
'hostUin': '0',
'format': 'json',
'inCharset': 'utf8',
'outCharset': 'utf - 8',
'notice': '0',
'platform': 'yqq.json',
'needNewCode': '0',
'data': '{"req_0":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"8874756349","songmid":["' +
mid + '"],"songtype":[0],"uin":"0","loginflag":1,"platform":"20"}},"comm":{"uin":0,"format":"json","ct":24,"cv":0}}',
}
headers= {
'Referer': 'https://y.qq.com/portal/player.html',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}
try:
response = requests.get(url,params=data,headers=headers)
purl_json = json.loads(response.text).get('req_0').get('data').get('midurlinfo')[0].get('purl')
return purl_json
except requests.RequestException:
print('请求得到purl出错')
def download(purl,name):
base = 'https://ws.stream.qqmusic.qq.com/'
url = base+purl
headers= {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
}
response = requests.get(url,headers = headers)
with open('./QQQQ/{}.mp3'.format(name),'wb') as f:
f.write(response.content)
if __name__ == '__main__':
dissid_list = parse_dissid()
mid_name_list = []
# 将所有歌曲全部下载,因为数量多且程序未优化,会很慢,不过也可以尝试
# for dissid in dissid_list:
# mid_name = parse_mid(dissid)
# mid_name_list.append(mid_name)
#如下是只下载第一个歌单里面的歌曲
mid_name = parse_mid(dissid_list[0])
mid_name_list.append(mid_name)
purl_names_list = []
for mid in mid_name_list:
for i in mid:
purl = parse_purl(i[0])
purl_names = [purl,i[1]]
purl_names_list.append(purl_names)
print(list(purl_names))
if not os.path.exists('QQQQ'):
os.mkdir('QQQQ')
for purl_name in purl_names_list:
purl_and_name = list(purl_name)
download(purl_and_name[0],purl_and_name[1])
需要注意的是,程序中的cookie
和账号需要改成你自己的,另外因为程序比较简单,没有使用多线程、Scrapy等,下载会比较慢,之后找时间把程序重构优化一下。
如果文章对你有帮助,还请点个赞哦~
不说了,我听歌曲了