基于curl封装一个爬虫

<?php  
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 5.1.6 or newer
 *
 * @package		CodeIgniter
 * @author		ExpressionEngine Dev Team
 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
 * @license		http://codeigniter.com/user_guide/license.html
 * @link		http://codeigniter.com
 * @since		Version 1.0
 * @filesource
 */

// ------------------------------------------------------------------------

class Crawler {
	private $_url;
	private $_handle;
	//多线程处理时一些变量
	private $_batch_config = array(
											'handle_array' => array(),
											'map_array' => array()
									);
	private $_curl_option;
	private $_single_or_multi = TRUE; //TRUE表示是单个curl句柄,FALSE表示一组批处理curl句柄(多线程操作)

	function __construct() {
		$this->_curl_option = array(
				CURLOPT_TIMEOUT => 60,
				CURLOPT_FOLLOWLOCATION => TRUE,
				CURLOPT_RETURNTRANSFER => TRUE,
				CURLOPT_HEADER => TRUE,
				CURLOPT_NOSIGNAL => TRUE,
		);
	}

	public function deal($url) {
		$this->set_url($url);
		$this->init_handle();

		return $this->get_response();
	}

	public function set_url($url) {
		$this->_url = $url;
		$this->_single_or_multi = is_array($url) ? FALSE : TRUE;
	}

	public function set_curl_option($option) {
		$this->_curl_option = $option;
	}

	public function init_handle() {
		if(is_array($this->_url)) {
			$this->_handle = curl_multi_init();
			foreach ($this->_url as $id=>$s_url) {
				$ch = curl_init($s_url);
				$this->_batch_config['handle_array'][] = $ch;
				curl_setopt_array($ch, $this->_curl_option);

				curl_multi_add_handle($this->_handle, $ch);
				$this->_batch_config['map_array'][(string)$ch] = $id;
// 				print_r($ch);
// 				echo "\n" . (string)$ch . "\n";
			}
			print_r($this->_batch_config);
		}
		else {
			$this->_handle = curl_init($this->_url);
			curl_setopt_array($this->_handle, $this->_curl_option);
		}
	}

	public function get_response() {
		if($this->_single_or_multi) {
			$response = curl_exec($this->_handle);
			curl_close($this->_handle);

			return $response;
		}
		else {
			$responses = array();
			do
			{
				while(($code = curl_multi_exec($this->_handle , $active)) == CURLM_CALL_MULTI_PERFORM);
				if($code != CURLM_OK)
				{
					break;
				}

				// a request was just completed -- find out which one
				while($done = curl_multi_info_read($this->_handle))
				{

					// get the info and content returned on the request
					$content = curl_multi_getcontent($done['handle']);
					$responses[$this->_batch_config['map_array'][(string) $done['handle']]] = $content;//$this->parseHead($content);

					// remove the curl handle that just completed
					curl_multi_remove_handle($this->_handle , $done['handle']);
					curl_close($done['handle']);
				}

				// Block for data in / output; error handling is done by
				// curl_multi_exec
				if($active > 0)
				{
					curl_multi_select($this->_handle , 0.5);
				}
			}
			while($active);

			return $responses;
		}
	}
}

今天在CI下写一个爬虫库,此爬虫支持单url抓取和多线程批量url抓取,以及自定义正则匹配所需要的内容。

未完成

===========2013.05.06============

完成多线程部分,类代码待完善

Apache虚拟主机中NameVirtualHost指令

说明 为一个基于域名的虚拟主机指定一个IP地址(和端口)
语法 NameVirtualHost addr[:port]
作用域 server config
状态 核心(C)
模块 core

如果您要配置基于域名的虚拟主机,NameVirtualHost指令就是您必须的指令之一。

尽管addr参数可以使用主机名,但建议您还是使用IP地址。比如:

NameVirtualHost 111.22.33.44

使用NameVirtualHost指令,您可以指定一个基于域名的虚拟主机将使用哪个IP地址来接受请求。在一个防火墙或是其它代理接受了请求并把它转到服务器所在的另外一个IP地址上的情况下,您必须指定伺服请求的机器物理界面上的IP地址。如果您对于多个地址使用了多个基于域名的虚拟主机,您应该为每个地址使用这个指令。

注意

“主服务器”和任何其它”_default_“服务器都不会伺服发送到NameVirtualHost IP地址的请求。(除非您指定了NameVirtualHost,但没有为这个地址指定任何VirtualHost)。

