Web crawler作业报告

类别:编程语言 点击:0 评论:0 推荐:

Web crawler作业报告
 

 

A.      Result

对net.pku.edu.cn测试的结果:

站点内html文件数:1525;

站点内doc文件数:6;

站点内pdf文件数:825;

单词“net”出现的次数(不区分大小写):306;

num of url within the host:8313;

站点内死链数(不包括连到站点外的死链):360;

//所有的死链保存在文件deadlink.txt中。

 

B.      过程

1)       分析所提供的天网crawler library类库及其成员函数的功能;写理解库函数功能所用的测试程序。大多数类成员函数都能从其名称或类型猜到它的作用。但像CHttp::const char*get_body(int&len),CHTMLLexic::CHTMLItem*output()和CHTMLItem对象的内容等不能一下子理解出,但经过一些简单的测试也不难理解其内容和功能。(主要在10-17完成)

2)       做好web crawler的框架。该框架只是简单对站点内的链接进行遍历,除了sfetch(),输出正在访问的url和做遍历必须的工作外什么也不做。由于对c++不熟悉,遇到很多语言方面的问题,主要是怎么使用queue和hash_set的问题。请教了师兄,在网上查STL文档,总算学会了怎么样使用queue和hash_set。(10-18)

3)       发现在sfetch一些像rm格式的大文件时程序很久没有进展。由于对于这种指向大文件的url,我只是需要判断它是否为死链,首先想到的是能不能不用sfetch而直接判断该url指向的资源是否存在。请教了师兄,师兄告诉我两个办法:一个是建立socket连接然后请求应答头,根据应答头的status来判断;另一个办法是我自己也有想到的,用另一个线程对正在sfetch的线程计时,如果计时超过了设定的timeout而sfetch又没有跳出,那一定是遇到大文件了,然后用某种方法让sfetch跳出正在进行的工作。第一种方法听起来简单一些,于是决定用第一种方法来解决这个问题。但由于对c++不熟悉,我把这个问题搁到后面,而直接采取简单的办法先:把后缀名为rm,asf,mov等的链接过滤掉。(10-19)

4)       遇到一些小问题。像baseurl,运行时segmentation fault,由于老师提供的test.cpp造成的“误导”,最先一直把CHTMLRef(constchar*html,intlen,constchar*baseurl)中的baseurl设为http://net.pku.edu.cn/,后改为current_url.str().c_str()。Segmentation fault是指针越界的问题,遇到了好几次,都一一解决了。(主要是10-20的工作)

5)       发现crawl时陷入了死循环,有一次运行了整个通宵都没有结束,发现是存储访问过的url的hash_set的问题。后通过一些测试程序对hash_set进行了分析,发现最先insert到hash_set中的元素,在经过大量insert之后,莫名奇妙地消失了。后又用元素类型为char*或const char*的set,map等,也相继出现类似的问题。分析:用char*或const char*元素类型的container都会出现曾经insert过的元素从container中无端消失的问题。于是改用string作container的元素类型,但hash_set似乎不支持用string作为其元素类型,于是改用map,查找一个url的时间复杂度由使用hash_set时的O(1)变为O(lgn)。不过map容器可使一个url与一个int类型的元素对应,该int类型用在此程序中可用来标记其是否为死链。(10-23)

6)       经过多日的独立探索(除了使用goole,baidu外未请教其他人),终于学会在c++中建立socket连接,发送请求和接受应答信息。学会了才发现这其实很简单,但万事开头难,并且c++网络编程也的确较麻烦,因此我进入c++网络编程的大门颇费了不少周折。使用这种方法,对每一个url,我先对其建立socket连接,发送请求"HEAD "+url.str()+""+" HTTP/1.0\n\n",recv其返回的应答信息。对status和content-type进行分析,这就避免对每一个url指向的文件都要sfetch再进行分析浪费很多时间了。(10-27)

7)       对程序的输出,注释等进行改善。程序完成。进行最后一次crawl。(10-28)

 

C.      程序特点

1)       程序一共有五个函数:int str_count(const char* body,int body_len,const char* key ),string get_head(CURL url),int  isDeadLink(CURL url),string content_type(CURL url),int main()。

2)       int str_count(const char* body,int body_len,const char* key ):用于计数网页内容body中出现了多少个key所存的单词(在本程序中key为”net”)。此函数计数的是浏览器中可见的文本内容中出现的key单词数。对于network这样的字符串中出现的”net”不计数,但对net.pku.edu.cn中出现的”net”计数,也就是查看字符串”net”左右的是否为字母,若不是字母,才计数。不区分大小写。返回body中出现key的次数。

3)       string get_head(CURL url):建立对url所在的host的socket连接,发送请求"HEAD "+url.str()+""+" HTTP/1.0\n\n",revc应答头。函数以string类型返回应答头信息。

4)       int  isDeadLink(CURL url):调用get_head(url),查看返回的应答头中的status状态信息是否>=400,若是,则断定为死链,返加1,否则返回0。

5)       string content_type(CURL url):调用get_head(url),取出返回的应答头中的content-type信息,并返回此信息。

6)       int main():主要数据结构是一个队列url_tobevisit和一个map visited_url。url_tobevisit用来存储从网页中取出但还未访问的url;visited_url用来存储已经访问过的url,且若某url url_str为死链,则visited_url[url_str]=0,否则使其为1。

由于在net.pku.edu.cn站点指向外部的链接有很多是国外的链接,由于无法连接而无法判断这些链接是否为死链,故索性将进入队列的链接全限制为站点内部的链接。

从队列中弹出的url首先用isDeadLink()判断是否是死链,这样即使是指向rm大文件的url也能很快判断它是否为死链,与判断一般网页文件url的速度没有区别。如果判断为死链,则死链计数加1,且不再对该url进行其它处理,直接取队列中的下一个url。若不为死链,则继续判断其content-type,根据content-type对html,doc,pdf资源的数量进行计数。如果该url的content-type为“text/html”,再对其进行sfetch(),从取出的body中取出url,将其中未访问过的加入队列。

当该站点所有url都已遍历完毕,则进行一系列结果输出工作。遍历visited_url并将其中的死链写入deadlink.dat文件(根据visited_url元素的second是否为1判断其是否为死链)。打印出html,doc,pdf,deadlink和”net”出现的次数。

7)       程序的主要特点是效率高,准确,严谨,风格好。效率高:建立socket连接请求应答头判断是否为死链及获取content-type,只sfetch html类型的文件;准确,严谨:对一个站点内的url都进行分析,而不对大文件消极地避开;风格好:模块清晰。

 

D.      问题,需要注意的

语言方面的问题主要是至今还搞不清楚为什么用char 或const char*作为container的元素类型会在container中元素较多的时候无端地消失一些元素。

如果在socket连接后忘了close它,程序运行中建立的socket连接越来越多,当连接多到一定数量时服务器将不能接受,程序将会被abort。所以一定要记住关闭socket连接。

程序的主要时间耗费在网络连接和sfetch上,要提高crawler的速度,可采用多线程技术。由于程序等待时间较多,适于多个线程运行,这样可以同时连接多个url或sfetch多个网页内容。

最后一个问题:如果要我自己写一个模块来取出html网页中浏览器中文本内容(即tag的内容),应该怎么写呢?

感想:任何一个看起来很小的东西要做得很完美都不容易。搭建一个web crawler的粗糙框架只需要半天的时间,但要做得很完善可能两个星期都不够。写程序的主要时间用在查在线文档上,其次是调试。

 

10/28/2004

 

本文地址:http://com.8s8s.com/it/it26127.htm