robots.txt文件的解析及过滤

什么是robots.txt文件?

        robots.txt(统一小写)是一种存放于网站根目录下的ASCII编码的文本文件,它通常告诉网络搜索引擎的漫游器(又称网络蜘蛛),此网站中的哪些内容是不能被搜索引擎的漫游器获取的,哪些是可以被(漫游器)获取的。 因为一些系统中的URL是大小写敏感的,所以robots.txt的文件名应统一为小写。robots.txt应放置于网站的根目录下。如果想单独定义搜索引擎的漫游器访问子目录时的行为,那么可以将自定的设置合并到根目录下的robots.txt,或者使用robots元数据。Robots.txt协议并不是一个规范,而只是约定俗成的,所以并不能保证网站的隐私。 ——维基百科(http://zh.wikipedia.org/wiki/Robots.txt

基本属性:

User-agent: 定义搜索引擎的类型
Disallow: 定义禁止搜索引擎收录的地址
Allow: 定义允许搜索引擎收录的地址
Crawl-delay:支持Crawl-delay参数,设置为多少秒,以等待同服务器之间连续请求(网络爬虫的礼貌策略)
#:一些robots.txt 会有注释,#后面都是注释内容,需要过滤掉 

一些例子;

User-agent: * 这里的*代表的所有的搜索引擎种类,*是一个通配符
Disallow: /admin/ 这里定义是禁止爬寻admin目录下面的目录
Disallow: /require/ 这里定义是禁止爬寻require目录下面的目录
Disallow: /ABC/ 这里定义是禁止爬寻ABC目录下面的目录
Disallow: /cgi-bin/*.htm 禁止访问/cgi-bin/目录下的所有以".htm"为后缀的URL(包含子目录)。
Disallow: /*?* 禁止访问网站中所有的动态页面
Disallow: /jpg$ 禁止抓取网页所有的.jpg格式的图片
Disallow:/ab/adc.html 禁止爬去ab文件夹下面的adc.html文件。
Allow: /cgi-bin/ 这里定义是允许爬寻cgi-bin目录下面的目录
Allow: /tmp 这里定义是允许爬寻tmp的整个目录
Allow: .htm$ 仅允许访问以".htm"为后缀的URL。
Allow: .gif$ 允许抓取网页和gif格式图片
Crawl-delay: 10 

C++实现解析robots.txt,并对url进行过滤:

robotsFilter.h

/*
* urlFilter.h
*
* Created on: 2012-3-26
* Author: xiaojay
*/
#ifndef URLFILTER_H_
#define URLFILTER_H_
#include "../urlFrontier/url.h"
#include<string.h>
#include<assert.h>
#include <iostream>
using namespace std;
#define MAXDISALLOW 100

//接口
class urlFilter
{
public:
// 当u合法时,返回true,否则返回false
virtual bool filter(url * u) = 0;
virtual bool filter(const char * url_text) = 0;
};
//robots.txt过滤
class robotsFilter : public urlFilter
{
public:
robotsFilter(const char * host_name , const char * robots_txt);
~robotsFilter();
virtual bool filter(url * u);
virtual bool filter(const char * url_text);
void parseRobots();
void printDisallow();
bool robotsMatch(char * match , char * file);
private:
char * strlwr(char *); //辅助函数,大写转小写
char * disallow [MAXDISALLOW];
int disallow_size;
char * allow[MAXDISALLOW];
int allow_size;
char * hostname;
char * robotstxt;
int crawldelay ;
};

#endif /* URLFILTER_H_ */

robotsFilter.cpp

/*
* robotFilter.cpp
*
* Created on: Mar 31, 2012
* Author: mayday
*/
#include "urlFilter.h"

// 初始化相关参数
robotsFilter::robotsFilter(const char * host_name , const char * robots_txt)
{
assert(host_name!=NULL && robots_txt!=NULL);
int len = strlen(host_name);
hostname = new char [len + 1];
strcpy(hostname,host_name);

len = strlen(robots_txt);
robotstxt = new char [len + 1];
strcpy(robotstxt,robots_txt);

disallow_size = 0;
allow_size = 0;
crawldelay = 0;
}
robotsFilter::~robotsFilter()
{
delete [] hostname;
delete [] robotstxt;
for(int i=0;i<disallow_size;i++)
{
delete [] disallow[i];
}
for(int i=0;i<allow_size;i++)
{
delete [] allow[i];
}
}

char* robotsFilter::strlwr( char* str )
{
char* orig = str;
// process the string
for (;*str != '\0'; str++ )
*str = tolower(*str);
return orig;
}
//解析robots文本
void robotsFilter::parseRobots()
{
//cout<<robotstxt<<endl;
//key:value
char key [32];
char value [100];
int i,j;
int posl = 0, posm = 0 ,posr =0;
int len = strlen(robotstxt);
bool hasAgent = false;
//总体思路,确定三个标志的位置,然后分割字符串. ——>posl( key )posm( value )posr
while(posl<len && posm<len && posr<len)
{
//找到第一个不为空格和换行符的字符位置,确定posl
while(posl<len && (robotstxt[posl]==' '
|| robotstxt[posl]=='\n' || robotstxt[posl]=='\r')) posl++;
//以#开头的,直接过滤掉该行
if(robotstxt[posl]=='#')
{
while(posl<len && robotstxt[posl]!='\n') posl++;
continue;
}
//找‘:’,确定posm
posm = posl+1;
while(posm<len && robotstxt[posm]!=':') posm++;
//找换行符位置,确定posr
posr = posm+1;
while(posr<len && robotstxt[posr]!='\n') posr++;

for(j=0,i=posl;i<posm;i++)
{
if(robotstxt[i]!=' '&&robotstxt[i]!='\t'&&robotstxt[i]!='\r'&&robotstxt[i]!='\n')
key[j++] = robotstxt[i];
}
key[j] = '\0';
for(j=0,i=posm+1;i<posr;i++)
{
if(robotstxt[i]!=' '&&robotstxt[i]!='\t'&&robotstxt[i]!='\r'&&robotstxt[i]!='\n')
value[j++] = robotstxt[i];
}
value[j]='\0';
posl = posr;
//cout<<key<<"\t"<<value<<endl;
//确定user-agent,是否针对本爬虫
if(strcmp(strlwr(key),"user-agent")==0){
if(strcmp(value,"*")==0||strcmp(value,"webcrawler")==0)
{
hasAgent = true;
}
else hasAgent = false;
}
if(hasAgent)
{
int len_val = strlen(value);
if(len_val<=0) continue;
if(strcmp(strlwr(key),"disallow")==0 && disallow_size<MAXDISALLOW)
{
disallow[disallow_size] = new char [len_val+1];
strcpy(disallow[disallow_size],strlwr(value));
disallow_size++;
}
else if (strcmp(strlwr(key),"allow")==0 && allow_size<MAXDISALLOW)
{
allow[allow_size] = new char [len_val+1];
strcpy(allow[allow_size],strlwr(value));
allow_size++;
}
else if(strcmp(strlwr(key),"craw-delay")==0)
{
crawldelay = 0;
int len_val = strlen(value);
for(int i=0;i<len_val;i++)
{
crawldelay = crawldelay * 10 + value[i]-'0';
}
}
}
}
}

void robotsFilter::printDisallow()
{
for(int i=0;i<disallow_size;i++)
{
cout<<disallow[i]<<endl;
}
}
//url->file属性与robots.txt匹配串的匹配,如果匹配成功返回true,否则返回false
bool robotsFilter::robotsMatch(char * match ,char * file)
{
int len_match = strlen(match);
if(len_match<=1) return true;
int len_file = strlen(file);
if(len_match>len_file) return false;
// ends with '$'
if(match[len_match-1]=='$')
{
int pos_match = 0;
char head [50],tail[20];
pos_match = len_match - 1;
while(pos_match>=0&&match[pos_match]!='*') pos_match --;
strncpy(head,match,pos_match);
head[pos_match] = '\0';
strncpy(tail,match+pos_match+1,len_match-pos_match-2);
tail[len_match-pos_match-2] = '\0';
int lhead = pos_match;
int ltail = len_match-pos_match-2;
int i = 0;
while(i<lhead && head[i]==file[i]) i++;
if(i!=lhead) return false;
i=0;
while(i<ltail && tail[i]==file[len_file-ltail+i]) i++;
if(i!=ltail) return false;
return true;
}
// 统计 '*'
int starlist [10] , starcount=0;
for(int i=0;i<len_match;i++)
if(match[i]=='*')
starlist[starcount++] = i;
//if have no star
if(starcount==0)
{
int i=0;
while(i<len_match && match[i]==file[i]) i++;
if(i!=len_match) return false;
else return true;
}
// if have star
/*
算法如下,首先按照'*',分割为多给子串
然后各个子串需按序匹配到file文本,且每个子串按首次匹配为准
*/
int i=0;
//test head
while(i<starlist[0]&&match[i]==file[i]) i++;
if (i!=starlist[0]) return false;
//test tail
if(match[len_match-1]!='*')
{
int length = len_match - (starlist[starcount-1] + 1);
i = 0;
while(i<length && match[len_match - i -1] == file[len_file - i -1]) i++;
if (i!=length) return false;
}

// match each star
int start_match , end_match , start_file = starlist[0];
char case_str [50];
for(int i=1;i<starcount;i++)
{
start_match = starlist[i-1]+1;
end_match = starlist[i];
strncpy(case_str,match+start_match,end_match-start_match);
case_str[end_match-start_match] = '\0';
int i = start_file;
int j = 0;
//match case_str , perhaps KMP algorithms is better than mine
while(i<len_file && j<(end_match-start_match))
{
if(file[i]==case_str[j]){
++i;++j;
}else{
i = i-j+1;
j = 0;
}
}
if(j==(end_match-start_match))
{
start_file = i;
}else return false;
}
return true;
}

// if u->file matches robots.txt ,return false ; otherwise true
bool robotsFilter::filter(url * u)
{
/*
*robots.txt里面可能有Allow,也可能有Disallow
*其实如果两个都设置的话,会有一点歧义,crawler不知道到底应不应该爬
*这里给出的方法是:两个都进行测试,只要通过一个就ok。
*/
if(disallow_size<=0 && allow_size<=0 ) return true;
bool pass_disallow = true;
if(disallow_size>0)
{
if(strcmp(disallow[0],"/")==0 || strcmp(disallow[0],"*")==0
||strcmp(disallow[0]," ")==0 ||strcmp(disallow[0],"")==0)
pass_disallow = false;
else
{
for(int i=0;i<disallow_size;i++)
{
if (robotsMatch(disallow[i],u->getFile())) {
pass_disallow = false;
break;
}
}
}
}
else pass_disallow = false;
if(pass_disallow==true) return true;

bool pass_allow = false;
if (allow_size>0)
{
if(strcmp(allow[0],"/")==0 || strcmp(allow[0],"*")==0) pass_allow = true;
else
{
for(int i=0;i<allow_size;i++)
{
if (robotsMatch(allow[i],u->getFile()))
{
pass_allow = true;
break;
}
}
}
}
return pass_allow;
}
bool robotsFilter::filter(const char * url_text)
{
char tmp [150];
strcpy(tmp,url_text);
url * u = new url(tmp,0,0);
bool res = filter(u);
delete u;
return res;
}




posted @ 2012-04-03 14:33  糖拌咸鱼  阅读(1677)  评论(2编辑  收藏  举报