微信公众帐号开发之应用实例音乐搜索
白羽 2018-06-26 来源 :网络 阅读 1024 评论 0

摘要:本问将带你了解微信公众帐号开发之应用实例音乐搜索,希望本文对大家学微信有所帮助。


 


微信公众平台支持向用户回复音乐消息,用户收到音乐消息后,点击即可播放音乐。通过音乐消息,公众账号可以实现音乐搜索(歌曲点播)功能,即用户输入想听的音乐名称,公众账号返回对应的音乐(歌曲)。

 

考虑到歌曲名称有重复的情况,用户还可以同时指定歌曲名称、演唱者搜索歌曲。下面就为读者详细介绍歌曲点播功能的实现过程。

 

音乐消息说明

在微信公众平台开发者文档中提到,向用户回复音乐消息需要构造如下格式的XML数据。

[html] view plain copy
1. <xml>  
2.     <ToUserName><![CDATA[toUser]]></ToUserName>  
3.     <FromUserName><![CDATA[fromUser]]></FromUserName>  
4.     <CreateTime>12345678</CreateTime>  
5.     <MsgType><![CDATA[music]]></MsgType>  
6.     <Music>  
7.         <Title><![CDATA[TITLE]]></Title>  
8.         <Description><![CDATA[DESCRIPTION]]></Description>  
9.         <MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>  
10.         <HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>  
11.         <ThumbMediaId><![CDATA[media_id]]></ThumbMediaId>  
12.     </Music>  
13. </xml>

上面XML中,需要注意的是<Music>节点中的参数,说明如下:

1)参数Title:标题,本例中可以设置为歌曲名称;

2)参数Description:描述,本例中可以设置为歌曲的演唱者;

3)参数MusicUrl:普通品质的音乐链接;

4)参数HQMusicUrl:高品质的音乐链接,在WIFI环境下会优先使用该链接播放音乐;

5)参数ThumbMediaId:缩略图的媒体ID,通过上传多媒体文件获得;它指向的是一张图片,最终会作为音乐消息左侧绿色方形区域的背景图片。
上述5个参数中,最为重要的是MusicUrl和HQMusicUrl,这也是本文的重点,如何根据歌曲名称获得歌曲的链接。如果读者只能得到歌曲的一个链接,可以将MusicUrl和HQMusicUrl设置成一样的。至于ThumbMediaId参数,必须是通过微信认证的服务号才能得到,普通的服务号与订阅号可以忽略该参数,也就是说,在回复给微信服务器的XML中可以不包含ThumbMediaId参数。

 

百度音乐搜索API介绍

上面提到,给用户回复音乐消息最关键在于如何根据歌曲名称获得歌曲的链接,我们必须找一个现成的音乐搜索API,除非读者自己有音乐服务器,或者只向用户回复固定的几首音乐。百度有一个非公开的音乐搜索API,之所以说非公开,是因为笔者没有在百度官网的任何地方看到有关该API的介绍,但这并不影响读者对本例的学习,我们仍然可以调用它。百度音乐搜索API的请求地址如下:

[html] view plain copy

1. //box.zhangmen.baidu.com/x?op=12&count=1&title=TITLE$$AUTHOR$$$$  

//box.zhangmen.baidu.com为百度音乐盒的首页地址,上面的链接中不用管参数op和count,重点关注TITLE和AUTHOR,TITLE表示歌曲名称,AUTHOR表示演唱者,AUTHOR可以为空,参数TITLE和AUTHOR需要进行URL编码(UTF-8或GB2312均可)。例如,要搜索歌曲零点乐队的“相信自己”,可以像下面这样:

[html] view plain copy

1. // GB2312编码的音乐搜索链接  

2. //box.zhangmen.baidu.com/x?op=12&count=1&title=%CF%E0%D0%C5%D7%D4%BC%BA$$%C1%E3%B5%E3%C0%D6%B6%D3$$$$  

3. // UTF-8编码的音乐搜索链接  

4. //box.zhangmen.baidu.com/x?op=12&count=1&title=%E7%9B%B8%E4%BF%A1%E8%87%AA%E5%B7%B1$$%E9%9B%B6%E7%82%B9%E4%B9%90%E9%98%9F$$$$  

通过浏览器访问上面的地址,返回的是如下格式的XML数据:

