使用nginx反向代理获取百度MP3的真实网址

在没有自己的音乐搜索引擎的时候,却又想让用户可以较为方便的在自己的网站上搜索网络歌曲,在这里使用的是百度的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配置:

 #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首页音乐列表脚本:

if (/<div(?:\s+)id=\"songResults\"[^>]+\>[\n\s]*(<table[\S\s]*?\<\/table\>)/.test(_data)) {
			var table_str = RegExp['$1'];
			var tr_reg = /(<tr[^>]*>[\S\s]+?<\/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的核心方法:

parseMusicURL : function(data) {
		if(data) {
			/var encurl\s*=\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代码:

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,只是我的开发环境中经常用。

↓下载示例

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏coding

vagrant极简教程:快速搭建centos7前言vagrant简介基本使用小结

1264
来自专栏菩提树下的杨过

ruby on rails + mysql 开发环境搭建

ror对于初学者来讲,可能最大的困难莫过于开发环境的安装与搭建,今天折腾了大半天基本搞定了,把过程贴在这里,以便初学者少走弯路。 1.安装ruby 1.8.6 ...

3325
来自专栏用户2442861的专栏

当你输入一个网址的时候,实际会发生什么

原文:http://igoro.com/archive/what-really-happens-when-you-navigate-to-a-url/  

1601
来自专栏Java学习123

Linux自动压缩备份目录文件与恢复

首先建立以个空白文档,然后打开编辑成需要的内容,最后保存为*.sh文件 比如我现在Linux 系统 /usr下有个目录soft需要让它每天自动压缩打包到/opt...

4195
来自专栏做全栈攻城狮

C#实现动态网站伪静态,使seo更友好

本教程将使用Visual Studio 2013手把手教你实现webform动态页面的伪静态。本教程配套的C#源码工程可通过我的github下载。地址:http...

1394
来自专栏码匠的流水账

聊聊flink的checkpoint配置

3072
来自专栏BeJavaGod

[群友分享] 第一次安装liunx系统排坑总结

本文来自群友“易水难求”总结,适合新手排坑 第一次安装设置虚拟机爬坑总结 第一坑:使用VMware Workstation 11版本的设备安装 CentOS-...

3977
来自专栏CodeSheep的技术分享

前后端分离实践:基于vue实现网站前台的权限管理

2887
来自专栏PHP在线

在Mac下使用MAMP Pro环境

以前,我使用Windows作为自己的工作系统,后来,改用Mac作为自己的主要工作系统了。 在Windows下,快速搭建*AMP环境,使用xampp或者WAMP之...

7737
来自专栏Linux运维学习之路

zabbix自动发现与自动注册及SNMP监控

自动发现与自动注册 自动发现:zabbix Server主动发现所有客户端,然后将客户端登记自己的小本本上,缺点zabbix server压力山大(网段大,客户...

8678

扫码关注云+社区

领取腾讯云代金券