分享

web信息的搜集---Url.h+Url.cpp

 闲来看看 2013-01-18

web信息的搜集---Url.h+Url.cpp

/*

 * Url.h

 * Created on: 2011-10-6

 *      Author: qiuxiong

 * CUrl类的作用:

 * 解析URL,构造HTTP请求报文,发送给目标服务器.

 * URL的通用形式是:

 * scheme://hostname[:port][/path][;params][?query][#fragment]

 * scheme 表示协议名称,例如http,ftp,mailto等等

 * hostname 表示主机号,其实就是该主机对应的因特网域名

 * port 表示端口号

 * path 表示URL路径

 * params 表示对象参数

 * query 表示查询信息,也经常记为request

 * fragment 表示片段标识

 */

#ifndef URL_H_

#define URL_H_

#include<string>

using namespace std;

const unsigned int URL_LEN=256;//URL最大长度

const unsigned int HOST_LEN=256;//主机号的最大长度

const int DEFAULT_HTTP_PORT=80;//HTTP默认的端口号为80

const int DEFAULT_FTP_PORT=21;//FTP默认的端口号为21

//自定义的枚举类型:表示协议类型,有HTTP协议,FTP协议,其他的无效协议

enum url_scheme{SCHEME_HTTP,SCHEME_FTP,SCHEME_INVALID};

class CUrl

{

public:

string m_sUrl;//URL字符串

enum url_scheme m_eScheme;//URL解析出的协议类型

string m_sHost;//URL解析出的主机号

int m_nPort;//URL解析出的端口号

string m_sPath;//URL解析出的请求资源

public:

CUrl();

~CUrl();

bool ParseUrlEx(string strUrl);//解析是不是HTTP协议的URL,不是返回FALSE,否则进一步解析URL对应的协议名称,主机号,端口号,请求资源赋值给成员变量,最后返回TRUE

//真正的解析URL的函数

void ParseUrlEx(const char *url,char *protocol,int lprotocol,char *host,int lhost,char *request,int lrequest,int *port);

char *GetIpByHost(const char *host);//通过主机号得到IP字符串

bool IsValidHost(const char *host);//判断该主机号是不是有效的主机号

bool IsForeignHost(string host);//判断该主机号是否为外国主机号

bool IsImageUrl(string url);//判断该URL是否为图片链接

bool IsValidIp(const char *ip);//判断该IP字符串是否有效

bool IsVisitedUrl(const char *url);//判断该URL是否访问过

bool IsUnReachedUrl(const char *url);//----没有实现---------

bool IsValidHostChar(char ch);//判断该字符是否为构成主机号的字符

private:

void ParseScheme(const char *url);//解析出URL所以对应的协类型

};

#endif /* URL_H_ */

/*

 *Url.cpp

 * Created on: 2011-10-6

 *      Author: qiuxiong

 * 有了URL搜集系统就可以根据URL的标识抓取其对应的网页

 */

#include<iostream>

#include<string>

#include<sys/socket.h>

#include<netdb.h>

#include "Http.h"

#include "Tse.h"

#include "Url.h"

#include "Md5.h"

#include "StrFun.h"

//DNS缓存:我们不能每次解析出了一个主机号就交给DNS server来得到IP,建立一个DNS缓存,防止出现类似于拒绝访问攻击的作用

map<string,string>mapCacheHostLookup;

//这个set容器保存的是已经访问过的URL对应的MD5值

extern set<string>setVisitedUrlMD5;//

extern map<unsigned long ,unsigned long>mapIpBlock;//

typedef map<string,string>::value_type valTypeCHL;

pthread_mutex_t mutexCacheHost=PTHREAD_MUTEX_INITIALIZER;//线程的互斥变量初始化

//自定义协议类-端口-可以访问,例如 "http://" 80 1

struct scheme_data

{

char *leading_string;

int default_port;

int enabled;

};

static struct scheme_data supported_schemes[]=

{

{"http://",DEFAULT_HTTP_PORT,1},

{"ftp://",DEFAULT_FTP_PORT,1},

{NULL,-1,0}

};

//解析出URL所以对应的协议类型,然后将协议类型赋值给成员变量m_eScheme

void CUrl::ParseScheme(const char *url)

{

int i;

for(i=0;supported_schemes[i].leading_string;i++)

if(strncasecmp(url,supported_schemes[i].leading_string,strlen(supported_schemes[i].leading_string))==0)

{

if(supported_schemes[i].enabled)

{

this->m_eScheme=(enum url_scheme)i;//强制转化为枚举类型

return;

}

else

{

this->m_eScheme=SCHEME_INVALID;

return;

}

}

this->m_eScheme=SCHEME_INVALID;

return;

}