[html] view plain copy

1. <result>  

2.     <count>1</count>  

3.     <url>  

4.         <encode>  

5.             <![CDATA[//zhangmenshiting.baidu.com/data2/music/44277542/ZWZla2xra2pfn6NndK6ap5WXcJVob5puZ2trbWprmnBjZ2xolpeZa2drZmWZmZmdl2hjZWhvnWlpYmRtZmltcGplZFqin5t1YWBobW5qcGxia2NmZ2twbzE$]]>  

6.         </encode>  

7.         <decode>  

8.             <![CDATA[44277542.mp3?xcode=a39c6698955c82594aab36931dcbef60139f180191368931&mid=0.59949419022597]]>  

9.         </decode>  

10.         <type>8</type>  

11.         <lrcid>64644</lrcid>  

12.         <flag>1</flag>  

13.     </url>  

14.     <durl>  

15.         <encode>  

16.             <![CDATA[//zhangmenshiting2.baidu.com/data2/music/44277530/ZWZla2xramhfn6NndK6ap5WXcJVob5puZ2trbWprmnBjZ2xolpeZa2drZmWZmZmdl2hjaGhvnZ5qlGRpbpedamJla1qin5t1YWBobW5qcGxia2NmZ2twbzE$]]>  

17.         </encode>  

18.         <decode>  

19.             <![CDATA[44277530.mp3?xcode=a39c6698955c82594aab36931dcbef60439ff9b159af2138&mid=0.59949419022597]]>  

20.         </decode>  

21.         <type>8</type>  

22.         <lrcid>64644</lrcid>  

23.         <flag>1</flag>  

24.     </durl>  

25.     <p2p>  

26.         <hash>022bc0fbf66cd19bea96db49634419dc2600393f</hash>  

27.         <url>  

28.             <![CDATA[ ]]>  

29.         </url>  

30.         <type>mp3</type>  

31.         <size>5236902</size>  

32.         <bitrate>192</bitrate>  

33.     </p2p>  

34. </result>  

返回结果中的主要参数说明如下:

1)<count> 表示搜索到的音乐数;

2)<url>中包含了普通品质的音乐链接,<durl>中包含了高品质音乐的链接;

3)<encode>中包含了加密后的音乐链接,实际上只是对音乐名称进行了加密,<decode>中包含了解密后的音乐名称。因此,要获取音乐的链接就需要重点分析<encode>和<decode>中的内容,下面会专门为读者进行介绍。

4)<type>表示音乐文件的类型,如rm、wma、mp3等;

5)<lrcid>是歌词的ID,<url>中的歌词ID为64644,那么如何得到歌词呢?本例并不关心歌词,只是附带提一下。歌词的地址如下:

[html] view plain copy

1. //xxxx.com/bdlrc/646/64644.lrc  

其中,//xxxxxxxx.com/bdlrc/是固定值;646为歌词所在目录名,计算方法为歌词ID(64644)除以100,取整数部分;64644.lrc是歌词文件名。

下面来看如何从<encode>和<decode>中得到音乐链接。为了便于说明,笔者将上面搜索结果中的<url>和<durl>部分抽取出来,并进行了标注,如下图所示。

 

上图中,1和2拼接起来是普通品质音乐的链接,3和4拼接起来是高品质音乐的链接。也就是说,普通品质和高品质的音乐链接如下:

[html] view plain copy

1. // 普通品质音乐链接  

2. //zhangmenshiting.baidu.com/data2/music/44277542/44277542.mp3?xcode=a39c6698955c82594aab36931dcbef60139f180191368931  

3. // 高品质音乐链接  

4. //zhangmenshiting2.baidu.com/data2/music/44277530/44277530.mp3?xcode=a39c6698955c82594aab36931dcbef60439ff9b159af2138  

参数xcode可以理解为随机验证码,每次搜索得到的值都不一样,如果不带该参数会报未授权异常“401 Authorization Required”。需要注意的是,xcode是有时间限制的,超过限制再访问链接会报异常:{"Error":{"code":"2","Message":"object not exists","LogId":"3456414897"}}。在xcode有效的前提下,通过浏览器访问上面的音乐链接,会提示下载音乐。

 

编程调用百度音乐搜索API

