本帖最后由 wordge 于 2017-8-31 22:07 编辑
前言:
鉴于现在网上没有一个成熟的音乐第三方接口(其原因:音乐版权问题),但是又想在自己的web项目中添加在线音乐怎么办?那下面我们就一起来剖析QQ音乐(y.qq.com)播放音乐时候的请求响应来打造一个自己的在线音乐(搜索、显示、播放)的小应用。本例子仅做学习使用。其中代码部分没有做严谨判断,先实现功能。
一、搜索音乐
1.输入实时匹配推荐歌曲名称
打开chrome浏览器(web前端开发利器之一)、打开QQ音乐网站、打开F12开发者工具
紧接着,我们在搜索框中输入几个字符试试(先别点击查询),看看F12面板中Network有什么变化
我们每输入一个字母就会向QQ音乐后台发送异步请求,下面我们随便点击一个请求来看看具体的请求和响应的内容
我们来分析下请求部分
请求:
url:https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg
参数名 含义
is_xml 0代表获取json数据 1代表获取xml数据 可空:默认为0
format jsonp方式获取(一般跨域会用到) json 可空:默认就是json
key 查询的关键字 非空
g_tk QQ官方的一个加密值,具体没有去研究、先别管 可空
jsonpCallback jsonp的回调函数名称 可空:非jsonp方式调用 经测试中自己的应用中以jsonp调用会报错,其原因是jsonp的回调方法名,其有具体的算法
loginUin 登录身份验证码,如果qq登录后就是你的qq号码 可空
hostUin 主机身份验证码,具体没有去研究、先别管 可空
inCharset 请求字符编码 可空
outCharset 响应字符编码 可空
notice 通知,具体没有去研究、先别管 可空
platform 平台 可空
needNewCode 是否需要新编码 ,具体没有去研究、先别管 可空
响应:
参数 含义
code 状态码
subcode 子编码
data 数据对象
data-->album 搜索匹配推荐专辑数据
data-->mv 搜索匹配推荐MV数据
data-->singer 搜索匹配推荐歌手数据
data-->song 搜索匹配推荐单曲数据,这是我们要的数据
下面我们根据这个url来测试下:
实现思路:
1.创建一个页面,一个输入框
2.输入框监听键盘事件,获取输入框输入内容,做ajax请求到Servlet
3.Servlet中根据页面ajax请求过来的key参数值来调用之前分析好的QQ音乐的推荐匹配地址(这里用的是Jodd.jar来做的http模拟请求,当然也可以使用httpclient.jar等)
4.根据调用QQ音乐推荐匹配地址返回的数据获取itemList的内容,然后返回到info.jsp做一个下拉显示
好,分析得到上面的数据后,下面我们就来做这件的搜索实时匹配推荐歌曲
先来张效果图
2.项目结构(简单的servlet工程)
3.页面代码
[HTML] 纯文本查看 复制代码 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>嘿马音乐</title>
<script type="text/javascript" src="${ pageContext.request.contextPath }/jquery-1.8.3.js"></script>
<script type="text/javascript" src="${ pageContext.request.contextPath }/search.js"></script>
<style type="text/css">
#d1 tr:hover{
background-color: #ddd;
}
</style>
</head>
<body style="min-height: 600px;">
<div style="text-align: center;">
<h1>嘿马♪♫</h1>
<input type="text" id="key" name="key" style="width:400px;height:30px;" placeholder="刚刚好">
<input id="querybtn" type="button" value="嘿一下" style="height: 30px;margin-left: 2px;">
</div>
<div id="d1" style="display:none;position:absolute;top:103px;left:400px;border:1px solid gray;width:400px;height:200px;"></div>
</body>
</html>
4.js部分代码
[JavaScript] 纯文本查看 复制代码 var index = 0;
var oldIndex = 0;
$(function(){
// 为文本框绑定事件:
$("#key").keyup(function(event){
var currentCode = event.keyCode;
if(currentCode==13 && $("#d1 td").size()!=0){
//回车键的时候选中当前 并关闭
$("#d1").hide();
return;
}
// 获得文本框的值:
var word = $(this).val();
// 异步发送请求:
if(currentCode!=38 && currentCode!=40){
if(word != ""){
$.post("/online_music/RecommendMusicServlet",{"key":word},function(data){
if(""!=$.trim(data)){
$("#d1").show().html(data);
$("#d1 td").each(function(){
$(this).click(function(){
$("#key").val($(this).text());
$("#d1").hide();
});
});
}else{
$("#d1").hide();
}
});
}else{
$("#d1").hide();
}
}else{
var size = $("#d1 td").size();
if(size==0){
return;
}
$("#d1 td").eq(index).css("background-color","#ddd");
$("#d1 td").eq(oldIndex).css("background-color","");
$("#key").val($("#d1 td").eq(index).text());
if(currentCode==38){//向上
if(index!=0){
oldIndex = index;
index--;
}else{
oldIndex = index;
index = size-1;
}
}else if(currentCode==40){//向下
if(index>=(size-1)){
oldIndex = index;
index = 0;
}else{
oldIndex = index;
index++;
}
}
}
});
//点击body的时候关闭推荐面板
$("body").click(function(){
$("#d1").hide();
});
$("#key").focus(function(){
var word = $(this).val();
if(word != ""){
$.post("/online_music/RecommendMusicServlet",{"key":word},function(data){
if(""!=$.trim(data)){
$("#d1").show().html(data);
$("#d1 td").each(function(){
$(this).click(function(){
$("#key").val($(this).text());
$("#d1").hide();
});
});
}else{
$("#d1").hide();
}
});
}else{
$("#d1").hide();
}
});
//点击查询按钮“嘿一下”进行模糊查询
$("#querybtn").click(function(){
var word = $("#key").val();
world = $.trim(word);
if(word != ""){
window.open("/online_music/PageSong?w="+word);
}
});
});
5.输入实时推荐匹配servlet
[Java] 纯文本查看 复制代码 package com.net023.servlet;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import jodd.json.JsonParser;
@WebServlet("/RecommendMusicServlet")
public class RecommendMusicServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
// 接收参数:
request.setCharacterEncoding("UTF-8");
String word = request.getParameter("key").trim();
//https://c.y.qq.com/soso/fcgi-bin/client_search_cp?p=3&n=30&w=七里香&format=json
List<Map<String, Object>> itemlist = null;
if(null!=word&&!"".equals(word)){
HttpResponse httpResponse = HttpRequest.get("https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg?is_xml=0&format=json").query("key", word).send();
String bodyText = httpResponse.bodyText();
Map<String, Object> dd = new JsonParser().parse(bodyText);
Map<String, Object> data = (Map<String, Object>) dd.get("data");
Map<String, Object> song = (Map<String, Object>) data.get("song");
if(null!=song){
itemlist = (List<Map<String, Object>>) song.get("itemlist");
}
}
/**
* {
"docid": "3236607256197134876",
"id": "107762070",
"mid": "001hs89m3C5p3g",
"name": "小半",
"singer": "陈粒"
},
*/
request.setAttribute("list", itemlist);
request.getRequestDispatcher("/info.jsp").forward(request, response);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
6.info.jsp
[HTML] 纯文本查看 复制代码 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:if test="${not empty list }">
<table border="0" width="100%">
<c:forEach var="song" items="${ list }">
<tr>
<td id="${song.mid }">${ song.name }</td>
</tr>
</c:forEach>
</table>
</c:if>
二、模糊查询
1.同样我们打开chrome浏览器、F12开发者工具、点击输入框后面的查询按钮、查看Network的请求响应详情
发现会有一个请求,并返回和我们输入查询相关的返回数据(json)、下面我们就来分析下这个请求和响应
歌曲图片根据查看QQ音乐网站的音乐图片查看图片地址为
https://y.gtimg.cn/music/photo_new/T002R300x300M000 + albummid + .jpg
或者
https://y.gtimg.cn/music/photo_new/T002R150x150M000 + albummid + .jpg
我们来分析下请求部分
请求:
url:https://c.y.qq.com/soso/fcgi-bin/client_search_cp
参数名 含义
p 当前请求数据页码
format jsonp方式获取(一般跨域会用到) 、 json
new_json 0 旧json数据结构 1新json数据结构
n 分页每页显示记录条数
w 查询的关键字 非空
...其他参数我们用不到,不再介绍
响应:只介绍我们能用到的
参数 含义
data 返回数据
data-->song 返回当前页的数据
data-->curnum 分页记录大小
data-->curpage 当前页码
data-->totalnum 总共记录数
data-->list
下面我们根据这个url来测试下:
实现思路:
1.输入框输入要查询的歌曲名称、点击查询“嘿一下”
2.根据输入的内容跳转到新开的浏览器访问Servlet
3.Servlet中根据请求过来的w参数值来调用之前分析好的QQ音乐的模糊查询地址(这里用的是Jodd.jar来做的http模拟请求,当然也可以使用httpclient.jar等)
4.根据调用QQ音乐模糊查询返回的数据获取list的内容,然后返回到songs.jsp进行循环遍历展示
当然我们这里没有做实际的分页,下次再继续优化
来张效果图
2.分页模糊查询servlet(PageSong)
3.songs.jsp
[Groovy] 纯文本查看 复制代码 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>搜索结果</title>
</head>
<body>
<ul>
<c:forEach items="${songlist }" var="song">
<c:if test="${not empty song.songmid }">
<li>
<img alt="" src="https://y.gtimg.cn/music/photo_new/T002R150x150M000${song.albummid }.jpg">
<a href="${ pageContext.request.contextPath }/ListenToMusicServlet?songMid=${song.songmid }" target="_blank">#歌曲名称:${song.songname } #歌手:${song.singer[0].name }</a>
</li>
<br/>
<br/>
</c:if>
</c:forEach>
</ul>
</body>
</html>
三、播放音乐
1.点击一个歌曲,点击播放
2.在播放页面打开F12,强制刷新、播放、查看请求响应
经过分析查看,我们可以看到有2条重要的请求
我们先来查看分析第一个勾选的请求
其规则就是之前我们获取list中的songmid作为参数、filename(‘C400’+songmid+‘.m4a’)、guid(任意的几个数字)、cid(固定值205361747)然后作为参数访问https://c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg,然后返回vkey
完整的url为https://c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg?cid=205361747&guid=55&songmid={0}&filename={1}
然后我们来分析第二个勾选的请求url
其规则就是我们之前获取的vkey和之前使用的guid和fromtag固定值66作为参数访问http://dl.stream.qqmusic.qq.com/C400+songmid+.m4a
其完整url为http://dl.stream.qqmusic.qq.com/C400+songmid+.m4a?vkey={0}&fromtag={1}&guid={2}
下面我们就来访问下这两个url
实现思路:
1.根据我们之前模糊查询后,循环遍历的jsp页面上点击链接,带上songmid参数、访问servlet
2.在servlet中根据songmid来访问第一个url来得到vkey,然后再根据vkey来拼接生成最终播放音乐的url,然后重定向
2.ListenToMusicServlet
源码附件...
|