[wbia 1]表示web based information architecture作业1的第1部分,搜索到这篇日志的读者可以直接忽略之。 我对heritrix的了解较浅,希望此文对第一次用爬虫的程序猿有帮助。如果有什么错误请直接留言指正,不胜感激。 heritrix是个开源爬虫,可以比较自由的配置爬取过程。heritrix可以获取完整的、精确的、站点内容的深度复制。包括获取图像以及其他非文本内容。抓取并存储相关的内容。对内容来者不拒,不对页面进行内容上的修改。重新爬行对相同的URL不针对先前的进行替换。爬虫通过Web用户界面启动、监控、调整,允许弹性的定义要获取的URL。 一、关于安装和运行(部分内容转自 http://blessed24.iteye.com/blog/969777 ) 1. 下载:到www.sourceforge.net网站搜索heritrix,然后下载下来heritrix-1.14.4-RC1.zip 2. 安装 (1).解压heritrix-1.14.4-RC1.zip,假设解压到了c盘根目录下并把解压后的文件夹命名为heritrix (2).进入c:\heritrix\conf复制文件jmxremote.password.template到c:\heritrix下并把文件重新命名为jmxremote.password, 然后修改其内容为下: monitorRole @123456789@ ->monitorRole admin (@于@之间设置的是密码,后面是->用户角色用户名) controlRole @123456789@ ->controlRole shi 并设置文件jmxremote.password的属性为只读 (3).进入c:\heritrix\conf打开文件heritrix.properties,修改其中的几项key-value值 heritrix.cmdline.admin = admin:770629 (用户名:密码) heritrix.cmdline.port = 8080 (heritrix服务器默认端口号8080,保证该端口不被占用就不用改了) (4).打开cmd,切换目录到c:\heritrix\bin 然后敲入命令:heritrix --admin=admin:123456789 由于heritrix是开源软件,如果需要更复杂的改动,可以搜索并下载其源码heritrix-1.14.0-RC1-src.zip,并参见 http://blessed24.iteye.com/blog/969777 进行Eclipse的配置。heritrix的配置比较灵活,暂时无需修改源码。 二、关于抓取前配置 1. 进行完“一”之后,即可用过浏览器访问heritrix的交互界面,如果是在本机配置的,访问网址http://127.0.0.1:8080,如果配置在服务器上,可以访问服务器的ip+端口号。 2.开始一个简单的抓取任务(部分内容转自 http://www.iteye.com/topic/84206 ) (1) WUI的上边的导航栏选择"Jobs",呈现的第一项是"Create New Job",选择第四小项"With defaults"。输入项的前两项Name和Description随意,Seeds非常重要:http://www.baidu.com/注意最后一个反斜杠必须。 (2) 选择下边的"Modules",进入Module配置页(Heritrix的扩展功能都是通过模块概念实现的,可以实现自己的模块完成自己想要的功能)。其中第一项 "Select Crawl Scope" 使用默认的 "org.archive.crawler.deciderules.DecidingScope"。倒数第三项 "Select Writers" 删除默认的 "org.archive.crawler.writer.ARCWriterProcessor" ,后添加"org.archive.crawler.writer.MirrorWriterProcessor",这样执行任务的时候抓取到的页面会以镜像的方式放在本地的目录结构中,而不是生成ARC存档文件。 (3) 选择"Modules"右边的"Submodules",在第一项内容中 "crawl-order ->scope->decide-rules->rules" 删除掉其中的 "acceptIfTranscluded" (org.archive.crawler.deciderules.TransclusionDecideRule) 的这一项抓取作用域的规则。否则当Http请求返回301或者302时Heritrix会去抓取其他域下的网页。 (4) 在WUI的第二行导航栏中选择"Settings"进入Job的配置页面,其中主要修改两项:http-headers 下的user-agent 和from,他们的"PROJECT_URL_HERE" 和 "CONTACT_EMAIL_ADDRESS_HERE" 替换为自己的内容("PROJECT_URL_HERE" 要以 "http://" 开头) (5) 在WUI的第二行导航栏中选择最右边的"Submit job" (6) 在WUI的第一行导航栏中选择第一项的"Console",点击"Start",抓取任务正式开始,时间长短有网络状况和所抓取网站的深度有关。 3. 个性化配置 (1).域名正则表达式 我们的任务是抓取pku深研院以及其下属的系、所、中心的网页。用上面方法抓取存在一个问题,不能抓取非pkusz.edu.cn域名下的系、所、中心。而如果放开域名限制,那么就会抓出北大深研院这个范围。这时候需要用正则表达式进行筛选。我的做法是这样的,Modules中选择org.archive.crawler.deciderules.DecidingScope,然后在Submodule的rules中选择org.archive.crawler.deciderules.MatchesListRegExpDecideRule,其他全部删掉。在setting中的regexp-list中,增加五个正则表达式,.*szpku.* .*pnbs.pku.* .*stl.pku.* .*pkusz.* .*szcie.pku.*,注意这里,java的正则表达式的通配符是.*,而不是*。另外,module以及submodule的选择,会影响setting里的内容,选择了相应的module,才有相应的setting。 (2).限制抓取文件类型 大部分时间我们关心的是网页的文本,所以不太希望抓进来一堆图片、视频、压缩包什么的,所以需要限制下。在submodule中,添加org.archive.crawler.deciderules.MatchesFilePatternDecideRule,对于每个submodule的含义,可以通过点击旁边的问号得知。这个Submodule的作用就是在接受文件的时候,接受不是某几个类型的文件。在setting中设置,accept all,表示接受所有不是以下类型的文件: Images: .bmp, .gif, .jp(e)g, .png, .tif(f) Audio: .mid, mp2, .mp3, .mp4, .wav Video: .avi, .mov, .mpeg, .ram, .rm, .smil, .wmv Miscellaneous: .doc, .pdf, .ppt, .swf 这样,抓取得到的镜像会小很多。 (3). 修改编码类型 抓到中文的时候,可能会显示乱码,需要修改下编码格式。在setting中搜索到encoding,在空中填写UTF-8。 三、关于抓取过程 整个抓取过程持续了三天多,不过大部分的网页是在第一个晚上抓的。由于爬虫在不停的填充队列,所以越到后来抓的越慢,再就是我猜测可能是后来链接比较深的url是比较少访问到的url,同时访问速度也是比较慢的。第一个晚上抓到了90%,剩下的将近三天只抓取到95%,实在等不到,就将其停止了。总共镜像抓了5.31G,有三十多万个文件。应该算比较多的了,日志有1.27G。看了下,应该都是深研院的网站,到此抓取算告一段落了,下一步开始计算pagerank。 四。计算 这里,我们得到了网页间的链接关系,可以比较方便的开始进行pagerank的计算了。首先需要预处理得到整个链接关系图。对于一个url的定义如下:
struct url
{
int id;
string urlTxt;
int outDegree;
vector<int> refList;
int inDegree;
double score[2];
bool operator > (const url& u) const
{
return score[scoreIdx]>u.score[scoreIdx];
}
};
预处理得到Url集合,为了加速运算,将每个url编号,在图中只存储每个url的编号。
void initUrl()
{
cout<<nowTime()<<" Begin init user."<<endl;
ifstream in(mapPath.c_str());
string urlStr;
int id=0;
while(in>>urlStr)
{
if(urlStr.substr(urlStr.size()-4)==".css"||urlStr.substr(urlStr.size()-3)==".js")
continue;
if(urlId.find(urlStr)==urlId.end())
{
urlId[urlStr]=id;
url newUrl;
newUrl.id=id;
newUrl.inDegree=0;
newUrl.outDegree=0;
newUrl.urlTxt=urlStr;
urls.push_back(newUrl);
id++;
}
}
in.close();
}
得到整个图关系代码如下:
void initNet()
{
cout << nowTime() << ": Begin init net." << endl;
ifstream in(mapPath.c_str());
string fromStr,toStr,rootStr;
in>>rootStr;
while(in>>fromStr>>toStr)
{
if(links.find(fromStr+"#"+toStr)!=links.end()) continue;
else links.insert(fromStr+"#"+toStr);
if(fromStr.substr(fromStr.size()-4)==".css"||toStr.substr(toStr.size()-3)==".js")
{
continue;
}
int from=urlId[fromStr];
int to=urlId[toStr];
urls[to].refList.push_back(from);
urls[to].inDegree++;
urls[from].outDegree++;
}
in.close();
cout << nowTime() << ": Complete init net." << endl;
}
计算pagerank的代码如下:
int calPagerank()
{
cout << nowTime() << ": Begin calculate pagerank." << endl;
int oldIdx = 0;
int newIdx = 1;
int urlNum=urls.size();
for(int i=0;i<urlNum;i++) urls[i].score[oldIdx]=sum/urlNum;
for(int iteration=0; iteration <= 10000; iteration++)
{
cout<<nowTime()<<": iteration "<<iteration<<endl;
for(int i =0;i<urlNum;i++)
{
urls[i].score[newIdx] = 0;
}
double perSum = 0;
for(int i = 0; i < urlNum; i ++)
{
urls[i].score[newIdx] = sum*rem/urlNum;
if(urls[i].inDegree> 0)
{
for(vector<int>::iterator iter = urls[i].refList.begin(); iter != urls[i].refList.end(); iter++)
{
int id = *iter;
urls[i].score[newIdx] += (1-rem)*urls[id].score[oldIdx]/urls[id].outDegree;
}
}
perSum += urls[i].score[newIdx];
}
for(int i=0;i<urlNum;i++)
{
urls[i].score[newIdx] = urls[i].score[newIdx] / perSum;
}
double dif = 0;
for(int i = 0; i < urlNum; i ++)
{
dif += abs(urls[i].score[newIdx] - urls[i].score[oldIdx]);
}
if( dif < bound )
{
break;
}
oldIdx = newIdx;
newIdx = 1 - oldIdx;
}
cout << nowTime() << ": Complete calculate pagerank." << endl;
return newIdx;
}
将得到的pagerank结果写入文件:
void pagerankWriteToFile()
{
cout << nowTime() << ": Begin write pagerank to file." << endl;
ofstream out(rankPath.c_str(), ios::trunc|ios::out);
out << setiosflags(ios::fixed) << setprecision(20);
sort(urls.begin(),urls.end(),greater<url>());
for(int i=0;i<urls.size();i++)
{
out<<urls[i].urlTxt<<"\t"<<urls[i].score[scoreIdx]<<"\t"<<urls[i].inDegree<<endl;
}
out.close();
cout << nowTime() << ": Complete write pagerank to file." << endl;
}
进行完这些计算后,和同学交流,发现得到的结果出入较大。交流发现,是对出度为0的点没有处理的原因。由于我抓的图是部分的图,所以出度为0的点,实际上是和其他网络交流的一个Saddle,但是应有的pagerank并没有从这里流出去。这个出度为0的点像黑洞一样只吸收pagerank值却不支出pagerank值。所以,需要将这些出度为0的点进行特殊处理,即认为这些节点向其他每个节点都连了一条边。经过修改后计算pagerank的代码如下:
int calPagerank()
{
cout << nowTime() << ": Begin calculate pagerank." << endl;
int oldIdx = 0;
int newIdx = 1;
int urlNum=urls.size();
for(int i=0;i<urlNum;i++) urls[i].score[oldIdx]=sum/urlNum;
for(int iteration=0; iteration <= 10000; iteration++)
{
cout<<nowTime()<<": iteration "<<iteration<<endl;
for(int i=0;i<urlNum;i++) urls[i].score[newIdx] = sum*rem/urlNum;
for(int i=0;i<urlNum;i++)
{
if(urls[i].outDegree!=0) continue;
for(int j=0;j<urlNum;j++)
{
if(i==j) continue;
urls[j].score[newIdx]+=(1-rem)*urls[i].score[oldIdx]/(urlNum-1);
}
}
double perSum = 0;
for(int i = 0; i < urlNum; i ++)
{
if(urls[i].inDegree> 0)
{
for(vector<int>::iterator iter = urls[i].refList.begin(); iter != urls[i].refList.end(); iter++)
{
int id = *iter;
urls[i].score[newIdx] += (1-rem)*urls[id].score[oldIdx]/urls[id].outDegree;
}
}
perSum += urls[i].score[newIdx];
}
for(int i=0;i<urlNum;i++)
{
urls[i].score[newIdx] = urls[i].score[newIdx] / perSum;
}
double dif = 0;
for(int i = 0; i < urlNum; i ++)
{
dif += abs(urls[i].score[newIdx] - urls[i].score[oldIdx]);
}
if( dif < bound )
{
break;
}
oldIdx = newIdx;
newIdx = 1 - oldIdx;
}
cout << nowTime() << ": Complete calculate pagerank." << endl;
return newIdx;
}
以下是程序完整的C++代码:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <time.h>
#include <map>
#include <list>
#include <cstdlib>
#include <cstring>
#include <string>
#include <memory.h>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
using namespace std;
int scoreIdx;
struct url
{
int id;
string urlTxt;
int outDegree;
vector<int> refList;
int inDegree;
double score[2];
bool operator > (const url& u) const
{
return score[scoreIdx]>u.score[scoreIdx];
}
};
map<string,int> urlId;
set<string> links;
vector<url> urls;
double sum = 1.0;
double rem = 0.15;
double bound = 0.000001;
string mapPath="linkMap.txt";
string rankPath="pagerank.txt";
string nowTime()
{
char outTime[64];
time_t t = time(0);
strftime(outTime, sizeof(outTime), "%Y/%m/%d %X", localtime(&t));
return outTime;
}
void initUrl()
{
cout<<nowTime()<<" Begin init user."<<endl;
ifstream in(mapPath.c_str());
string urlStr;
int id=0;
while(in>>urlStr)
{
if(urlStr.substr(urlStr.size()-4)==".css"||urlStr.substr(urlStr.size()-3)==".js")
{
continue;
}
if(urlId.find(urlStr)==urlId.end())
{
urlId[urlStr]=id;
url newUrl;
newUrl.id=id;
newUrl.inDegree=0;
newUrl.outDegree=0;
newUrl.urlTxt=urlStr;
urls.push_back(newUrl);
id++;
}
}
in.close();
}
void initNet()
{
cout << nowTime() << ": Begin init net." << endl;
ifstream in(mapPath.c_str());
string fromStr,toStr,rootStr;
in>>rootStr;
int cnt=0;
while(in>>fromStr>>toStr)
{
if(links.find(fromStr+"#"+toStr)!=links.end()) continue;
else links.insert(fromStr+"#"+toStr);
if(fromStr.substr(fromStr.size()-4)==".css"||toStr.substr(toStr.size()-4)==".css"||fromStr.substr(fromStr.size()-3)==".js"||toStr.substr(toStr.size()-3)==".js")
{
continue;
}
cnt++;
int from=urlId[fromStr];
int to=urlId[toStr];
urls[to].refList.push_back(from);
urls[to].inDegree++;
urls[from].outDegree++;
}
cout<<"The link number is " << cnt<<endl;
in.close();
cout << nowTime() << ": Complete init net." << endl;
}
int calPagerank()
{
cout << nowTime() << ": Begin calculate pagerank." << endl;
int oldIdx = 0;
int newIdx = 1;
int urlNum=urls.size();
for(int i=0;i<urlNum;i++) urls[i].score[oldIdx]=sum/urlNum;
for(int iteration=0; iteration <= 10000; iteration++)
{
cout<<nowTime()<<": iteration "<<iteration<<endl;
for(int i=0;i<urlNum;i++) urls[i].score[newIdx] = sum*rem/urlNum;
for(int i=0;i<urlNum;i++)
{
if(urls[i].outDegree!=0) continue;
for(int j=0;j<urlNum;j++)
{
if(i==j) continue;
urls[j].score[newIdx]+=(1-rem)*urls[i].score[oldIdx]/(urlNum-1);
}
}
double perSum = 0;
for(int i = 0; i < urlNum; i ++)
{
if(urls[i].inDegree> 0)
{
for(vector<int>::iterator iter = urls[i].refList.begin(); iter != urls[i].refList.end(); iter++)
{
int id = *iter;
urls[i].score[newIdx] += (1-rem)*urls[id].score[oldIdx]/urls[id].outDegree;
}
}
perSum += urls[i].score[newIdx];
}
for(int i=0;i<urlNum;i++)
{
urls[i].score[newIdx] = urls[i].score[newIdx] / perSum;
}
double dif = 0;
for(int i = 0; i < urlNum; i ++)
{
dif += abs(urls[i].score[newIdx] - urls[i].score[oldIdx]);
}
if( dif < bound )
{
break;
}
oldIdx = newIdx;
newIdx = 1 - oldIdx;
}
cout << nowTime() << ": Complete calculate pagerank." << endl;
return newIdx;
}
void pagerankWriteToFile()
{
cout << nowTime() << ": Begin write pagerank to file." << endl;
ofstream out(rankPath.c_str(), ios::trunc|ios::out);
out << setiosflags(ios::fixed) << setprecision(20);
sort(urls.begin(),urls.end(),greater<url>());
for(int i=0;i<urls.size();i++)
{
out<<urls[i].urlTxt<<"\t"<<urls[i].score[scoreIdx]<<"\t"<<urls[i].inDegree<<endl;
}
out.close();
cout << nowTime() << ": Complete write pagerank to file." << endl;
}
int main()
{
initUrl();
initNet();
scoreIdx = calPagerank();
pagerankWriteToFile();
return 0;
}
经过如此计算后,结果虽然有一定变化,但是变化不大。也许是数据集或者抓取过程的原因,我也没在我的代码以及他的代码中发现什么问题。如果有发现代码问题的,请留言,不胜感激。下面将对得到的结果进行介绍。 |