以QQ音乐的单曲页面URL https://y.qq.com/n/yqq/song/000YU69H3N55rZ.html 为例,说明如何解析得到真实的音频地址。
打开该页面地址,是这样的:
点击页面中的播放按钮,会在新页面的播放器里播放该音乐。在新页面中打开开发者工具的网络探测,重新刷新页面,会发现相关请求如下:
https://c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg?g_tk=5381&jsonpCallback=MusicJsonCallback39355320624659207&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0&cid=205361747&callback=MusicJsonCallback39355320624659207&uin=0&songmid=000YU69H3N55rZ&filename=C400000YU69H3N55rZ.m4a&guid=328569683http://dl.stream.qqmusic.qq.com/C400000YU69H3N55rZ.m4a?vkey=927C721D272189FED484EF874A8D646C6B13C20C53277FB99D496DA6804CDF1B64C33A4F049D24FFC4366605DB4755A6E9AB632AA770C09A&guid=328569683&uin=0&fromtag=66
其中,第二个请求就是音频的真实地址,该地址中主要有如下几个部分:
- 文件名:C400000YU69H3N55rZ.m4a
- vkey参数:927C721D272189FED484EF874A8D646C6B13C20C53277FB99D496DA6804CDF1B64C33A4F049D24FFC4366605DB4755A6E9AB632AA770C09A
- guid参数:328569683
- uin参数:0
- fromtag参数:66
第一个请求的响应如下:
MusicJsonCallback39355320624659207({"code":0,"cid":205361747,"data":{"expiration":80400,"items":[{"subcode":0,"songmid":"000YU69H3N55rZ","filename":"C400000YU69H3N55rZ.m4a","vkey":"927C721D272189FED484EF874A8D646C6B13C20C53277FB99D496DA6804CDF1B64C33A4F049D24FFC4366605DB4755A6E9AB632AA770C09A"}]}})
我们发现,vkey参数值就在上述响应内容里面。songmid的值正好出现在单曲页面URL里面,通过字符串拼接可以得到文件名:"C400" + songmid + ".m4a"
只有文件名和vkey是不够的,还需要知道guid参数值是如何得来的,没有guid参数的话请求音频真实地址会返回响应码403。
在js文件 https://y.gtimg.cn/music/portal/js/common/pkg/player_module_b5739d0.js 中,搜索 fcg_music_express_mobile3 可找到如下jsonp请求代码:
r.length > 0 ? MUSIC.jQueryAjax.jsonp({url: location.protocol + "//c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg",data: {cid: 205361747,format: "json",callback: a,uin: g_user.getUin(),songmid: r.join(","),filename: n.join(","),guid: _getGuid()},...}) : t && t()
这段js代码正好写明了请求参数有哪些,guid是通过调用 _getGuid 得到的。_getGuid 方法的代码如下:
function _getGuid() {if (_guid.length > 0)return _guid;var e = MUSIC.cookie.get("pgv_pvid");if (e && e.length > 0)return _guid = e;var t = (new Date).getUTCMilliseconds();return _guid = Math.round(2147483647 * Math.random()) * t % 1e10,document.cookie = "pgv_pvid=" + _guid + "; Expires=Sun, 18 Jan 2038 00:00:00 GMT; PATH=/; DOMAIN=qq.com;",_guid}
由此可见,guid的核心构造代码是如下两行:
var t = (new Date).getUTCMilliseconds()
_guid = Math.round(2147483647 * Math.random()) * t % 1e10
Python代码示例
import re
import json
import time
import random
import requestsdef resolve(url):"""resolve audio url:param url: like 'https://y.qq.com/n/yqq/song/000YU69H3N55rZ.html':return:"""songmid = re.search('/(\w+).html$', url).groups()[0]filename = 'C400' + songmid + '.m4a'guid = int(random.random() * 2147483647) * int(time.time() * 1000) % 10000000000d = {'format': 'json','cid': 205361747,'uin': 0,'songmid': songmid,'filename': filename,'guid': guid,}r = requests.get('https://c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg', params=d, verify=False)vkey = json.loads(r.content)['data']['items'][0]['vkey']audio_url = 'http://dl.stream.qqmusic.qq.com/%s?vkey=%s&guid=%s&uin=0&fromtag=66' % (filename, vkey, guid)return audio_url