开始是main函数,在main.cpp
如果控制台参数是1个,就进行搜索:
CSearch iSearch;
iSearch.DoSearch();
如果控制台参数是2个,就运行网络爬虫:
CCrawl iCrawl(argv[2], "visited.all");
iCrawl.DoCrawl();
其中 argv[2]是inputfile visited.all是outputfile.
在DoCrawl函数里,初始化是把已经访问过的url加入到一个集合中去,
即调用函数GetVisitedUrlMD5(),这个函数里,主要是读取一个文件:
while( getline(ifsMD5,strMD5) ){
setVisitedUrlMD5.insert(strMD5);
}
setVisitedUrlMD5就是一个已经访问过的url集合
同理于 GetVisitedPageMD5();GetIpBlock();
GetUnreachHostMD5();
分别读取文件内容到setVisitedPageMD5,mapIpBlock,setUnreachHostMD5
set<string> setVisitedUrlMD5;
set<string> setVisitedPageMD5;
map<unsigned long,unsigned long> mapIpBlock;
其中 setUnreachHostMD5 是从文件读出不能到达的url,然后对这个url取了一个MD5值,存进内存中这个集合的.
这些前期工作做完以后,就开始读取种子文件,也就是开始爬取采用的起始url.
先是打开所有的输出流文件,函数是OpenFilesForOutput().也就是将来爬取得结果存放的文件.这里面包括索引文件(专门的类来打开),visited.url,link4SE.url,link4History.url(这个可能存放的是一篇HTML中的图像链接),unreach host file,visited url md5 file,visited page md5 file.
for(unsigned int i=0; i< NUM_WORKERS; i++){
if( pthread_create( &tids[i], NULL, start, this))
cerr << "create threads error" << endl;
}
启动10个线程,每个线程执行的函数是start,参数是this指针
在启动线程的同时,读取种子文件的url
while( getline(ifsSeed, strUrl) ){
//对读到的url进行一些处理后,调用AddUrl(strUrl)函数
AddUrl(strUrl.c_str());
}
也同时读取未访问的url
while( getline(ifsUnvisitedUrl, strUrl) ){
AddUrl(strUrl.c_str());
}
开看AddUrl函数做了哪些工作
AddUrl(const char * url)
在这个函数里,先判断是否是图像类型链接,是的话就加入m_ofsLink4HistoryFile 集合
string strUrl = url;
if (iUrl.IsImageUrl(strUrl))
{
if( m_ofsLink4HistoryFile ){
pthread_mutex_lock(&mutexLink4HistoryFile);
m_ofsLink4HistoryFile << strUrl << endl;;
pthread_mutex_unlock(&mutexLink4HistoryFile);
}
return;
}
在通过函数iUrl.ParseUrlEx(strUrl) ,解析strUrl代表的各种信息,如Host,scheme,port 以及request
如果不是图片链接,就把这个Host的MD5摘要加入到集合setUnvisitedUrlMD5中去,Host来源是iUrl.m_sHost成员.
CMD5 iMD5;
iMD5.GenerateMD5( (unsigned char*)iUrl.m_sHost.c_str(), iUrl.m_sHost.size() );
string strDigest = iMD5.ToString();
setUnvisitedUrlMD5.insert(strDigest);注意这个保存的是MD5
之后把<host,url>存到一个map映射里去 mmapUrls.insert(mvalType( iUrl.m_sHost, strUrl));
这个mmap里保存的是没有访问过的url,不是MD5,上面的那个集合是用来判断用的,
实际的线程函数会用到mmap
再来看前面说的线程函数start
start(arg)-->fetch(arg)
参数arg是CCrawl对象指针
在函数fetch中,先打开天网索引文件,以便存放结果
string ofsName = DATA_TIANWANG_FILE + "." + CStrFun::itos(pthread_self());
CTianwangFile tianwangFile(ofsName);
再打开一个Link4SEfile文件(Link4SE.raw),ofsName = DATA_LINK4SE_FILE + "." + CStrFun::itos(pthread_self());
CLink4SEFile link4SEFile(ofsName);
在fetch函数里,遍历mmap,取出<host,url>对
multimap<string,string>::iterator it=mmapUrls.begin();
取出url:
string strUrl = (*it).second;
再调用下载函数
(( CCrawl* )arg)->DownloadFile(&tianwangFile,&link4SEFile,iUrl,nGSock);
开始下载实际网页.
在DownloadFile函数里,实际下载的功能由file_length = http.Fetch( strUrlLocation, &downloaded_file, &fileHead, &location, &nSock);这句来实现.
char *downloaded_file = NULL,
*fileHead = NULL,
*location = NULL;
下载来的网页文件保存在downloaded_file,fileHead,location内存中.
之后把这些内容作为参数传递进CPage对象里.
CPage iPage(iUrl.m_sUrl, strUrlLocation, fileHead, downloaded_file, file_length);
对已经爬取了的网页url处理,把它的MD5插入到已经爬取网页url集合中去
iMD5.GenerateMD5( (unsigned char*)iUrl.m_sUrl.c_str(), iUrl.m_sUrl.length() );
strDigest = iMD5.ToString();
if( setVisitedUrlMD5.find(strDigest) != setVisitedUrlMD5.end() ) {
//找到集合中有这个元素,其实
//可以不用检查的
cout << "!vurl: "; //1.crawled already
return; //立刻返回
}
setVisitedUrlMD5.insert(strDigest);
SaveVisitedUrlMD5(strDigest);//写入已经爬取的url的文件
之后 把网页内容写入天网格式文件
SaveTianwangRawData(pTianwangFile, &iUrl, &iPage);
对爬取的网页内容提取超链接,这是用Lex完成的,上篇已经写过了TSE中的Lex流程.
先是struct uri page_uri;
uri_parse_string(iPage.m_sUrl.c_str(), &page_uri);
把该网页的url由String转换成结构来存放.
之后调用
hlink_detect_string(iPage.m_sContent.c_str(), &page_uri, onfind, &p);
就是上篇写的处理网页类.
再来看看onfind函数