//從網(wǎng)頁中提取出超鏈接信息
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;
//如果沒有從網(wǎng)頁中提取出為搜索引擎或者為歷史網(wǎng)頁存檔準備的超鏈接則返回false
if(!bFind4SE&&!bFind4History)
return false;
return true;
}
//從網(wǎng)頁體中提取出包含超鏈接信息的標識
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;
/*
例如:一個網(wǎng)頁中有如下超鏈接
<a >百度</a><a >QQ</a><img src="http://www."><......
我們將會提取為
m_sContentLinkInfo=>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;
/*
刪除所有的'\'字符 避免出現(xiàn)下面的情況---注意'\'是轉(zhuǎn)義字符"\\"
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=>QQ<img src="http://www.">
我們這里提取出>QQ 過濾掉<img src="http://www.">
因為<img src="http://www.">的超鏈接是為歷史網(wǎng)頁存檔準備的超鏈接
*/
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;
/*
通過上面的提取我們得到>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提取出為歷史網(wǎng)頁存檔準備的超鏈接
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=>QQ<img src="http://www.">
我們這里提取出<img src="http://www."> 過濾掉>QQ
因為>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是不是正規(guī)的url
bool CPage::NormalizeUrl(string &strUrl)
{
string::size_type idx=0;
//URL沒有htp://協(xié)議名我們這里認為strUrl不是正規(guī)的URL
if(CStrFun::FindCase(strUrl,"http://")==string::npos)
return false;
//將http://www.baidu.com轉(zhuǎn)化為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://"協(xié)議名
if(CStrFun::FindCase(strUrl,"http://")!=0)
return false;
return true;
}
/*最終得到為搜索引擎準備的超鏈接
并將相對路徑的URL和絕對路徑的URL分別處理,同時,我們發(fā)現(xiàn)從一個網(wǎng)頁中提取的超鏈接可以是相同的,這個時候
我們必須去重,這個函數(shù)用map容器很好的做到了這一點
還有一些URL不是正規(guī)的URL也要過濾
還有一些URL是必要過濾也要過濾--通過IsFilterLink(string strUrl)實現(xiàn)
*/
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==' ')//在遇到'>'之前,出現(xiàn)了' '字符,我們必須將' '字符賦值為'\0'說明URL提取完了,因為URL不可能出現(xiàn)' '字符
*ptr='\0';
//例如: "http://www.baidu.com/" height=100 width=150>百度 出現(xiàn)空格說明還有其他的屬性值
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)//一個網(wǎng)頁中提取的超鏈接是其本身,我就不要了,因為我們已經(jīng)有了這個網(wǎng)頁的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;
}
//最終得到為歷史網(wǎng)頁存檔準備的超鏈接
//并將相對路徑的URL和絕對路徑的URL分別處理,同時,我們發(fā)現(xiàn)從一個網(wǎng)頁中提取的超鏈接可以是相同的,這個時候
//我們必須去重,這個函數(shù)用vector容器很好的做到了這一點
//還有一些URL不是正規(guī)的URL也要過濾
//還有一些URL是必要過濾也要過濾--通過IsFilterLink(string strUrl)實現(xiàn)
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==' ')//在遇到'>'之前,出現(xiàn)了' '字符,我們必須將' '字符賦值為'\0'說明URL提取完了,因為URL不可能出現(xiàn)' '字符
*ptr='\0';
//例如: "http://www./" height=100 width=150>google 出現(xiàn)空格說明還有其他的屬性值
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;
}




