摘要:本问将带你了解微信公众帐号开发之应用实例音乐搜索,希望本文对大家学微信有所帮助。
微信公众平台支持向用户回复音乐消息,用户收到音乐消息后,点击即可播放音乐。通过音乐消息,公众账号可以实现音乐搜索(歌曲点播)功能,即用户输入想听的音乐名称,公众账号返回对应的音乐(歌曲)。
考虑到歌曲名称有重复的情况,用户还可以同时指定歌曲名称、演唱者搜索歌曲。下面就为读者详细介绍歌曲点播功能的实现过程。
音乐消息说明
在微信公众平台开发者文档中提到,向用户回复音乐消息需要构造如下格式的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. }
上述代码的逻辑比较简单,用户发送“歌曲+名称”或者“歌曲+名称@演唱者”就能搜索歌曲,搜索不到时会提示用户,如果发送其他内容回复歌曲点播功能的用法。
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之微信频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号