知道如何从API返回结果中得到音乐链接后,就可以编写程序来实现了。笔者将发送HTTP请求、URL编码、解析XML等操作全部封装在BaiduMusicService类中,该类的代码如下:

[java] view plain copy
1. import java.io.InputStream;  
2. import java.io.UnsupportedEncodingException;  
3. import java.net.HttpURLConnection;  
4. import java.net.URL;  
5. import java.util.List;  
6.   
7. import org.dom4j.Document;  
8. import org.dom4j.Element;  
9. import org.dom4j.io.SAXReader;  
10.   
11. import org.liufeng.course.message.resp.Music;  
12.   
13. /** 
14.  * 百度音乐搜索API操作类 
15.  *  
16.  * @author liufeng 
17.  * @date 2013-12-09 
18.  */  
19. public class BaiduMusicService {  
20.     /** 
21.      * 根据名称和作者搜索音乐 
22.      *  
23.      * @param musicTitle 音乐名称 
24.      * @param musicAuthor 音乐作者 
25.      * @return Music 
26.      */  
27.     public static Music searchMusic(String musicTitle, String musicAuthor) {  
28.         // 百度音乐搜索地址  
29.         String requestUrl = "//box.zhangmen.baidu.com/x?op=12&count=1&title={TITLE}$${AUTHOR}$$$$";  
30.         // 对音乐名称、作者进URL编码  
31.         requestUrl = requestUrl.replace("{TITLE}", urlEncodeUTF8(musicTitle));  
32.         requestUrl = requestUrl.replace("{AUTHOR}", urlEncodeUTF8(musicAuthor));  
33.         // 处理名称、作者中间的空格  
34.         requestUrl = requestUrl.replaceAll("\\+", "%20");  
35.   
36.         // 查询并获取返回结果  
37.         InputStream inputStream = httpRequest(requestUrl);  
38.         // 从返回结果中解析出Music  
39.         Music music = parseMusic(inputStream);  
40.   
41.         // 如果music不为null,设置标题和描述  
42.         if (null != music) {  
43.             music.setTitle(musicTitle);  
44.             // 如果作者不为"",将描述设置为作者  
45.             if (!"".equals(musicAuthor))  
46.                 music.setDescription(musicAuthor);  
47.             else  
48.                 music.setDescription("来自百度音乐");  
49.         }  
50.         return music;  
51.     }  
52.   
53.     /** 
54.      * UTF-8编码 
55.      *  
56.      * @param source 
57.      * @return 
58.      */  
59.     private static String urlEncodeUTF8(String source) {  
60.         String result = source;  
61.         try {  
62.             result = java.net.URLEncoder.encode(source, "UTF-8");  
63.         } catch (UnsupportedEncodingException e) {  
64.             e.printStackTrace();  
65.         }  
66.         return result;  
67.     }  
68.   
69.     /** 
70.      * 发送http请求取得返回的输入流 
71.      *  
72.      * @param requestUrl 请求地址 
73.      * @return InputStream 
74.      */  
75.     private static InputStream httpRequest(String requestUrl) {  
76.         InputStream inputStream = null;  
77.         try {  
78.             URL url = new URL(requestUrl);  
79.             HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();  
80.             httpUrlConn.setDoInput(true);  
81.             httpUrlConn.setRequestMethod("GET");  
82.             httpUrlConn.connect();  
83.             // 获得返回的输入流  
84.             inputStream = httpUrlConn.getInputStream();  
85.         } catch (Exception e) {  
86.             e.printStackTrace();  
87.         }  
88.         return inputStream;  
89.     }  
90.   
91.     /** 
92.      * 解析音乐参数 
93.      *  
94.      * @param inputStream 百度音乐搜索API返回的输入流 
95.      * @return Music 
96.      */  
97.     @SuppressWarnings("unchecked")  
98.     private static Music parseMusic(InputStream inputStream) {  
99.         Music music = null;  
100.         try {  
101.             // 使用dom4j解析xml字符串  
102.             SAXReader reader = new SAXReader();  
103.             Document document = reader.read(inputStream);  
104.             // 得到xml根元素  
105.             Element root = document.getRootElement();  
106.             // count表示搜索到的歌曲数  
107.             String count = root.element("count").getText();  
108.             // 当搜索到的歌曲数大于0时  
109.             if (!"0".equals(count)) {  
110.                 // 普通品质  
111.                 List<Element> urlList = root.elements("url");  
112.                 // 高品质  
113.                 List<Element> durlList = root.elements("durl");  
114.   
115.                 // 普通品质的encode、decode  
116.                 String urlEncode = urlList.get(0).element("encode").getText();  
117.                 String urlDecode = urlList.get(0).element("decode").getText();  
118.                 // 普通品质音乐的URL  
119.                 String url = urlEncode.substring(0, urlEncode.lastIndexOf("/") + 1) + urlDecode;  
120.                 if (-1 != urlDecode.lastIndexOf("&"))  
121.                     url = urlEncode.substring(0, urlEncode.lastIndexOf("/") + 1) + urlDecode.substring(0, urlDecode.lastIndexOf("&"));  
122.   
123.                 // 默认情况下,高音质音乐的URL 等于 普通品质音乐的URL  
124.                 String durl = url;  
125.   
126.                 // 判断高品质节点是否存在  
127.                 Element durlElement = durlList.get(0).element("encode");  
128.                 if (null != durlElement) {  
129.                     // 高品质的encode、decode  
130.                     String durlEncode = durlList.get(0).element("encode").getText();  
131.                     String durlDecode = durlList.get(0).element("decode").getText();  
132.                     // 高品质音乐的URL  
133.                     durl = durlEncode.substring(0, durlEncode.lastIndexOf("/") + 1) + durlDecode;  
134.                     if (-1 != durlDecode.lastIndexOf("&"))  
135.                         durl = durlEncode.substring(0, durlEncode.lastIndexOf("/") + 1) + durlDecode.substring(0, durlDecode.lastIndexOf("&"));  
136.                 }  
137.                 music = new Music();  
138.                 // 设置普通品质音乐链接  
139.                 music.setMusicUrl(url);  
140.                 // 设置高品质音乐链接  
141.                 music.setHQMusicUrl(durl);  
142.             }  
143.         } catch (Exception e) {  
144.             e.printStackTrace();  
145.         }  
146.         return music;  
147.     }  
148.   
149.     // 测试方法  
150.     public static void main(String[] args) {  
151.         Music music = searchMusic("相信自己", "零点乐队");  
152.         System.out.println("音乐名称:" + music.getTitle());  
153.         System.out.println("音乐描述:" + music.getDescription());  
154.         System.out.println("普通品质链接:" + music.getMusicUrl());  
155.         System.out.println("高品质链接:" + music.getHQMusicUrl());  
156.     }  
157. }  
下面对代码进行简单的说明:
1)代码中的Music类是对音乐消息的封装(不包括ThumbMediaId参数),读者可以在本系列教程的第4篇中找到该类的定义;
2)运行上述代码需要引入dom4j的JAR包,笔者使用的是dom4j-1.6.1.jar;
3)searchMusic()方法是提供给外部调用的,在CoreService类中会调用该方法获得音乐消息需要的Music相关的4个参数(Title、Description、MusicUrl和HQMusicUrl);
4)parseMusic()方法用于解析XML,读者可以结合代码中的注释和之前对XML的分析进行理解,这里就不再赘述了。
5)116行、127行中的get(0)表示返回多条音乐结果时默认取第一条。

 

