//从网页中提取出超链接信息
bool CPage::ParseHyperLinks()
{
if(GetContentLinkInfo()==false)
return false;
if(m_sContentLinkInfo.empty())
return false;
bool bFind4SE=false;
bool bFind4History=false;
if(GetLinkInfo4SE())
if(FindRefLink4SE())
bFind4SE=true;
if(GetLinkInfo4History())
if(FindRefLink4History())
bFind4History=true;
//如果没有从网页中提取出为搜索引擎或者为历史网页存档准备的超链接则返回false
if(!bFind4SE&&!bFind4History)
return false;
return true;
}
//从网页体中提取出包含超链接信息的标识
bool CPage::GetContentLinkInfo()
{
if(m_sContent.empty())
return false;
m_sContentLinkInfo=m_sContent;
string &s=m_sContentLinkInfo;
const string delims("\t\r\n");
string::size_type pre_idx,idx=0;
//找到所有的"\t\r\n"并将'\t'替换为' ' 如果是\t\t\r\n则删除一个\t
while((idx=s.find_first_of(delims,idx))!=string::npos)
{
pre_idx=idx;
//删除从idx开始的一个字符,并用一个' '替换
s.replace(idx,1,1,' ');
idx++;
while((idx=s.find_first_of(delims,idx))!=string::npos)
{
if(idx-pre_idx==1)
s.erase(idx,1);
else
break;
}
idx--;
}
//将s中<br>标记全部替换为空格
CStrFun::ReplaceStr(s,"<br>"," ");
if(s.size()<20)
return false;
/*
例如:一个网页中有如下超链接
<a href="http://www.baidu.com/">百度</a><a href="http://www.qq.com/">QQ</a><img src="http://www."><......
我们将会提取为
m_sContentLinkInfo=href="http://www.baidu.com/">百度href="http://www.qq.com/">QQ<img src="http://www.">
*/
string::size_type idxHref=0,idxArea=0,idxImg=0;
string dest;
do
{
if(s.empty())
break;
idxHref=CStrFun::FindCase(s,"href");
idxArea=CStrFun::FindCase(s,"<area");
idxImg=CStrFun::FindCase(s,"<img");
pre_idx=idxHref>idxArea?idxArea:idxHref;
pre_idx=pre_idx>idxImg?idxImg:pre_idx;
if(pre_idx==string::npos)
break;
s=s.substr(pre_idx);
idx=s.find_first_of('<',1);
if(idx!=string::npos)
dest=dest+s.substr(0,idx);
else
break;
s=s.substr(idx);
idxHref=idxImg=idxArea=0;
}while(1);
s=dest;
/*
删除所有的'\'字符 避免出现下面的情况---注意'\'是转义字符"\\"
document.write("<a href=\"http://www.baidu.com/index.html\">百度</a>");
*/
CStrFun::EraseStr(s,"\\");
if(s.size()<20)
return false;
return true;
}
//再从m_sContentLinkInfo提取出为搜索引擎准备的超链接
bool CPage::GetLinkInfo4SE()
{
if(m_sContentLinkInfo.empty())
return false;
m_sLinkInfo4SE=m_sContentLinkInfo;
string &s=m_sLinkInfo4SE;
string::size_type pre_idx,idx,idxHref=0,idxArea=0;
string dest;
/*
例如:上面的m_sContentLinkInfo=href="http://www.baidu.com/">百度href="http://www.qq.com/">QQ<img src="http://www.">
我们这里提取出href="http://www.baidu.com/">百度href="http://www.qq.com/">QQ 过滤掉<img src="http://www.">
因为<img src="http://www.">的超链接是为历史网页存档准备的超链接
*/
do
{
if(s.empty())
break;
idxHref=CStrFun::FindCase(s,"href");
idxArea=CStrFun::FindCase(s,"<area ");
pre_idx=idxHref>idxArea?idxArea:idxHref;
if(pre_idx==string::npos)
break;
s=s.substr(pre_idx);
idx=s.find_first_of('<',1);
if(!(s.length()<4))
{
idxHref=CStrFun::FindCaseFrom(s,"href",4);
idx=idx>idxHref?idxHref:idx;
}
if(idx!=string::npos)
dest=dest+s.substr(0,idx);
else if(idx==string::npos&&pre_idx!=string::npos)
{
dest=dest+s;
break;
}
else
break;
s=s.substr(idx);
idxHref=idxArea=0;
}while(1);
s=dest;
if(s.length()<20)
return false;
CStrFun::EraseStr(s,"\"");
CStrFun::EraseStr(s,"'");
CStrFun::EraseStr(s," ");
dest.clear();
idxHref=0;
/*
通过上面的提取我们得到href="http://www.baidu.com/">百度href="http://www.qq.com/">QQ
我们再次提取
m_sLinkInfo4SE="http://www.baidu.com/">百度"http://www.qq.com/">QQ
*/
const string delims(" #>");
do
{
if(s.empty())
break;
idxHref=CStrFun::FindCase(s,"href");
if(idxHref==string::npos)
break;
idx=s.find('=',idxHref);
if(idx==string::npos)
break;
s=s.substr(idx+1);
while(s.length()>0&&s[0]==' ')
s.erase(0,1);
if(s.length()==0)
break;
idx=s.find_first_of(delims,1);
if(idx==string::npos)
break;
dest+='"'+s.substr(0,idx);
idx=s.find('>');
if(idx==string::npos)
break;
dest+='>';
s=s.substr(idx+1);
idx=s.find('<');
if(!s.empty())
{
idxHref=CStrFun::FindCase(s,"href");
idx=idx>idxHref?idxHref:idx;
}
if(idx==string::npos)
{
dest+=s;
break;
}
dest+=s.substr(0,idx);
idxHref=0;
}while(1);
idx=0;
while((idx=dest.find("\"\"",idx))!=string::npos)
{
dest.erase(idx,1);
}
s=dest;
return s.length()<20?false:true;
}
//再从m_sContentLinkInfo提取出为历史网页存档准备的超链接
bool CPage::GetLinkInfo4History()
{
if(m_sContentLinkInfo.empty())
return false;
m_sLinkInfo4History=m_sContentLinkInfo;
string &s=this->m_sLinkInfo4History;
string::size_type pre_idx,idx,idxImg=0,idxHref=0;
string dest;
/*
例如:上面的m_sContentLinkInfo=href="http://www.baidu.com/">百度href="http://www.qq.com/">QQ<img src="http://www.">
我们这里提取出<img src="http://www."> 过滤掉href="http://www.baidu.com/">百度href="http://www.qq.com/">QQ
因为href="http://www.baidu.com/">百度href="http://www.qq.com/">QQ的超链接是为搜索引擎准备的超链接
*/
do
{
if(s.empty())
break;
idxImg=CStrFun::FindCase(s,"<img");
pre_idx=idxImg;
if(pre_idx==string::npos)
break;
s=s.substr(pre_idx);
idx=s.find_first_of('<',1);
if(!(s.length()<4))
{
idxHref=CStrFun::FindCaseFrom(s,"href",4);
idx=idx>idxHref?idxHref:idx;
}
if(idx!=string::npos)
dest+=s.substr(0,idx);
else if(idx==string::npos&&pre_idx!=string::npos)
{
dest+=s;
break;
}
else
break;
s=s.substr(idx);
idxImg=0;
}while(1);
s=dest;
if(s.length()<20)
return false;
CStrFun::EraseStr(s,"\"");
CStrFun::EraseStr(s,"'");
CStrFun::EraseStr(s," ");
dest.clear();
idxImg=0;
/*
通过上面的提取我们得到<img src="http://www.">
我们再次提取
m_sLinkInfo4History="http://www.>
*/
const string delims(" #>");
string::size_type idxSrc=0;
do
{
if(s.empty())
break;
idxImg=CStrFun::FindCase(s,"img");
if(idxImg==string::npos)
break;
s=s.substr(idxImg+3);
if(s.empty())
break;
idxSrc=CStrFun::FindCase(s,"src");
if(idxSrc==string::npos)
break;
idx=s.find('=',idxSrc);
if(idx==string::npos)
break;
s=s.substr(idx+1);
while(s.length()>0&&s[0]==' ')
s.erase(0,1);
if(s.length()==0)
break;
idx=s.find_first_of(delims,1);
if(idx==string::npos)
break;
if(s.at(0)=='"')
dest+=s.substr(0,idx);
else
dest+='"'+s.substr(0,idx);
idx=s.find('>');
if(idx==string::npos)
break;
dest+='>';
s=s.substr(idx+1);
idx=s.find('<');
if(idx==string::npos)
{
dest+=s;
break;
}
dest+=s.substr(0,idx);
idxImg=0;
}while(1);
idx=0;
while((idx=dest.find("\"\""))!=string::npos)
{
dest.erase(idx,1);
}
s=dest;
return s.length()<20?false:true;
}
//判断strUrl是不是正规的url
bool CPage::NormalizeUrl(string &strUrl)
{
string::size_type idx=0;
//URL没有htp://协议名我们这里认为strUrl不是正规的URL
if(CStrFun::FindCase(strUrl,"http://")==string::npos)
return false;
//将http://www.baidu.com转化为http://www.baidu.com/
idx=strUrl.rfind('/');
if(idx<8)
{
strUrl+="/";
return true;
}
//将"/./"-->"/"
while((idx=strUrl.find("/./"))!=string::npos)
{
if(idx!=string::npos)
strUrl.erase(idx,2);
}
//将"xxxx/../yyy"-->xxx/yyy
while((idx=strUrl.find("/../"))!=string::npos)
{
string strPre,strBuf;
strPre=strUrl.substr(0,idx);
if(strUrl.size()>idx+4)
strBuf=strUrl.substr(idx+4);
idx=strPre.rfind("/");
if(idx!=string::npos)
strPre=strPre.substr(0,idx+1);
if(strPre.length()<10)
return false;
strUrl=strPre+strBuf;
}
//最后再次检测一下URL中是否还有"http://"协议名
if(CStrFun::FindCase(strUrl,"http://")!=0)
return false;
return true;
}
/*最终得到为搜索引擎准备的超链接
并将相对路径的URL和绝对路径的URL分别处理,同时,我们发现从一个网页中提取的超链接可以是相同的,这个时候
我们必须去重,这个函数用map容器很好的做到了这一点
还有一些URL不是正规的URL也要过滤
还有一些URL是必要过滤也要过滤--通过IsFilterLink(string strUrl)实现
*/
bool CPage::FindRefLink4SE()
{
if(m_sLinkInfo4SE.empty())
return false;
char *buffer=(char *)m_sLinkInfo4SE.c_str();
int urlnum=0,len;
char *ptr;
static char buf[URL_REFERENCE_LEN];
memset(buf,0,URL_REFERENCE_LEN);
len=strlen(buffer);
if(len<8)
return false;
len=len<URL_REFERENCE_LEN-1?len:URL_REFERENCE_LEN-1;
strncpy(buf,buffer,len);
/*
例如:m_sLinkInfo4SE="http://www.baidu.com/">百度"http://www.qq.com/">QQ
我们这里提取为
http://www.baidu.com 百度
http://www. QQ
*/
ptr=buf;
while(ptr-buf<len&&*ptr)
{
while(*ptr=='"'&&*ptr)ptr++;
if(!*ptr)
break;
this->m_RefLink4SE[urlnum].link=ptr;
while(*ptr&&*ptr!='>')
{
if(*ptr==' ')//在遇到'>'之前,出现了' '字符,我们必须将' '字符赋值为'\0'说明URL提取完了,因为URL不可能出现' '字符
*ptr='\0';
//例如: "http://www.baidu.com/" height=100 width=150>百度 出现空格说明还有其他的属性值
ptr++;
}//end while
if(!*ptr)
{
urlnum++;
break;
}//end if
if(*ptr=='>')
{
*ptr++='\0';
if(!*ptr)
{
urlnum++;
break;
}//end if
if(*ptr=='"')
m_RefLink4SE[urlnum].anchor_text=NULL;
else
{
m_RefLink4SE[urlnum].anchor_text=ptr;
while(*ptr&&*ptr!='"')ptr++;
if(!*ptr)
{
urlnum++;
break;
}//end if
if(*ptr=='"')
*ptr='\0';
}//ene if
}//end if
ptr++;
urlnum++;
if(urlnum==MAX_URL_REFERENCES)
break;
}//end while
m_nRefLink4SENum=urlnum;
typedef map<string,string>::value_type valType;
m_mapLink4SE.clear();
CUrl iUrl;
if(iUrl.ParseUrlEx(m_sUrl)==false)
{
cout<<"ParseUrlEx erro in CPage::FindRefLink4SE():"<<endl;
return false;
}
for(int i=0;i<m_nRefLink4SENum;i++)
{
string str;
string::size_type idx;
const string delims(" #");
str=m_RefLink4SE[i].link;
idx=str.find_first_of(delims,0);
if(idx!=string::npos)
str=str.substr(0,idx);
if(str.size()==0||str.size()<4||str.size()>URL_LEN-1)
continue;
string::size_type idx1;
idx1=CStrFun::FindCase(str,"http");
if(idx1!=0)//str有可能是相对路径
{
char c1=m_sUrl.at(m_sUrl.length()-1);
char c2=str.at(0);
if(c2=='/')//str一定是相对路径
{
if(iUrl.m_nPort!=80)
str="http://"+iUrl.m_sHost+":"+CStrFun::itos(iUrl.m_nPort)+str;
else
str="http://"+iUrl.m_sHost+str;
}
else if(c1!='/'&&c2!='/')
{
string::size_type idx;
idx=m_sUrl.rfind('/');
if(idx!=string::npos)
{
if(idx>6)
str=m_sUrl.substr(0,idx+1)+str;
else
str=m_sUrl+"/"+str;
}//end if
else
continue;
}
else// c2!='/' c1=='/';
{
if(c1=='/')
str=m_sUrl+str;
else
str=m_sUrl+"/"+str;
}//end if
}//end if
if(NormalizeUrl(str)==false)
continue;
if(IsFilterLink(str))
continue;
if(str==m_sUrl)//一个网页中提取的超链接是其本身,我就不要了,因为我们已经有了这个网页的URL了
continue;
else
{//这样做为的是给URL去重
if(m_RefLink4SE[i].anchor_text)//有URL的描述符
{
if(m_mapLink4SE.find(str)==m_mapLink4SE.end())
m_mapLink4SE.insert(valType(str,m_RefLink4SE[i].anchor_text));
}
else//没有URL的描述符---这个时候描述符为'\0'
{
if(m_mapLink4SE.find(str)==m_mapLink4SE.end())
m_mapLink4SE.insert(valType(str,"\0"));
}//end if
}//end if
}//end for
m_nRefLink4SENum=m_mapLink4SE.size();
return true;
}
//最终得到为历史网页存档准备的超链接
//并将相对路径的URL和绝对路径的URL分别处理,同时,我们发现从一个网页中提取的超链接可以是相同的,这个时候
//我们必须去重,这个函数用vector容器很好的做到了这一点
//还有一些URL不是正规的URL也要过滤
//还有一些URL是必要过滤也要过滤--通过IsFilterLink(string strUrl)实现
bool CPage::FindRefLink4History()
{
if(m_sLinkInfo4History.empty())
return false;
char *buffer=(char*)m_sLinkInfo4History.c_str();
int urlnum=0,len;
char *ptr ;
static char buf[URL_REFERENCE_LEN/2];
memset(buf,0,URL_REFERENCE_LEN/2);
len=strlen(buffer);
if(len<8)
return false;
len=len<URL_REFERENCE_LEN/2-1?len:URL_REFERENCE_LEN/2-1;
strncpy(buf,buffer,len);
/*
例如:m_sLinkInfo4History="http://www.">
我们这里提取为
http://www.
*/
ptr=buf;
while(ptr-buf<len &&*ptr)
{
while(*ptr=='"'&&*ptr)ptr++;
if(!*ptr)
break;
this->m_RefLink4History[urlnum].link=ptr;
while(*ptr&&*ptr!='>')
{
if(*ptr==' ')//在遇到'>'之前,出现了' '字符,我们必须将' '字符赋值为'\0'说明URL提取完了,因为URL不可能出现' '字符
*ptr='\0';
//例如: "http://www./" height=100 width=150>google 出现空格说明还有其他的属性值
ptr++;
}//end while
if(!*ptr)
{
urlnum++;
break;
}
if(*ptr=='>')
{
*ptr++='\0';
if(!*ptr)
{
urlnum++;
break;
}
if(*ptr=='"')
{
}
else
{
while(*ptr&&*ptr!='"')
ptr++;
if(!*ptr)
{
urlnum++;
break;
}
if(*ptr=='"')
*ptr='\0';
}//end if
}//end if
ptr++;
urlnum++;
if(urlnum==MAX_URL_REFERENCES/2)
break;
}//end while
this->m_nRefLink4HistoryNum = urlnum;
m_vecLink4History.clear();
CUrl iUrl;
if(iUrl.ParseUrlEx(m_sUrl)==false)
{
cout<<"ParseUrlEx error in CPage::FindRefLink4History():"<< endl;
return false;
}
for(int i=0;i<m_nRefLink4HistoryNum;i++)
{
string str;
str=m_RefLink4History[i].link;
if(str.size()==0||str.size()>URL_LEN-1||str.size()<4)
continue;
string::size_type idx1;
idx1=CStrFun::FindCase(str,"http");
if(idx1!=0)//str有可能是相对路径
{
char c1=m_sUrl.at(m_sUrl.length()-1);
char c2=str.at(0);
if(c2=='/')//str一定是相对路径
{
if(iUrl.m_nPort!=80 )
str="http://"+iUrl.m_sHost+":"+CStrFun::itos(iUrl.m_nPort)+str;
else
str="http://"+iUrl.m_sHost+str;
}
else if(c1!='/'&&c2!='/')
{
string::size_type idx;
idx=m_sUrl.rfind('/');
if(idx!=string::npos)
{
if(idx>6) // > strlen("http://..")
str=m_sUrl.substr(0,idx+1)+str;
else
str=m_sUrl+"/"+str;
}
else
{
continue;
}
}
else// c2!='/' c1=='/'
{
if(c1=='/')
str=m_sUrl+str;
else
str=m_sUrl+"/"+str;
}//end if
}//end if
if(NormalizeUrl(str)==false)
continue;
if(IsFilterLink(str))
continue;
if(str==m_sUrl)
continue;
else
{
vector<string>::iterator it;
it=find(m_vecLink4History.begin(),m_vecLink4History.end(),str);
if(it==m_vecLink4History.end())
m_vecLink4History.push_back(str);
}
}//end for
m_nRefLink4HistoryNum = m_vecLink4History.size();
return true;
}