//解析是不是HTTP协议的URL,不是返回FALSE,否则进一步解析URL对应的协议名称,主机号,端口号,请求资源赋值给成员变量,最后返回TRUE

bool CUrl::ParseUrlEx(string strUrl)

{

char protocol[10];

char host[HOST_LEN];

char request[256];

int  port=-1;

memset(protocol,0,sizeof(protocol));

memset(host,0,sizeof(host));

memset(request,0,sizeof(request));

this->ParseScheme(strUrl.c_str());//得到协议类型

if(this->m_eScheme!=SCHEME_HTTP)//我们只抓取所有协议名为HTTP的WEB网页

return false;

ParseUrlEx(strUrl.c_str(),protocol,sizeof(protocol),host,sizeof(host),request,sizeof(request),&port);

m_sUrl=strUrl;

m_sHost=host;

m_sPath=request;

if(port>0)

m_nPort=port;

return true;

}

//真正的解析URL的函数

void CUrl::ParseUrlEx(const char *url,char *protocol,int lprotocol,char *host,int lhost,char *request,int lrequest,int *port)

{

char *ptr,*ptr2,*work;

*protocol=*host=*request=0;

*port=DEFAULT_HTTP_PORT;

int len=strlen(url);

work=new char[len+1];

memset(work,0,len+1);

strncpy(work,url,len);

ptr=strchr(work,':');//查找work字符串中首次出现':'的位置指针

//得到协议名称

if(ptr!=NULL)

{

*(ptr++)=0;

strncpy(protocol,work,lprotocol);

}

else

{

strncpy(protocol,"HTTP",lprotocol);

ptr=work;

}

//跳过"//"字符

if((*ptr=='/')&&(*(ptr+1)=='/'))

ptr+=2;

ptr2=ptr;

while(IsValidHostChar(*ptr2)&&*ptr2)

ptr2++;

*ptr2=0;

//得到主机号

strncpy(host,ptr,lhost);

int offset=ptr2-work;

const char *pStr=url+offset;

//得到请求资源

strncpy(request,pStr,lrequest);

ptr=strchr(host,':');

//得到端口号

if(ptr!=NULL)

{

*ptr=0;//不能小看这个地方,很有用,将主机号+端口号相分离

*port=atoi(ptr+1);

}

delete []work;

work=NULL;

}

//无参构造函数

CUrl::CUrl()

{

this->m_sUrl="";

this->m_eScheme=SCHEME_INVALID;

this->m_sHost="";

this->m_sPath="";

this->m_nPort=DEFAULT_HTTP_PORT;

}

//析构函数

CUrl::~CUrl()

{

}

//通过主机号得到IP字符串

char *CUrl::GetIpByHost(const char *host)

{

if(!host)

return NULL;

if(!IsValidHost(host))

return NULL;

unsigned long inaddr=0;

int len=0;

char *result=NULL;

inaddr=(unsigned long)inet_addr(host);//将字符串IP转化为32二进制的网络字节序

if(inaddr!=INADDR_NONE)//表示该主机号就是用IP地址表示的

{

len=strlen(host);

result=new char[len+1];

memset(result,0,len+1);

memcpy(result,host,len);

return result;

}

else//否则,我在DNS缓存中看能否查找该主机号对应的IP字符串

{

map<string,string>::iterator it=mapCacheHostLookup.find(host);

if(it!=mapCacheHostLookup.end())//在DNS缓存中查找到了

{

const char *strHostIp;

strHostIp=(*it).second.c_str();

inaddr=(unsigned long)inet_addr(strHostIp);

if(inaddr!=INADDR_NONE)

{

len=strlen(strHostIp);

result=new char[len+1];

memset(result,0,len+1);

memcpy(result,strHostIp,len);

return result;

}//end if

}//end if

}//end else

//通过上面的方法我们都没有查找,这个时候我们只能通过DNS server查找了,这种带宽的消耗是必要的!

struct hostent*hp;

hp=gethostbyname(host);//通过主机号或者说是域名得到hostent结构,这个结构包含主机号或者说域名的很多信息,例如我们要找的IP字符串就在其中

if(hp==NULL)

{

cout<<"url.cpp:   gethostbyname()函数出错!"<<endl;

return NULL;

}

struct in_addr in;

bcopy(*(hp->h_addr_list),(caddr_t)&in,hp->h_length);

char abuf[INET_ADDRSTRLEN];

//inet_ntop()将32位的二进制网络字节序转化为IP字符串

if(inet_ntop(AF_INET,(void*)&in,abuf,sizeof(abuf))==NULL)

{

cout<<"url.cpp:   inet_ntop()函数出错!"<<endl;

return NULL;

}

else

{

pthread_mutex_lock(&mutexCacheHost);//互斥的锁定函数

if(mapCacheHostLookup.find(host)==mapCacheHostLookup.end())

mapCacheHostLookup.insert(valTypeCHL(host,abuf));//将新得到的主机号<-->IP字符串插入DNS缓存中

pthread_mutex_unlock(&mutexCacheHost);//互斥的解锁函数

}

    len=strlen(abuf);

    result=new char[len+1];

    memset(result,0,len+1);

    memcpy(result,abuf,len);

    return result;

}

