1、歌曲信息接口分析
通过QQ音乐搜索指定歌手或歌曲,对页面数据资源进行分析
按下f12开发者工具,点击network,找寻页面数据接口
将该接口地址复制到postman工具下进行请求,查看获取到的数据
尝试删除一些参数,看看是否还是可以请求到指定的数据
只留下一下三个参数:
发现还是可以请求到指定数据
优化之后的接口:
https://c.y.qq.com/soso/fcgi-bin/client_search_cp?p=1&n=10&w=李荣浩
2、歌曲播放资源分析
点击播放歌曲,打开开发者工具中的media,找到播放的链接
复制此链接到浏览器输入栏
这就是我们最终需要的数据资源,我们需要拿到每个歌曲的播放地址,就可以实现歌曲的下载了
分析该链接地址
http://111.202.98.149/amobile.music.tc.qq.com/C400002TRY4a3zfxC8.m4a?guid=703417739&vkey=7B7DC7DDC36BCC5F0B4277260D53FE3D376C742CFB9B1206D29F6E1D137B7B3EDC5FB46C5DC244F53D372DFA85D31B9E542950C98826E44B&uin=741&fromtag=66
主要参数:
还有就是C400与.m4a之间的一串字符也是变化的,经过查找发现为media_mid
该js文件下有该链接的拼接的规则
经过验证,可以将之前获取的http://111.202.98.149/amobile.music.tc.qq.com替换为http://dl.stream.qqmusic.qq.com/然后再进行拼接,这个js里面显示的为C100,但是经过验证,直接用C100是访问不了的,应该经过处理,真正访问还是以C400进行的。后面拼接StrMediaMid值或者Songmid值,然后.m4a?fromtage…
但是后来就发现这个思路还是不对,因为该链接中还有其他参数,上面这个分析只能证明C400后面拼接的字符为StrMediaMid值或者Songmid值,但是还有vkey值并没有解决。所以就有下面这个接口作为最终拼接的结果。
3、播放链接拼接的后缀url接口
对该url进行解码:
https://u.y.qq.com/cgi-bin/musicu.fcg?-=getplaysongvkey13880444756036847&g_tk=5381&loginUin=1094013271&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0&data={“req”:{“module”:“CDN.SrfCdnDispatchServer”,“method”:“GetCdnDispatch”,“param”:{“guid”:“5900616959”,“calltype”:0,“userip”:""}},“req_0”:{“module”:“vkey.GetVkeyServer”,“method”:“CgiGetVkey”,“param”:{“guid”:“5900616959”,“songmid”:[“000sU9jC3bN2dY”],“songtype”:[0],“uin”:“1094013271”,“loginflag”:1,“platform”:“20”}},“comm”:{“uin”:1094013271,“format”:“json”,“ct”:24,“cv”:0}}
只需要留着后面的data参数字典即可,然后只需要更改songmid即可拿到不同歌曲的vkey值,这个接口有一个参数purl,是直接拼接好的,只需要拼接在域名地址后面即可正常的访问歌曲播放链接
1、请求歌曲信息接口,并提取songmid和media_mid以及歌曲名称
callback({
"code": 0,
"data": {
"keyword": "李荣浩",
"priority": 0,
"qc": [],
"semantic": {
"curnum": 0,
"curpage": 1,
"list": [],
"totalnum": 0
},
"song": {
"curnum": 10,
"curpage": 1,
"list": [
{
"albumid": 4801194,
"albummid": "004QnEHc3zjC7J",
"albumname": "耳朵",
"albumname_hilight": "耳朵",
"alertid": 23,
"belongCD": 0,
"cdIdx": 7,
"chinesesinger": 0,
"docid": "5480908422984286978",
"grp": [],
"interval": 279,
"isonly": 1,
"lyric": "",
"lyric_hilight": "",
"media_mid": "002TRY4a3zfxC8",
"msgid": 16,
"newStatus": 2,
"nt": 3360238371,
"pay": {
"payalbum": 0,
"payalbumprice": 0,
"paydownload": 1,
"payinfo": 1,
"payplay": 0,
"paytrackmouth": 1,
"paytrackprice": 200
},
"preview": {
"trybegin": 86935,
"tryend": 119757,
"trysize": 0
},
"pubtime": 1539705600,
"pure": 0,
"singer": [
{
"id": 60505,
"mid": "000aHmbL2aPXWH",
"name": "李荣浩",
"name_hilight": "李荣浩"
}
],
"size128": 4470267,
"size320": 11174951,
"sizeape": 0,
"sizeflac": 31202312,
"sizeogg": 6303035,
"songid": 218224537,
"songmid": "004DXFlC0nsTCZ",
"songname": "年少有为",
"songname_hilight": "年少有为",
"strMediaMid": "002TRY4a3zfxC8",
"stream": 1,
"switch": 636675,
"t": 1,
"tag": 11,
"type": 0,
"ver": 0,
"vid": "y0027sobcrh"
},
{
"albumid": 1276189,
"albummid": "001fi1zG0EjU2u",
"albumname": "有理想",
"albumname_hilight": "有理想",
"alertid": 23,
"belongCD": 0,
"cdIdx": 7,
"chinesesinger": 0,
"docid": "2871525826680312030",
"grp": [],
"interval": 312,
"isonly": 1,
"lyric": "《何以笙箫默》电影片尾曲",
"lyric_hilight": "《何以笙箫默》电影片尾曲",
"media_mid": "002l3MPE1qoTk9",
"msgid": 16,
"newStatus": 2,
"nt": 2725380024,
"pay": {
"payalbum": 0,
"payalbumprice": 0,
"paydownload": 1,
"payinfo": 1,
"payplay": 0,
"paytrackmouth": 1,
"paytrackprice": 200
},
"preview": {
"trybegin": 0,
"tryend": 0,
"trysize": 0
},
"pubtime": 1453392000,
"pure": 0,
"singer": [
{
"id": 60505,
"mid": "000aHmbL2aPXWH",
"name": "李荣浩",
"name_hilight": "李荣浩"
}
],
"size128": 5009016,
"size320": 12522251,
"sizeape": 0,
"sizeflac": 35731519,
"sizeogg": 6986156,
"songid": 102367085,
"songmid": "000edAg12jLBrN",
"songname": "不将就",
"songname_hilight": "不将就",
"strMediaMid": "002l3MPE1qoTk9",
"stream": 1,
"switch": 636675,
"t": 1,
"tag": 11,
"type": 0,
"ver": 0,
"vid": "a001738g7ov"
},
{
"albumid": 435269,
"albummid": "004AhJHV3slLjN",
"albumname": "模特",
"albumname_hilight": "模特",
"alertid": 23,
"belongCD": 0,
"cdIdx": 2,
"chinesesinger": 0,
"docid": "8279061032672769869",
"grp": [],
"interval": 306,
"isonly": 1,
"lyric": "",
"lyric_hilight": "",
"media_mid": "001A5fWh0Mkc4D",
"msgid": 16,
"newStatus": 2,
"nt": 3024286708,
"pay": {
"payalbum": 0,
"payalbumprice": 0,
"paydownload": 1,
"payinfo": 1,
"payplay": 0,
"paytrackmouth": 1,
"paytrackprice": 200
},
"preview": {
"trybegin": 0,
"tryend": 0,
"trysize": 0
},
"pubtime": 1379347200,
"pure": 0,
"singer": [
{
"id": 60505,
"mid": "000aHmbL2aPXWH",
"name": "李荣浩",
"name_hilight": "李荣浩"
}
],
"size128": 4901179,
"size320": 12252663,
"sizeape": 0,
"sizeflac": 35388063,
"sizeogg": 7222207,
"songid": 5016169,
"songmid": "002FRBul05dgjC",
"songname": "模特",
"songname_hilight": "模特",
"strMediaMid": "001A5fWh0Mkc4D",
"stream": 1,
"switch": 636675,
"t": 1,
"tag": 11,
"type": 0,
"ver": 0,
"vid": "n0031d236tz"
},
{
"albumid": 422762,
"albummid": "001LP8hk0a6pOp",
"albumname": "小黄",
"albumname_hilight": "小黄",
"alertid": 11,
"belongCD": 0,
"cdIdx": 1,
"chinesesinger": 0,
"docid": "2091987920690858858",
"grp": [],
"interval": 318,
"isonly": 0,
"lyric": "",
"lyric_hilight": "",
"media_mid": "004GCHEm08YZpW",
"msgid": 0,
"newStatus": 2,
"nt": 2447288168,
"pay": {
"payalbum": 0,
"payalbumprice": 0,
"paydownload": 0,
"payinfo": 0,
"payplay": 0,
"paytrackmouth": 0,
"paytrackprice": 0
},
"preview": {
"trybegin": 0,
"tryend": 0,
"trysize": 0
},
"pubtime": 1283961600,
"pure": 0,
"singer": [
{
"id": 60505,
"mid": "000aHmbL2aPXWH",
"name": "李荣浩",
"name_hilight": "李荣浩"
}
],
"size128": 5101799,
"size320": 12754214,
"sizeape": 0,
"sizeflac": 0,
"sizeogg": 6681078,
"songid": 102069682,
"songmid": "000sU9jC3bN2dY",
"songname": "老街",
"songname_hilight": "老街",
"strMediaMid": "004GCHEm08YZpW",
"stream": 1,
"switch": 606007,
"t": 1,
"tag": 11,
"type": 0,
"ver": 0,
"vid": ""
},
{
"albumid": 435269,
"albummid": "004AhJHV3slLjN",
"albumname": "模特",
"albumname_hilight": "模特",
"alertid": 23,
"belongCD": 0,
"cdIdx": 1,
"chinesesinger": 0,
"docid": "1269178546751535043",
"grp": [],
"interval": 273,
"isonly": 1,
"lyric": "",
"lyric_hilight": "",
"media_mid": "004CN4BA1FB6rC",
"msgid": 16,
"newStatus": 2,
"nt": 1447943207,
"pay": {
"payalbum": 0,
"payalbumprice": 0,
"paydownload": 1,
"payinfo": 1,
"payplay": 0,
"paytrackmouth": 1,
"paytrackprice": 200
},
"preview": {
"trybegin": 0,
"tryend": 0,
"trysize": 0
},
"pubtime": 1379347200,
"pure": 0,
"singer": [
{
"id": 60505,
"mid": "000aHmbL2aPXWH",
"name": "李荣浩",
"name_hilight": "李荣浩"
}
],
"size128": 4376222,
"size320": 10940271,
"sizeape": 0,
"sizeflac": 30671792,
"sizeogg": 6958416,
"songid": 5016168,
"songmid": "000rMFLS0ZnngN",
"songname": "李白",
"songname_hilight": "李白",
"strMediaMid": "004CN4BA1FB6rC",
"stream": 1,
"switch": 636675,
"t": 1,
"tag": 11,
"type": 0,
"ver": 0,
"vid": "k0027se8wtv"
},
{
"albumid": 3018400,
"albummid": "003PTZBu0IXqg2",
"albumname": "嗯",
"albumname_hilight": "嗯",
"alertid": 23,
"belongCD": 0,
"cdIdx": 8,
"chinesesinger": 0,
"docid": "14117649544002956417",
"grp": [],
"interval": 294,
"isonly": 1,
"lyric": "",
"lyric_hilight": "",
"media_mid": "003mfhdv3xm3jF",
"msgid": 16,
"newStatus": 2,
"nt": 495105184,
"pay": {
"payalbum": 0,
"payalbumprice": 0,
"paydownload": 1,
"payinfo": 1,
"payplay": 0,
"paytrackmouth": 1,
"paytrackprice": 200
},
"preview": {
"trybegin": 0,
"tryend": 0,
"trysize": 0
},
"pubtime": 1510848000,
"pure": 0,
"singer": [
{
"id": 60505,
"mid": "000aHmbL2aPXWH",
"name": "李荣浩",
"name_hilight": "李荣浩"
}
],
"size128": 4712259,
"size320": 11780367,
"sizeape": 0,
"sizeflac": 31727539,
"sizeogg": 6586203,
"songid": 209340068,
"songmid": "000ByvB6495lpZ",
"songname": "戒烟",
"songname_hilight": "戒烟",
"strMediaMid": "003mfhdv3xm3jF",
"stream": 1,
"switch": 636675,
"t": 1,
"tag": 11,
"type": 0,
"ver": 0,
"vid": "p0025rbpwso"
},
{
"albumid": 805873,
"albummid": "001FOctH2oGoAx",
"albumname": "李荣浩",
"albumname_hilight": "李荣浩",
"alertid": 23,
"belongCD": 0,
"cdIdx": 1,
"chinesesinger": 0,
"docid": "10343688124534745464",
"grp": [],
"interval": 261,
"isonly": 1,
"lyric": "",
"lyric_hilight": "",
"media_mid": "001j9q6k3nN0tD",
"msgid": 16,
"newStatus": 2,
"nt": 630893275,
"pay": {
"payalbum": 0,
"payalbumprice": 0,
"paydownload": 1,
"payinfo": 1,
"payplay": 0,
"paytrackmouth": 1,
"paytrackprice": 200
},
"preview": {
"trybegin": 0,
"tryend": 0,
"trysize": 0
},
"pubtime": 1417104000,
"pure": 0,
"singer": [
{
"id": 60505,
"mid": "000aHmbL2aPXWH",
"name": "李荣浩",
"name_hilight": "李荣浩"
}
],
"size128": 4181877,
"size320": 10454400,
"sizeape": 0,
"sizeflac": 27681238,
"sizeogg": 5796103,
"songid": 9106121,
"songmid": "000f6ltd2wJ5cP",
"songname": "喜剧之王",
"songname_hilight": "喜剧之王",
"strMediaMid": "001j9q6k3nN0tD",
"stream": 1,
"switch": 636675,
"t": 1,
"tag": 11,
"type": 0,
"ver": 0,
"vid": "i00158trdpn"
},
{
"albumid": 4801194,
"albummid": "004QnEHc3zjC7J",
"albumname": "耳朵",
"albumname_hilight": "耳朵",
"alertid": 23,
"belongCD": 0,
"cdIdx": 6,
"chinesesinger": 0,
"docid": "4008237768922141803",
"grp": [],
"interval": 241,
"isonly": 1,
"lyric": "",
"lyric_hilight": "",
"media_mid": "001dPKD40OUxFz",
"msgid": 16,
"newStatus": 2,
"nt": 2904443209,
"pay": {
"payalbum": 0,
"payalbumprice": 0,
"paydownload": 1,
"payinfo": 1,
"payplay": 0,
"paytrackmouth": 1,
"paytrackprice": 200
},
"preview": {
"trybegin": 46199,
"tryend": 78479,
"trysize": 0
},
"pubtime": 1539705600,
"pure": 0,
"singer": [
{
"id": 60505,
"mid": "000aHmbL2aPXWH",
"name": "李荣浩",
"name_hilight": "李荣浩"
}
],
"size128": 3861297,
"size320": 9652957,
"sizeape": 0,
"sizeflac": 0,
"sizeogg": 5211680,
"songid": 218224536,
"songmid": "001dPKD40OUxFz",
"songname": "耳朵",
"songname_hilight": "耳朵",
"strMediaMid": "001dPKD40OUxFz",
"stream": 1,
"switch": 636675,
"t": 1,
"tag": 11,
"type": 0,
"ver": 0,
"vid": "x00279avaof"
},
{
"albumid": 1276189,
"albummid": "001fi1zG0EjU2u",
"albumname": "有理想",
"albumname_hilight": "有理想",
"alertid": 23,
"belongCD": 0,
"cdIdx": 4,
"chinesesinger": 0,
"docid": "13990893781114472264",
"grp": [],
"interval": 284,
"isonly": 1,
"lyric": "",
"lyric_hilight": "",
"media_mid": "000LbvRN0WK7Rl",
"msgid": 16,
"newStatus": 2,
"nt": 3643846322,
"pay": {
"payalbum": 0,
"payalbumprice": 0,
"paydownload": 1,
"payinfo": 1,
"payplay": 0,
"paytrackmouth": 1,
"paytrackprice": 200
},
"preview": {
"trybegin": 0,
"tryend": 0,
"trysize": 0
},
"pubtime": 1453392000,
"pure": 0,
"singer": [
{
"id": 60505,
"mid": "000aHmbL2aPXWH",
"name": "李荣浩",
"name_hilight": "李荣浩"
}
],
"size128": 4548845,
"size320": 11371820,
"sizeape": 0,
"sizeflac": 32763885,
"sizeogg": 6439443,
"songid": 105526949,
"songmid": "001xBUtF3rgn7O",
"songname": "爸爸妈妈",
"songname_hilight": "爸爸妈妈",
"strMediaMid": "000LbvRN0WK7Rl",
"stream": 1,
"switch": 636675,
"t": 1,
"tag": 11,
"type": 0,
"ver": 0,
"vid": "s0019nmbrrx"
},
{
"albumid": 461367,
"albummid": "0029sjwR42NOu9",
"albumname": "作曲家",
"albumname_hilight": "作曲家",
"alertid": 23,
"belongCD": 0,
"cdIdx": 1,
"chinesesinger": 0,
"docid": "1069283592772426233",
"grp": [],
"interval": 227,
"isonly": 1,
"lyric": "",
"lyric_hilight": "",
"media_mid": "001xPNn04SwIzn",
"msgid": 16,
"newStatus": 2,
"nt": 698035760,
"pay": {
"payalbum": 0,
"payalbumprice": 0,
"paydownload": 1,
"payinfo": 1,
"payplay": 0,
"paytrackmouth": 1,
"paytrackprice": 200
},
"preview": {
"trybegin": 0,
"tryend": 0,
"trysize": 0
},
"pubtime": 1395590400,
"pure": 0,
"singer": [
{
"id": 60505,
"mid": "000aHmbL2aPXWH",
"name": "李荣浩",
"name_hilight": "李荣浩"
}
],
"size128": 3642290,
"size320": 9105435,
"sizeape": 0,
"sizeflac": 25385287,
"sizeogg": 5427882,
"songid": 5243873,
"songmid": "0012HIms0X4Yoc",
"songname": "作曲家",
"songname_hilight": "作曲家",
"strMediaMid": "001xPNn04SwIzn",
"stream": 1,
"switch": 636675,
"t": 1,
"tag": 11,
"type": 0,
"ver": 0,
"vid": "b0015ppqqip"
}
],
"totalnum": 565
},
"tab": 0,
"taglist": [],
"totaltime": 0,
"zhida": {
"chinesesinger": 0,
"type": 0
}
},
"message": "",
"notice": "",
"subcode": 0,
"time": 1569222049,
"tips": ""
})
代码提取数据:
import json
import requests
headers = {
'Origin': 'https://y.qq.com',
'Referer': 'https://y.qq.com/portal/search.html',
'Sec-Fetch-Mode': 'cors',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'
}
def get_music_info():
name = input('请输入歌手或歌曲:')
page = input('请输入页码:')
num = input('请输入当前页码需要返回的数据条数:')
url = f'https://c.y.qq.com/soso/fcgi-bin/client_search_cp?p={page}&n={num}&w={name}'
response = requests.get(url,headers=headers).text
# 将其切分为json字符串形式
music_json = response[9:-1]
# json转字典
music_data = json.loads(music_json)
# print(music_data)
music_list = music_data['data']['song']['list']
for music in music_list:
music_name = music['songname']
singer_name = music['singer'][0]['name']
songmid = music['songmid']
media_mid = music['media_mid']
print(music_name,singer_name,songmid,media_mid)
if __name__ == '__main__':
get_music_info()
2、请求vkey接口,获取到完整的purl,进行完整播放url拼接
import json
import requests
headers = {
'Origin': 'https://y.qq.com',
'Referer': 'https://y.qq.com/portal/search.html',
'Sec-Fetch-Mode': 'cors',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'
}
def get_music_info():
music_info_list = []
name = input('请输入歌手或歌曲:')
page = input('请输入页码:')
num = input('请输入当前页码需要返回的数据条数:')
url = f'https://c.y.qq.com/soso/fcgi-bin/client_search_cp?p={page}&n={num}&w={name}'
response = requests.get(url,headers=headers).text
# 将其切分为json字符串形式
music_json = response[9:-1]
# json转字典
music_data = json.loads(music_json)
# print(music_data)
music_list = music_data['data']['song']['list']
for music in music_list:
music_name = music['songname']
singer_name = music['singer'][0]['name']
songmid = music['songmid']
media_mid = music['media_mid']
music_info_list.append((music_name,singer_name,songmid,media_mid))
# print(music_name,singer_name,songmid,media_mid)
return music_info_list
def get_purl(music_info_list):
# 提取songid
for music in music_info_list:
music_name = music[0]
singer_name = music[1]
songmid = music[2]
# media_mid = music[3]
# 这里uid 可以不传
url = 'https://u.y.qq.com/cgi-bin/musicu.fcg?data={"req":{"module":"CDN.SrfCdnDispatchServer","method":"GetCdnDispatch","param":{"guid":"703417739","calltype":0,"userip":""}},"req_0":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"703417739","songmid":["%s"],"songtype":[0],"uin":"","loginflag":1,"platform":"20"}},"comm":{"uin":"","format":"json","ct":24,"cv":0}}'%songmid
response = requests.get(url,headers=headers).json()
purl = response['req_0']['data']['midurlinfo'][0]['purl']
full_media_url = 'http://dl.stream.qqmusic.qq.com/' + purl
print(music_name,singer_name,full_media_url)
if __name__ == '__main__':
music_info_list = get_music_info()
get_purl(music_info_list)
import os
import json
import requests
headers = {
'Origin': 'https://y.qq.com',
'Referer': 'https://y.qq.com/portal/search.html',
'Sec-Fetch-Mode': 'cors',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'
}
def get_music_info():
music_info_list = []
name = input('请输入歌手或歌曲:')
page = input('请输入页码:')
num = input('请输入当前页码需要返回的数据条数:')
url = f'https://c.y.qq.com/soso/fcgi-bin/client_search_cp?p={page}&n={num}&w={name}'
response = requests.get(url,headers=headers).text
# 将其切分为json字符串形式
music_json = response[9:-1]
# json转字典
music_data = json.loads(music_json)
# print(music_data)
music_list = music_data['data']['song']['list']
for music in music_list:
music_name = music['songname']
singer_name = music['singer'][0]['name']
songmid = music['songmid']
media_mid = music['media_mid']
music_info_list.append((music_name,singer_name,songmid,media_mid))
# print(music_name,singer_name,songmid,media_mid)
return music_info_list
def get_purl(music_info_list):
music_data = []
# 提取songid
for music in music_info_list:
music_name = music[0]
singer_name = music[1]
songmid = music[2]
# media_mid = music[3]
# 这里uid 可以不传
url = 'https://u.y.qq.com/cgi-bin/musicu.fcg?data={"req":{"module":"CDN.SrfCdnDispatchServer","method":"GetCdnDispatch","param":{"guid":"703417739","calltype":0,"userip":""}},"req_0":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"703417739","songmid":["%s"],"songtype":[0],"uin":"","loginflag":1,"platform":"20"}},"comm":{"uin":"","format":"json","ct":24,"cv":0}}'%songmid
response = requests.get(url,headers=headers).json()
purl = response['req_0']['data']['midurlinfo'][0]['purl']
full_media_url = 'http://dl.stream.qqmusic.qq.com/' + purl
# print(music_name,singer_name,full_media_url)
music_data.append(
{
'music_name': music_name,
'singer_name': singer_name,
'full_media_url': full_media_url
}
)
return music_data
def save_music_mp3(music_data):
if not os.path.exists('歌曲下载'):
os.mkdir('歌曲下载')
for music in music_data:
music_name = music['music_name']
singer_name = music['singer_name']
full_url = music['full_media_url']
music_response = requests.get(full_url,headers=headers).content
with open('歌曲下载/%s-%s.mp3'%(music_name,singer_name),'wb')as fp:
fp.write(music_response)
print('[%s]保存成功!'%music_name)
if __name__ == '__main__':
music_info_list = get_music_info()
music_data = get_purl(music_info_list)
save_music_mp3(music_data)