之前尝试过搜索关键词来获取QQ音乐信息 + 音乐直链。自己试过自建喜欢的音乐列表,并为此花费了较多功夫,也解决了几个小问题。(只是技术交流学习,以后就撤掉了😳
例如,由于播放链接有时效性,故而保存的时候仅保留id,播放的时候根据id来自动获取直链。
又比如,由于是静态网页,直接写死了HTML,新增更改十分费力。现在把歌曲内容信息和展示分开来,直接修改保存json即可。
但还是有些麻烦,每新增N首曲目需要 搜索歌名 -> 打印信息 -> 修改json文件
xN,不太方便。于是,我就想能不能直接更进一步,再爬一下个人的收藏夹
目标
获取QQ音乐指定列表的音乐曲目(分页),并打印输出,格式如下:
{
"title": "A cut in Love (被爱情切割)",
"author": "줄라이 (July)",
"pic": "https://y.gtimg.cn/music/photo_new/T002R300x300M000001mrdzu00xMPx.jpg",
"mid": "004DCwDo41i7ja",
"tag": ["haoting"],
"brief": "Happiness (幸福)-"
},
{
"title": "隐",
"author": "New One Studio",
"pic": "https://y.gtimg.cn/music/photo_new/T002R300x300M0000027mnht4733M5.jpg",
"mid": "001ms6bA1qcrae",
"tag": ["haoting"],
"brief": "隐形守护者OST-《隐形守护者》BGM"
},
{
"title": "aloha heja he-指弹前奏",
"author": "雨人",
"pic": "https://y.gtimg.cn/music/photo_new/T002R300x300M0000034pJDt1PDfyo.jpg",
"mid": "003Gzohl2hl4Or",
"tag": ["haoting"],
"brief": "这段旋律很有感觉"
},
...
解析
在PC Web端登录QQ音乐,发现目标URL如下:(这种工作熟练度上来之后没啥难度…
https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?
type=1
&json=1
&utf8=1
&onlysong=1
&nosign=1
&new_format=1
&song_begin=0
&song_num=10
&ctx=1
&disstid=689219101
&_=1558690697209
&g_tk=7654321
&loginUin=1234567
&hostUin=0
&format=json
&inCharset=utf8
&outCharset=utf-8
¬ice=0
&platform=yqq.json
&needNewCode=0
此时,请求头为:
accept-encoding:gzip, deflate, sdch, br
accept-language:zh-CN,zh;q=0.8
cookie:略
origin:https://y.qq.com
// referer:https://y.qq.com/n/yqq/playlist/{listId}.html
// 上边referer是进入到具体列表,下面那个是登录后的“我喜欢”
referer:https://y.qq.com/portal/profile.html
user-agent:略
- 参数释疑
- URL中disstid即列表的id,很重要,因为量较少,所以就自己人工获取吧~
- URL中song_begin、song_num,分页我就不说了
- URL中g_tk、loginUin,应该跟登录有关。loginUin为QQ号,g_tk未知。
不登录,g_tk=5381&loginUin=0
可以满足需求。 - URL中_即时间
- HEADER中cookie经测试没有影响
实现
以前的HtmlUnit等太重了,这里用的是自己封装的URLConnection,意思到了你懂的( ╯□╰ ))
public void printDesignedJson(int begin, int size, String albumId) {
long time = System.currentTimeMillis();
String g_tk = "5381"; //与user相对应?
String user = "0"; // 未登录,登录后是qq号
String url = "https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=1&nosign=1&new_format=1&ctx=1&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0";
url += String.format("&song_begin=%d&song_num=%d&_=%d&disstid=%s&g_tk=%s&loginUin=%s", begin, size, time, albumId, g_tk, user);
HttpRequestUtil util = new HttpRequestUtil();
HashMap<String, String> header = new HashMap<>();
header.put("accept", "application/json, text/javascript, */*; q=0.01");
header.put("accept-language", "zh-CN,zh;q=0.8");
header.put("origin", "https://y.qq.com");
header.put("referer", String.format("https://y.qq.com/n/yqq/playlist/%s.html", albumId));
header.put("user-agent", "太长,略");
String result = util.getContent(url, header);
System.out.println(result);
JSONArray musicList = new JSONObject(result).getJSONArray("songlist");
StringBuilder sb = new StringBuilder();
for(int i = 0; i < musicList.length(); i++) {
JSONObject json = musicList.getJSONObject(i);
JSONObject album = json.getJSONObject("album");
String albumPic = String.format("https://y.gtimg.cn/music/photo_new/T002R300x300M000%s.jpg", album.getString("mid"));
sb.append("{\r\n");
formatJson(sb, "title", json.getString("title"), false);
formatJson(sb, "author", json.getJSONArray("singer").getJSONObject(0).getString("title"), false);
formatJson(sb, "pic", albumPic, false);
formatJson(sb, "mid", json.getString("mid"), false);
sb.append(" \"tag\": [\"haoting\"],\r\n");
formatJson(sb, "brief", album.getString("title") + "-" + album.getString("subtitle"), true);
sb.append("},\r\n");
}
System.out.println(sb.toString());
}
private void formatJson(StringBuilder sb, String key, String value, boolean isLast) {
sb.append(" \"")
.append(key)
.append("\": \"")
.append(value);
if(isLast) {
sb.append("\"\r\n");
}else {
sb.append("\",\r\n");
}
}