//判断该字符是否为构成主机号的字符

bool CUrl::IsValidHostChar(char ch)

{//主机号只能是由26个大小写字符,所10个阿拉伯数字,以及. : - _构造而成

return (isalpha(ch)||isdigit(ch)||ch=='.'||ch==':'||ch=='_'||ch=='-');

}

//判断该主机号是不是有效的主机号

bool CUrl::IsValidHost(const char *host)

{

if(!host)//空的主机号,我们认为是无效的主机号

return false;

if(strlen(host)<6)//主机号长度小于6,我们认为ieshi无效的主机号

return false;

unsigned int i;

for(i=0;i<strlen(host);i++)

if(!IsValidHostChar(host[i]))

return false;

return true;

}

//判断该URL是否访问过

bool CUrl::IsVisitedUrl(const char *url)

{

if(!url)

return true;

CMD5 iMD5;

iMD5.GenerateMD5((unsigned char*)url,strlen(url));

string strDigest=iMD5.ToString();

if(setVisitedUrlMD5.find(strDigest)!=setVisitedUrlMD5.end())

return true;

return false;

}

//判断该IP字符串是否有效

bool CUrl::IsValidIp(const char *ip)

{

if(ip==NULL)

return false;

unsigned long inaddr=(unsigned long)inet_addr(ip);

if(inaddr==INADDR_NONE)//显然该IP参数不是正确的字符串IP

return false;

if(mapIpBlock.size()>0)

{

map<unsigned long,unsigned long>::iterator pos;

for(pos=mapIpBlock.begin();pos!=mapIpBlock.end();pos++)

{

unsigned long ret;

ret=inaddr&~((*pos).second);

if(ret==(*pos).first)

return true;

}//end for

return false;

}//end if

return true;

}

//判断该主机号是否为外国主机号

bool CUrl::IsForeignHost(string host)

{

if(host.empty())

return true;

if(host.size()>HOST_LEN)

return true;

unsigned long inaddr=0;

inaddr=(unsigned long)inet_addr(host.c_str());

if(inaddr!=INADDR_NONE)//显然该HOST是用IP字符串表示的,我们认为它不是外国主机号

return false;//目前无法通过字符串IP判断是不是外国主机号,所以一律当作国内主机号

string::size_type idx=host.rfind('.');

string tmp;//tmp得到的是主机号中的顶级域名

if(idx!=string::npos)

tmp=host.substr(idx+1);

CStrFun::Str2Lower(tmp,tmp.size());

//下面10个顶级域名是我们能访问的顶级域名,其他都视为不能访问的

const char *home_host[]={"cn","com","net","org","info","biz","tv","cc","hk","tw"};

int home_host_num=10;

for(int i=0;i<home_host_num;i++)

if(tmp==home_host[i])

return false;

return true;

}

//判断该URL是否为图片链接

bool CUrl::IsImageUrl(string url)

{

if(url.empty())

return false;

if(url.size()>URL_LEN)

return false;

string::size_type idx=url.rfind('.');

string tmp;

if(idx!=string::npos)

tmp=url.substr(idx+1);

CStrFun::Str2Lower(tmp,tmp.size());

const char *image_type[]={"jpg","bmp","gif","jpeg","png","tif","psd"};

int image_type_num=7;

for(int i=0;i<image_type_num;i++)

if(tmp==image_type[i])

return true;

return false;

}

//目前不知道系统设计这个函数的作用----我在这里让它一律返回false

bool CUrl::IsUnReachedUrl(const char *url)

{

return false;

}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多