另外,您还可以为您使用的基于域名的虚拟主机指定一个端口号。比如:

NameVirtualHost 111.22.33.44:8080

IPv6地址必须封装在一对方括号内,如下例所示:

NameVirtualHost [2001:db8::a00:20ff:fea7:ccea]:8080

为了接受所有界面的请求,您可以使用”*“:

NameVirtualHost *

<VirtualHost>指令的参数

请注意,<VirtualHost>指令的参数必须与NameVirtualHost指令的参数完全匹配。

NameVirtualHost 1.2.3.4
<VirtualHost 1.2.3.4>
# ...
</VirtualHost>

[来源]

ubuntu和centos下关于lamp的一些常规操作

本文中centos环境是在root账户下操作的

安装Apache, MySQL, PHP

sudo apt-get install apache2 mysql-server-5.0 php5 libapache2-mod-php5 php5-mysql ...(需要别的模块自己添加)
yum -y install httpd php mysql mysql-server php-mysql httpd-manual mod_ssl mod_perl mod_auth_mysql php-mcrypt php-gd php-xml php-mbstring php-ldap php-pear php-xmlrpc mysql-connector-odbc mysql-devel libdbi-dbd-mysql...(相关模块自己取舍,需要再加)

重启apache服务(其他服务类似)

sudo /etc/init.d/apache2 restart(或sudo service apache2 restart)
/etc/init.d/httpd restart(或 service httpd restart)

Apache相关配置

路径:

/etc/apache2/apache2.conf
/etc/httpd/conf/httpd.conf

虚拟主机配置

可以直接写到上述配置文件中,也可以单独写,我在centos中是直接写到apache主配置文件的,在ubuntu中,配置文件同路径中有两个文件夹,分别是

sites-available和sites-enabled,具体作用不言而喻(同目录中的mods-available和mods-enable文件夹也类似)

在sites-available中配好相关虚拟主机,我一般单独文件一个主机,然后运行

sudo a2ensite 虚拟主机配置文件名

然后restart或者reload apache服务都可以使配置生效。

用户和组

centos下一般是apache:apache

ubuntu下一般是www-data:www-data

Ubuntu 12.04下新建快捷方式

[Desktop Entry]
Name=ZendStudio
Exec=/usr/local/ZendStudio/ZendStudio
Icon=/usr/local/ZendStudio/icon.xpm
Terminal=false
StartupNotify=true
Type=Application
Categories=Development;

把程序包放在/usr/local下面

XML相关技术探讨

xml数据经常作为一种数据格式广泛运用在web接口中。“可扩展标记语言(XML)被称为标记语言和基于文本的数据存储格式,这要看对谁来说。它是标准通用标记语言(SGML)的一个子集,采用文本方式应用和描述信息的树状结构。XML 是很多语言/格式的基础,如 Really Simple Syndication (RSS)、Mozilla 的 XML User Interface Language (XUL)、Macromedia 的 Maximum eXperience Markup Language (MXML)、Microsoft 的 eXtensible Application Markup Language (XAML) 以及开放源代码的 Java XML UI Markup Language (XAMJ)。”——IBM developworks
在php中对xml数据的处理有很多种方式,本文会梳理下xml相关知识以及对利用PHP对XML数据的几种解析方式做一个小小的总结:对XML的解析主要有树和流两种方式,各有不同的适用场景.用树的方式解析,需要将整个XML加载到内存。树型解析的例子包括DOM和SimpleXML(基于libxml2)。流解析不需要将整个文档加载到内存中。是按需加载,只能访问当前解析的节点,适用于解析大型xml文件。流解析器的例子包括XMLReader和SAX。
示例xml文件:xmltest.xml

