在没有自己的音乐搜索引擎的时候,却又想让用户可以较为方便的在自己的网站上搜索网络歌曲,在这里使用的是百度的MP3~
换成以前也许很简单,直接抓取网页就可以获取了网络音乐的实际URL。而现在不行了,搜索出来结果后,需要再次点击请求后台生成一个地址,然后那个页面才有真实MP3的URL地址。我使用的方法可能较为被动,如果百度MP3一些规则一旦改动,下面的代码就跑不起来了(在保持现在规则不变的情况下,看上去还是很完美的)
目前我做的是,只搜索第一页的音乐,如果你想实现与百度一致的翻页,可能还需要再花点时间了(不过应该会很简单了)
首先,我使用的开发环境:
操作系统:xp sp3
web服务器:nginx
flash开发IDE:flashDevelop
网页开发工具:Editplus
思路:
网页获取keyword -->传递给flash –> flash通过nginx反向代理请求百度mp3首页的网页内容 –> 回传给javascript
用户点击“试听”听 –> 将百度MP3首页的临时地址传给flash重新再请求一次(也采用nginx的反向代理) –> 获取最终真实地址的网页内容-->回传给javascript -->脚本通过解码函数再得真实的播放地址。
需要注意项:
1、百度的MP3请求地址,做了防盗链,在flash的http请求头里面需要设置host <ip地址>、清空referer、设置不缓存页面(每次重新请求),nginx代码大致如下:
proxy_set_header host '220.181.38.82'; proxy_set_header referer '';
add_header Cache-Control 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0';
add_header Pragma no-cache;
proxy_pass http://220.181.38.82;
2、需要在服务器(本地)放置crossdomain.xml文件(因为flash的安全策略,请求资源时它会请求当前根目录下的crossdomain.xml文件,不符合规则将报安全沙箱错误)
下面测试一下,到底下面的方法得到的真实的URL是否正确:
注意它百度跳到指定的一个IP上,而不是域名,如果nginx里设置域名也是不行的,一定要用IP。
从两张图的对比来看,试验的页面是可以获得百度MP3的真实的网络地址, 项目测试成功。
nginx配置:
代码语言:javascript复制 #VHOST: meteoric.com
server {
listen 80;
server_name meteoric.com
charset utf-8;
access_log off;
ssi on;
ssi_silent_errors on;
location / {
root C:phpApp;
index index.html index.php;
}
location /crossdomain.xml {
alias C:/phpApp/searchMusic/crossdomain.xml;
}
location ~ ^/baidu(/?) {
rewrite .* http://www.baidu.com/ redirect;
}
location ~ ^/m$ {
proxy_set_header host '220.181.43.121';
proxy_set_header referer '';
proxy_pass http://mp3.baidu.com;
}
location ~ ^(. .php)(.*)$ {
root C:phpApp;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include php.conf;
}
}
#VHOST: meteoric2.com
server {
listen 80;
server_name meteoric2.com;
charset utf-8;
location /crossdomain.xml {
alias C:/phpApp/searchMusic/crossdomain.xml;
}
location ~ ^/m$ {
proxy_set_header host '220.181.38.82';
proxy_set_header referer '';
add_header Cache-Control 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0';
add_header Pragma no-cache;
proxy_pass http://220.181.38.82;
}
}
解析百度MP3首页音乐列表脚本:
代码语言:javascript复制if (/<div(?:s )id="songResults"[^>] >[ns]*(<table[Ss]*?</table>)/.test(_data)) {
var table_str = RegExp['$1'];
var tr_reg = /(<tr[^>]*>[Ss] ?</tr>)/;
var tr_str = "";
musicList = [];
while (tr_reg.test(table_str)) {
tr_str = RegExp['$1'];
table_str = table_str.replace(tr_str, "");
if (/<td/.test(tr_str)) {
musicList.push({
'tmpurl' : /<td(?:s )class="second">[^<]*<a(?:s )href="([^"] )/.test(tr_str) ? RegExp['$1'] : "",
'name' : /<td(?:s )class="second">[^>] >([^<] )/.test(tr_str) ? RegExp['$1'] : "",
'singer' : /<td(?:s )class="third">[^>] >[^>] >([^<] )/.test(tr_str) ? RegExp['$1'] : "",
'relurl' : null,
'size' : /<td(?:s )class="seventh">([^<] )/.test(tr_str) ? RegExp['$1'] : "",
'speed' : /<td(?:s )class="ninth">[^>] >(d)</.test(tr_str) ? RegExp['$1']*1 1 : ""
});
}
}
}
解析网络音乐真实URL的核心方法:
代码语言:javascript复制parseMusicURL : function(data) {
if(data) {
/var encurls*=s*"([^"]*)"s*||s "([^"]*)"/.test(data);
var encurl = RegExp['$1'] || RegExp['$2'];
/var song_(d )/.test(data);
var sertim = RegExp['$1'];
if(sertim && encurl) {
return this.decodeMusicURL(sertim, encurl);
} else {
return null;
}
} else {
return null;
}
},
decodeMusicURL : function(_rId, _url) {
var sertim = parseInt(_rId, 10);
var url = _url;
var len = url.length;
var decurl = "";
var asc_arr1 = [], asc_arr2 = [];
var key = sertim % 26;
key = key ? key : 1;
function init(head, bottom, middle){
for (var i = head; i <= bottom; i ) {
asc_arr1[i] = i middle;
asc_arr2[i middle] = i;
}
}
init(0, 9, 48);
init(10, 35, 55);
init(36, 61, 61);
for (var i = 0; i < len; i ) {
var word = url.charAt(i);
if (/[A-Za-z0-9]/.test(word)) {
var pos = asc_arr2[url.charCodeAt(i)] - key;
if (pos < 0)
pos = 62;
word = String.fromCharCode(asc_arr1[pos]);
}
decurl = word;
}
return decurl;
}
代理请求的Flash代码:
代码语言:javascript复制package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.external.ExternalInterface;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.system.System;
/**
* ...
* @author ZhangYi
*/
public class Main extends Sprite
{
private static var CallBack_Fun:String;
public function Main():void
{
if (stage) {
init();
} else {
addEventListener(Event.ADDED_TO_STAGE, init);
}
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
inited();
}
private function inited():void {
if (ExternalInterface.available) {
ExternalInterface.addCallback("loadURL", loadURL);
}
System.useCodePage = true;
var params:Object = root.loaderInfo.parameters;
if (params.initCallback) {
ExternalInterface.call(params.initCallback);
}
}
/**
* 请求指定的地址,获取数据后返回
*
* @param _url
* @param _callback
* @param method
*/
public function loadURL(_url:String, _callback:String = "", method:String = "get"):void {
var req:URLRequest = new URLRequest(_url);
req.method = method == "get" ? URLRequestMethod.GET : "POST";
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, onCompleteHandler);
loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
CallBack_Fun = _callback;
loader.load(req);
}
/**
* 数据加载完成
* @param evt
*/
private function onCompleteHandler(evt:Event):void {
var loader:URLLoader = evt.target as URLLoader;
ExternalInterface.call(CallBack_Fun, loader.data);
}
/**
* 请求时发生IO错误
* @param evt
*/
private function ioErrorHandler(evt:IOErrorEvent):void {
ExternalInterface.call(CallBack_Fun, null, evt.text);
}
}
}
实际运行请求的效果示意图:
获取音乐列表的请求<也就是百度MP3首页的字符--网页源代码>:
请求网络音乐的真实URL时,网页内有一个javascript解码函数:
除nginx外,其它源码(html、css、flash)都将上传打包。不一定非得用nginx,你也可以使用apache,只是我的开发环境中经常用。
↓下载示例