公众账号后台的实现

在公众账号后台的CoreService类中,需要对用户发送的文本消息进行判断,如果是以“歌曲”两个字开头,就认为用户是在使用“歌曲点播”功能,此时需要对“歌曲”两个字之后的内容进行判断,如果包含“@”符号,就表示需要按演唱者搜索,否则不指定演唱者。CoreService类的完整代码如下:

[java] view plain copy
1. package org.liufeng.course.service;  
2.   
3. import java.util.Date;  
4. import java.util.Map;  
5. import javax.servlet.http.HttpServletRequest;  
6. import org.liufeng.course.message.resp.Music;  
7. import org.liufeng.course.message.resp.MusicMessage;  
8. import org.liufeng.course.message.resp.TextMessage;  
9. import org.liufeng.course.util.MessageUtil;  
10.   
11. /** 
12.  * 核心服务类 
13.  *  
14.  * @author liufeng 
15.  * @date 2013-12-10 
16.  */  
17. public class CoreService {  
18.     /** 
19.      * 处理微信发来的请求 
20.      *  
21.      * @param request 
22.      * @return 
23.      */  
24.     public static String processRequest(HttpServletRequest request) {  
25.         // 返回给微信服务器的xml消息  
26.         String respXml = null;  
27.         // 文本消息内容  
28.         String respContent = null;  
29.         try {  
30.             // xml请求解析  
31.             Map<String, String> requestMap = MessageUtil.parseXml(request);  
32.             // 发送方帐号(open_id)  
33.             String fromUserName = requestMap.get("FromUserName");  
34.             // 公众帐号  
35.             String toUserName = requestMap.get("ToUserName");  
36.             // 消息类型  
37.             String msgType = requestMap.get("MsgType");  
38.   
39.             // 回复文本消息  
40.             TextMessage textMessage = new TextMessage();  
41.             textMessage.setToUserName(fromUserName);  
42.             textMessage.setFromUserName(toUserName);  
43.             textMessage.setCreateTime(new Date().getTime());  
44.             textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);  
45.   
46.             // 文本消息  
47.             if (MessageUtil.REQ_MESSAGE_TYPE_TEXT.equals(msgType)) {  
48.                 // 文本消息内容  
49.                 String content = requestMap.get("Content").trim();  
50.                 // 如果以“歌曲”2个字开头  
51.                 if (content.startsWith("歌曲")) {  
52.                     // 将歌曲2个字及歌曲后面的+、空格、-等特殊符号去掉  
53.                     String keyWord = content.replaceAll("^歌曲[\\+ ~!@#%^-_=]?", "");  
54.                     // 如果歌曲名称为空  
55.                     if ("".equals(keyWord)) {  
56.                         respContent = getUsage();  
57.                     } else {  
58.                         String[] kwArr = keyWord.split("@");  
59.                         // 歌曲名称  
60.                         String musicTitle = kwArr[0];  
61.                         // 演唱者默认为空  
62.                         String musicAuthor = "";  
63.                         if (2 == kwArr.length)  
64.                             musicAuthor = kwArr[1];  
65.   
66.                         // 搜索音乐  
67.                         Music music = BaiduMusicService.searchMusic(musicTitle, musicAuthor);  
68.                         // 未搜索到音乐  
69.                         if (null == music) {  
70.                             respContent = "对不起,没有找到你想听的歌曲<" + musicTitle + ">。";  
71.                         } else {  
72.                             // 音乐消息  
73.                             MusicMessage musicMessage = new MusicMessage();  
74.                             musicMessage.setToUserName(fromUserName);  
75.                             musicMessage.setFromUserName(toUserName);  
76.                             musicMessage.setCreateTime(new Date().getTime());  
77.                             musicMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_MUSIC);  
78.                             musicMessage.setMusic(music);  
79.                             respXml = MessageUtil.musicMessageToXml(musicMessage);  
80.                         }  
81.                     }  
82.                 }  
83.             }  
84.             // 未搜索到音乐时返回使用指南  
85.             if (null == respXml) {  
86.                 if (null == respContent)  
87.                     respContent = getUsage();  
88.                 textMessage.setContent(respContent);  
89.                 respXml = MessageUtil.textMessageToXml(textMessage);  
90.             }  
91.         } catch (Exception e) {  
92.             e.printStackTrace();  
93.         }  
94.         return respXml;  
95.     }  
96.   
97.     /** 
98.      * 歌曲点播使用指南 
99.      *  
100.      * @return 
101.      */  
102.     public static String getUsage() {  
103.         StringBuffer buffer = new StringBuffer();  
104.         buffer.append("歌曲点播操作指南").append("\n\n");  
105.         buffer.append("回复:歌曲+歌名").append("\n");  
106.         buffer.append("例如:歌曲存在").append("\n");  
107.         buffer.append("或者:歌曲存在@汪峰").append("\n\n");  
108.         buffer.append("回复“?”显示主菜单");  
109.         return buffer.toString();  
110.     }  
111. }

上述代码的逻辑比较简单,用户发送“歌曲+名称”或者“歌曲+名称@演唱者”就能搜索歌曲,搜索不到时会提示用户,如果发送其他内容回复歌曲点播功能的用法。

 

 


本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之微信频道!

 


本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程