<?xml version="1.0" encoding="UTF-8"?>
<SeachData>
	<item id="first">
		<title id="ftitle"><![CDATA[樱桃红]]></title>
		<subtitle><![CDATA[38集电视剧 在线观看]]></subtitle>
		<url><![CDATA[http://tv.2345.com/detail/18922.html]]></url>
		<tag>1</tag>
		<describ><![CDATA[主演:宋小宝 沈春阳 赵本山]]></describ>
		<type>2</type>
	</item>
	<item id="second">
		<title id="stitle"><![CDATA[贤妻]]></title>
		<subtitle><![CDATA[30集电视剧 在线观看]]></subtitle>
		<url><![CDATA[http://tv.2345.com/detail/19716.html]]></url>
		<tag>1</tag>
		<describ><![CDATA[主演:刘涛 保剑锋 谢祖武]]></describ>
		<type>2</type>
	</item>
</SeachData>

树解析:

1.DOM解析

<?php
$xml = new DOMDocument();
$xml->load('xmltest.xml');
$xml->preserveWhiteSpace = false;
$data = $xml->getElementsByTagName('item');

foreach( $data as $k=>$v )
{
	//$v是DOMElement
	echo $data->item($k)->getAttribute('id')."\n";
	//DOMNodeList 
	$titleNode = $v->getElementsByTagName('title');
	foreach ($titleNode as $k2 => $v2)
	{
		echo $v2->nodeValue. "\n";
	}
}

输出:

first
樱桃红
second
贤妻

DOM和XPATH解析

<?php
$xml = new DOMDocument;
$xml->preserveWhiteSpace = false;
$xml->load('xmltest.xml');

$xpath = new DOMXPath($xml);

// We start from the root element
$query = '//item/title';

//DOMNodeList Object
$entries = $xpath->query($query);

foreach ($entries as $k=>$v) {
	echo $v->nodeValue . "\n";
	echo "Found {$v->nextSibling->nodeValue}," . " by {$v->nodeValue}\n";
}

输出:

樱桃红
Found 38集电视剧 在线观看, by 樱桃红
贤妻
Found 30集电视剧 在线观看, by 贤妻

是不是很像js解析html,^_^

XPATH资料

 

2.SimpleXML解析

<?php
$xmlString = file_get_contents('xmltest.xml');
$xml = new SimpleXMLElement($xmlString);
echo $xml->item[0]->title;

输出:

樱桃红
38集电视剧 在线观看

在此值得一提的有三个函数:

simplexml_import_dom(将一个dom节点转化为SimpleXMLElement对象,相反功能的一个函数dom_import_simplexml)
simplexml_load_file(加载xml文件,得到SimpleXMLElement对象)
simplexml_load_string(加载格式化的xml字符串,得到SimpleXMLElement对象)

 

 

流解析
1.XMLReader解析

nodeType属性 取值 说明
1 代表标签的开始
15 代表标签的介绍
14 代表空标签体
3 代表标签体内容

<?php
$reader = new XMLReader();
$reader->open("testxml.xml");
while ($reader->read()) {
	switch ($reader->nodeType) {
		case (XMLREADER::ELEMENT):
			if ($reader->localName == 'item') {
				$domElement = $reader->expand();
				$dom = new DOMDocument();
				$domNode = $dom->importNode($domElement, TRUE);
				$simpleXml = simplexml_import_dom($domNode);
				echo $simpleXml->title . "\n";
				echo $simpleXml->subtitle . "\n";
				echo $simpleXml->url . "\n";
				echo "------------\n";
			}
	}
}

输出:

樱桃红
38集电视剧 在线观看
http://tv.2345.com/detail/18922.html
------------
贤妻
30集电视剧 在线观看
http://tv.2345.com/detail/19716.html
------------

 

2.SAX解析Simple API for XML (SAX),这个用的少了,大文件用XMLReader配合前面的DOMDocument和SimpleXML基本可以搞定一切

Simple API for XML (SAX) 
<?php
$g_items = array ();
$g_elem = null;
function startElement($parser, $name, $attrs) {
	global $g_items, $g_elem;
	if ($name == 'item')
		$g_items [] = array ();
	$g_elem = $name;
}
function endElement($parser, $name) {
	global $g_elem;
	$g_elem = null;
}
function textData($parser, $text) {
	global $g_items, $g_elem;
	if ($g_elem == 'TITLE') {
		$g_items [] = $text;
	}
}

$parser = xml_parser_create ();

xml_set_element_handler ( $parser, "startElement", "endElement" );
xml_set_character_data_handler ( $parser, "textData" );

$f = fopen ( 'testxml.xml', 'r' );

while ( $data = fread ( $f, 4096 ) ) {
	xml_parse ( $parser, $data );
}

xml_parser_free ( $parser );
foreach ( $g_items as $item ) {
	echo $item . "\n";
}

输出:

樱桃红
贤妻

 

以上就是总结的几点对php对xml数据的解析了,今天写了这几个例子跑了下,也熟悉了想相关的函数和类,当然关于xml还有很多其他知识点,暂不讨论了。转载请注明出处: CoderAladdin

 

trim函数的妙用

trim本来只是过滤头尾的空白字符或者特定字符
但是如果这样用
trim($v,'(,)’);
可以过滤”(fadfasdf),”这个字符串中的第一个和最后两个字符,注意”,”和”)”是最后两个字符

自动同步wordpress文章到有道云笔记的的插件制作(一)

一直想玩一下各大开放平台来着,虽然老早就申请了各大平台的开发者账号,但是从来没有开过刀。一直都忙的跟狗一样,再加上自己难以克服的惰性,所以…

今天是春节前在公司的最后一天,上午和@麦克是你的河马在公司dota来着,玩到high处,我开始高调起来,全然不顾走过来的部门总监,大喊大叫,于是可想而知的悲剧了……然后就安静的看起有道openAPI的文档还有sdk,想把之前的一个想法实现下。一直以来都是有道笔记的忠实用户,这款笔记软件也一直没让我失望,表现一直不错(还记的当时较低版本的时候提了个小建议,升级的时候那个功能竟然加了进去,不知道是不是采纳了我的建议,嘿嘿)。不用evernote就是因为免费版的左下角有个小块的广告,虽然谈不上难看,但是我的洁癖是不能容忍的。也许你会说,现在有道笔记的左下角也有个android客户端的推广广告。是的,正是因为这个小广告,我才开始有了做这个插件的想法,不想直视它,但是又不愿意放弃它(在这里给有道笔记提个小建议,如果用户从这个推广链接点到有道的应用下载界面,然后点了安装过这个软件,就不要再去显示了嘛,不然像我这种人,怎么看怎么不舒服,至于实现,是very easy的哦~)。我的这个想法是把平时的笔记文章写到博客上然后自动同步到有道笔记去,好处嘛自己去体会喽。

第一次玩开放平台,也是第一次接触oauth认证,流程很容易懂,开发者先去开放平台申请,然后等个几天之后,平台会给你key或许还有个secret,通过这些信息,你可以去按照api规定的一些方法组装一些参数去获取一组token,通过这个token,跳到登陆授权页面,让用户选择是否授权,然后用户授权之后,重定向到callbackUrl(如果有callbackUrl的话),同时有一个verifier,然后通过这个verifier和之前得到的token,再去请求一个accessToken,成功的话,给你返回一个accessToken和accessSecret。ok到了这里,你就可以通过这个accessToken和accessSecret来操作用户的有道笔记了[注],增删改查神马的,一切由你的代码说了算喽(当然这是在用户给你的代码授权的前提下),遇到的一个问题就是,我第一次成功走了这遍流程的时候,那我第二次是不是要再去要用户授权再去操作呢?我想那会把用户逼疯的,所以我觉得只要一次授权,就可以了。回头看我标记了[注]的那句话。是的,就是这个accessToken和accessSecret,只要用户第一次授权之后,你把这玩意保存下来,怎么存随你了,那么就可以用这玩意去做你想做的事情了(增删改查对应accessToken用户的笔记)。这个问题把我搞了有好一会,还是在一个腾讯微博开放平台开发者论坛里面找到的答案,要是看文档的时候能心细点看到我标[注]相关的那段,也就不用纠结这个了。当然,文档上是这样写的“通常Access Token 具有一定的有效期,在有效期内应用程序可以一直访问该用户的数据,而当Access Token 过期后,应用程序则需要再次走一遍授权流程。”。所以过期之后,又得走一遍授权,这点上希望有道让用户自己来决定是否撤销授权。也就是让用户自己操作删除第三方应用保存的accessToken。

今天就是小改了下有道托管在google code的sdk,自己实在太懒,懒得去组装api中规定的请求参数,就直接用sdk中封装好的方法,实现了下代码对笔记的操作,还没真正写成wordpress的插件,所以本文是(一),其实还没写过wordpress的插件,囧rz..,什么时候才能完工,还真心说不定。下面就是改的sdk里面sample的代码,代码丑陋,斗胆放出。文章表达观点若有不当,还请斧正。

<?php
include 'ynote_client.php';
header("Content-type: text/html; charset=utf-8");
$do = $_GET['do'] ? $_GET['do'] : 'api_test';

// please fill the following fields to run the demo
$oauth_consumer_key = "**********************";
$oauth_consumer_secret = "***********************";
$this_page = "http://localhost/test/php_sdk/ynote_client_example.php"; // this is the URL for the demo to get back to this page.

if(!($_GET['oauth_token'] && $_GET['oauth_verifier']))
{
	if ($do == 'api_test') {
	/******************************************** DEMO *************************************************/
		$client = new YnoteClient($oauth_consumer_key, $oauth_consumer_secret, 'http://sandbox.note.youdao.com');

		$response = $client->getRequestToken();
	    $request_token = $response['oauth_token'];
		$request_secret = $response['oauth_token_secret'];
		echo '<br />';

		$call_back = $this_page.'?request_token='.$request_token.'&request_secret='.$request_secret;
		$content = $client->getAuthorizeUrl($call_back);
		echo '<script type="text/javascript">location.href="'.$content.'"</script>';

	}
}
else 
{
	$post['verifier'] = $_GET['oauth_verifier'];
	$post['oauth_request_token'] = $_GET['request_token'];
	$post['oauth_request_secret'] = $_GET['request_secret'];

	$client = new YnoteClient($oauth_consumer_key, $oauth_consumer_secret);

	//这里要把token保存好,下次就不用授权了
    $oauth_access_token = '***************';
    $oauth_access_secret = '************************';

    echo 'access token:'.$oauth_access_token;
    echo '<br />';
    echo 'access secret:'.$oauth_access_secret;
    echo '<br />';

    $user_info_response = $client->getUserInfo($oauth_access_token, $oauth_access_secret);
    echo '<pre>';
    print_r(json_decode($user_info_response));
    echo '</pre>';

    echo '<br />';
    $list_notebook_response = $client->listNotebooks($oauth_access_token, $oauth_access_secret);
    $list_notebook = json_decode($list_notebook_response);
    echo '<pre>';
    print_r($list_notebook);
    echo '</pre>';

    echo '<br />';
    $list_notes_response = $client->listNotes($oauth_access_token, $oauth_access_secret, $list_notebook[0]->path);
    $list_note = json_decode($list_notes_response);
    echo '<pre>';
    print_r($list_note);
    echo '</pre>';

    echo '<br />';
    $get_note_response = $client->getNote($oauth_access_token, $oauth_access_secret, $list_note[0]);
	echo '<pre>';
    print_r(json_decode($get_note_response));
	echo '</pre>';
}

用开放平台的接口做事情还是有一定的局限性的,现在肚子里对微博神马的还有不少想法想去实现,如果开放的这些接口不能满足我的需求的话,会借@朱光星云共享给我的某个神器去做,哈哈。

顺便吐个槽,昨晚睡前掐指一算,无形地诞生了很多痛苦,不算上年终奖和1月份的工资(因为还没发,日),我12年整年攒下的钱一共才1k+,是的没错,才1000多块!真不晓得钱去哪里了,我了个大操!今年光棍节的时候一共花了六七百块给自己买了两件外套和两双鞋,没有买裤子,现在穿身上的牛仔裤也穿了快三年了,袋口和裤脚都破破烂烂了,真他妈苦逼。

mysql复合查询结果集顺序的一个问题

刚到家,@冰冻的狐狸在qq上给我留言,问这两句SQL为什么查询结果的排序不同

SELECT * FROM `pp_user_history` order by id desc;
SELECT * from (SELECT * FROM `pp_user_history` order by id desc) as hh GROUP BY hh.item_id;

我在本地用两条sql测试了下

SELECT * FROM words ORDER BY id ASC;
SELECT * FROM (SELECT * FROM `words` ORDER BY id ASC) AS hh GROUP BY hh.pinyin_s;

果然是不同的

仔细结果集发现
YJ1_21]E_LOZ6ET7`L`VRI7
通过红框圈出来的那个字段得到如下结论:

结果集排序默认按照主查询的group by字段升序排

BTW.明天年会,尼玛还有一个项目要赶着上线,哥要是不是请了将近三天假陪老娘在医院哥也搞完了啊,催个毛线!今天组里有个同事提离职了,我也和老大说了自己的想法和看法。人是需要成长的,路,也该是不断探索的,我想如果我不按照寻常路走下去,或许我是会遭受许多的挫折和磨难,但是我想也会得到更多。

在此新年之际,谢过老大一年来对我的悉心教导。谢谢!

Apache虚拟主机支持rewrite的设置

今天在用CI写东西的时候,要优化下url,结果.htaccess中rewrite无效,google了很多文章,看了很多CI中国的帖子,没一个说到虚拟主机的apche配置的,甚至社区里面的“前辈大牛”还说和apche配置无关。经过多番尝试,发现在虚拟主机中对url重写的配置需要在虚拟主机配置的前面才行,不知道我得出的这个结论是否正确,请各位访客指正。

顺便感慨下,今天也和斌哥说到的,虽然在公司里面做的产品有这么大流量,看起来是个很好的平台,但是有关服务器,有关负载均衡等方面的东西我们毫不涉及,这些都是需要我们自己平时去充实的知识。当然,写好当下自己该写好的代码是最重要的。

apache配置如下

<VirtualHost *:80>
<Directory "D:/wamp/www/CodeIgniter">
    AllowOverride All
</Directory>
ServerName loc.ci.com
DocumentRoot "D:/wamp/www/CodeIgniter"
</VirtualHost>

母亲周三又要过来这边医院检查了,希望一切都好,努力挣钱!!!

更正下,刚突然想到去看了下vps上本博客的apache virtualhost配置,发现顺序是可以不放前面的
像这样

<VirtualHost *:80>
ServerName loc.ci.com
DocumentRoot "D:/wamp/www/CodeIgniter"
<Directory "D:/wamp/www/CodeIgniter">
    AllowOverride All
</Directory>
</VirtualHost>

Directory后面的路径要写全

Apache的Order Allow,Deny 详解

Allow和Deny可以用于apache的conf文件或者.htaccess文件中(配合Directory, Location, Files等),用来控制目录和文件的访问授权。

所以,最常用的是:
Order Deny,Allow
Allow from All

注意“Deny,Allow”中间只有一个逗号,也只能有一个逗号,有空格都会出错;单词的大小写不限。上面设定的含义是先设定“先检查禁止设定,没有禁止的全部允许”,而第二句没有Deny,也就是没有禁止访问的设定,直接就是允许所有访问了。这个主要是用来确保或者覆盖上级目录的设置,开放所有内容的访问权。

按照上面的解释,下面的设定是无条件禁止访问:
Order Allow,Deny
Deny from All

如果要禁止部分内容的访问,其他的全部开放:
Order Deny,Allow
Deny from ip1 ip2
或者
Order Allow,Deny
Allow from all
Deny from ip1 ip2

apache会按照order决定最后使用哪一条规则,比如上面的第二种方式,虽然第二句allow允许了访问,但由于在order中allow不是最后规则,因此还需要看有没有deny规则,于是到了第三句,符合ip1和ip2的访问就被禁止了。注意,order决定的“最后”规则非常重要,下面是两个错误的例子和改正方式:

Order Deny,Allow
Allow from all
Deny from domain.org
错误:想禁止来自domain.org的访问,但是deny不是最后规则,apache在处理到第二句allow的时候就已经匹配成功,根本就不会去看第三句。
解决方法:Order Allow,Deny,后面两句不动,即可。

Order Allow,Deny
Allow from ip1
Deny from all
错误:想只允许来自ip1的访问,但是,虽然第二句中设定了allow规则,由于order中deny在后,所以会以第三句deny为准,而第三句的范围中又明显包含了ip1(all include ip1),所以所有的访问都被禁止了。
解决方法一:直接去掉第三句。
解决方法二:
Order Deny,Allow
Deny from all
Allow from ip1

 

下面是测试过的例子:
——————————–
Order deny,allow
allow from all
deny from 219.204.253.8
#全部都可以通行
——————————-
Order deny,allow
deny from 219.204.253.8
allow from all
#全部都可以通行
——————————-
Order allow,deny
deny from 219.204.253.8
allow from all
#只有219.204.253.8不能通行
——————————-
Order allow,deny
allow from all
deny from 219.204.253.8
#只有219.204.253.8不能通行
——————————-
——————————-
Order allow,deny
deny from all
allow from 219.204.253.8
#全部都不能通行
——————————-
Order allow,deny
allow from 219.204.253.8
deny from all
#全部都不能通行
——————————-
Order deny,allow
allow from 219.204.253.8
deny from all
#只允许219.204.253.8通行
——————————-
Order deny,allow
deny from all
allow from 219.204.253.8
#只允许219.204.253.8通行
——————————-
——————————–
Order deny,allow
#全部都可以通行(默认的)
——————————-
Order allow,deny
#全部都不能通行(默认的)
——————————-
Order allow,deny
deny from all
#全部都不能通行
——————————-
Order deny,allow
deny from all
#全部都不能通行
——————————-
对于上面两种情况,如果换成allow from all,则全部都可以通行!
——————————-
Order deny,allow
deny from 219.204.253.8
#只有219.204.253.8不能通行
——————————-
Order allow,deny
deny from 219.204.253.8
#全部都不能通行
——————————-
Order allow,deny
allow from 219.204.253.8
#只允许219.204.253.8通行
——————————-
Order deny,allow
allow from 219.204.253.8
#全部都可以通行
——————————-
——————————-
order deny,allow
allow from 218.20.253.2
deny from 218.20
#代表拒绝218.20开头的IP,但允许218.20.253.2通过;而其它非218.20开头的IP也都允许通过。
——————————-
order allow,deny
allow from 218.20.253.2
deny from 218.20
#和上面的差不多,只是掉换的order语句中的allow、deny先后顺序,但最终结果表示全部都拒绝!

form:http://hi.baidu.com/enjoypain/blog/item/f48c7aecdba298d12f2e21ac.html

前段时间做了个Apache的HTTP代理服务器,其中的order allow,deny这部分弄的不太懂,于是上网找资料看,谁知道越看越糊涂,其中有些难以分辨对错甚至是误导。就像破解windows系统密码的一些文章那样,很多都是人云亦云的,并没有经过测试。废话少说,先把我经过测试后分析总结出来的结论show出来,相信这对大家的理解非常有帮助。

 

总则——

影响最终判断结果的只有两点:

1. order语句中allow、deny的先后顺序;

2. allow、deny语句中各自包含的范围。

 

温馨提醒——

1. 修改完配置后要保存好并重启Apache服务,配置才能生效;

2. 开头字母不分大小写;

3. allow、deny语句不分先后顺序,谁先谁后不影响最终判断结果;但都会被判断到;

4. order语句中,“allow,deny”之间“有且只有”一个逗号(英文格式的),而且先后顺序很重要;

5. Apache有一条缺省规则,“order allow,deny”本身就默认了拒绝所有的意思,因为deny在allow的后面;同理,“order deny,allow”本身默认的是允许所有;当然,最终判断结果还要综合下面的allow、deny语句中各自所包含的范围;(也就是说order语句后面可以没有allow、deny语句)

6. allow、deny语句中,第二个单词一定是“from”,否则Apache会因错而无法启动,

7. “order allow,deny”代表先判断allow语句再判断deny语句,反之亦然。

 

上面说的都是要记住的,而下面说的是我独创的理解方法。如果有人看了而没有豁然开朗的感觉,那算是我的失败!

 

判断原则分4步走——

1. 首先判断默认的;

2. 然后判断逗号前的;

3. 最后判断逗号后的;

4. 最终按顺序叠加而得出判断结果。

 

上面三点我说的简单而形象,主要是为了便于记忆。暂时不理解不要紧,继续看下面详细的解说自然会明白。下面以一个普通例子来做解释——

order deny,allow

allow from 218.20.253.2

deny from 218.20

1. 所谓“首先判断默认的”,就是判断“order deny,allow”这句,它默认是允许所有;

2. 所谓“然后判断逗号前的”,因为在本例子中的order语句里面,deny在逗号的前面,所以现在轮到判断下面的deny语句了——“deny from 218.20”;

3. 所谓“最后判断逗号后的”,因为在本例子中的order语句里面,allow在逗号的后面,所以最后轮到判断下面的allow语句了——“allow from 218.20.253.2”。

4. 所谓“最终按顺序叠加而得出判断结果”,这是一个形象化了的说法,我把每一步判断都看作一个“不透明的图层”,然后一步步按顺序叠加上去,最终得出的“图像”就是判断结果。

 

用过作图软件的人应该都知道“图层”是怎么回事,我估计Apache关于order allow deny这方面的设计理念和photoshop等作图软件关于图层的设计理念是一样的。即“游戏规则”是一样的。

那么上面的例子就可以是这么一个步骤和图像——

1. 先画一个白色的大圆,代表“order deny,allow”语句,默认意思是允许所有;

2. 然后画一个小一点的黑色圆,代表“deny from 218.20”语句,意思是拒绝所有以218.20开头的IP,放进白色的大圆里面;

3. 最后再画一个白色的圆,代表“allow from 218.20.253.2”语句,意思是允许218.20.253.2通过,放在黑色圆的上面。

4. 到此为止,我们已经可以看到一个结果了,白色大圆上面有一个黑色圆,黑色圆上面还有一个白色圆。最后,我们所能看到的黑色部分就是拒绝通行的,剩下的白色部分都是允许通行的。判断的结果就是这么简单形象!

 

如果不懂的用作图软件,我们再来个非常贴近生活的比喻——

把上面的例子改动一点点,以便更好的理解:

order deny,allow

allow from 218.20.253.2

deny from 219.30

1. 首先拿一张A4白纸,代表第order语句,意思是允许全部;

2. 然后拿一张黑色纸剪一个圆,放在A4纸里面的某个位置上,代表deny语句,意思是拒绝所有以219.30开头的IP;

3. 最后拿白纸再剪一个圆,放在黑色圆的旁边,代表allow语句,意思是允许218.20.253.2通过;注意,这个例子不是放进黑色圆里面了,因为deny和allow语句不再有相互包含的关系了。

4. A4纸上面有一个黑色圆和一个白色圆,结果自然很明显了。不过白色的A4纸上再放一个白色的圆,显然是多余的了,因为大家都是白色的,都代表允许,所以就重复了,可以去掉白色的圆而不会影响判断结果。

 

如果看到这里还没明白,那一定是我还有什么没说清楚的。那么请好好分析我所做过的测试例子,将在最后列出来。

在这里再啰嗦一下,allow、deny语句后面跟的参数有多种形式,有不同的表达方式,我在网上看到的做法是deny from IP1 IP2 IP3或allow from domain.com等。其它的表达方式大家再找别的资料看吧。我想说的是另一种表达方式:

order deny,allow

allow from IP1 IP2

allow from domain.info

allow from 219.20.55.0/24

deny from all

我没具体验证过这是否对,不过这样是可以正常启动Apache服务的,按道理应该是正确的表达方式。哈哈,像我这样的入门者只能这样了,还希望大家多多指教!

下面是测试过的例子:
——————————–
Order deny,allow
allow from all
deny from 219.204.253.8
#全部都可以通行
——————————-
Order deny,allow
deny from 219.204.253.8
allow from all
#全部都可以通行
——————————-
Order allow,deny
deny from 219.204.253.8
allow from all
#只有219.204.253.8不能通行
——————————-
Order allow,deny
allow from all
deny from 219.204.253.8
#只有219.204.253.8不能通行
——————————-
——————————-
Order allow,deny
deny from all
allow from 219.204.253.8
#全部都不能通行
——————————-
Order allow,deny
allow from 219.204.253.8
deny from all
#全部都不能通行
——————————-
Order deny,allow
allow from 219.204.253.8
deny from all
#只允许219.204.253.8通行
——————————-
Order deny,allow
deny from all
allow from 219.204.253.8
#只允许219.204.253.8通行
——————————-
——————————–
Order deny,allow
#全部都可以通行(默认的)
——————————-
Order allow,deny
#全部都不能通行(默认的)
——————————-
Order allow,deny
deny from all
#全部都不能通行
——————————-
Order deny,allow
deny from all
#全部都不能通行
——————————-
对于上面两种情况,如果换成allow from all,则全部都可以通行!
——————————-
Order deny,allow
deny from 219.204.253.8
#只有219.204.253.8不能通行
——————————-
Order allow,deny
deny from 219.204.253.8
#全部都不能通行
——————————-
Order allow,deny
allow from 219.204.253.8
#只允许219.204.253.8通行
——————————-
Order deny,allow
allow from 219.204.253.8
#全部都可以通行
——————————-
——————————-
order deny,allow
allow from 218.20.253.2
deny from 218.20
#代表拒绝218.20开头的IP,但允许218.20.253.2通过;而其它非218.20开头的IP也都允许通过。
——————————-
order allow,deny
allow from 218.20.253.2
deny from 218.20
#和上面的差不多,只是掉换的order语句中的allow、deny先后顺序,但最终结果表示全部都拒绝!