“网络蜘蛛”从互联网上抓取网页,把网页送入“网页数据库”,从网页中“提取URL”,把URL送入“URL数据库”,“蜘蛛控制”得到网页的URL,控制“网络蜘蛛”抓取其它网页,反复循环直到把所有的网页抓取完成。
系统从“网页数据库”中得到文本信息,送入“文本索引”模块建立索引,形成“索引数据库”。同时进行“链接信息提取”,把链接信息(包括锚文本、链接本身等信息)送入“链接数据库”,为“网页评级”提供依据。
“用户”通过提交查询请求给“查询服务器”,服务器在“索引数据库”中进行相关网页的查找,同时“网页评级”把查询请求和链接信息结合起来对搜索结果进行相关度的评价,通过“查询服务器”按照相关度进行排序,并提取关键词的内容摘要,组织最后的页面返回给“用户”。
——万维网Web自动搜索引擎(技术报告)邓雄(Johnny Deng) 2006.12
图1-1是整个程序的架构图。
图1-1 程序架构图
开发环境:
简单的说网络爬虫就是一只会嗅着url(链接)爬过成千上万网页,并把网页内容搬到你电脑上供你使用的苦力虫子。
如图(这里少张图)所示:你给定爬虫的出发页面A的url,它就从起始页A出发,读取A的所有内容,并从中找到3个url,分别指向页面B、C、D,然后它就顺着链接依次抓取B、C、D页面的内容,并从中发现新的链接,然后沿着链接继续爬到新的页面–>抓取内容–>分析链接–>爬到新的页面…….直到找不到新的链接或者满足了人为设定的停止条件为止。你可以对爬虫带回来的网页内容继续进行分析、处理,以满足你的需求。
至于这只虫子前进的方式,则分为广度优先搜索(BFS)和深度优先搜索(DFS)。在这张图中BFS的搜索顺序是A-B-C-D-E-F-G-H-I,而深度优先搜索的顺序是A-B-E-I-C-F-D-G-H。
我们使用BFS的方式对网页进行爬取。直接使用python的内建类型 list存储url地址,每次从一个页面中得到所有链接url地址后,将其从待抓取队列的队首删去,将得到的所有链接并入待抓取队列的末尾。
网页爬取模块主要包括一下几个函数。
索引就像图书馆每个书架上的小牌子,你要找某一本书,譬如一本学习python语言的书,你就先搜索“信息与计算机分部”,然后搜索“编程语言”,这样就可以在相应的架子上找到你想找的书了。搜索引擎的索引与此类似,所不同的是它会为所有网页的每个词语都建立索引,当你输入一串搜索字符串,程序会先进行分词,然后再依照每个词的索引找到相应网页。比如在搜索框中输入“从前有座山山里有座庙 小和尚”,搜索引擎首先会对字符串进行分词处理“从前/有/座山/山里/有/座庙/小和尚”,然后按照一定规则对词做布尔运算,比如每个词之间做“与”运算,然后在索引里搜索“同时”包含这些词的页面。
当然在本实验中所做的这个简陋的引擎的索引功能也非常简陋,通过分析网页源码,我们利用网页中meta元素的内容,生成keywords。如图2-1:
图2-1 页面中的keywords
Keywords 的内容比较简单,我们可以按照图书的书名,作者,出版社,出版时间等信息搜索图书。
这个以谷歌创始人Larry Page命名的算法大概是互联网最著名的算法了,当年这个算法,帮助谷歌在搜索质量上一举打败了当时最流行的Yahoo,AltaVista等引擎,为谷歌崛起成为世界上最了不起的科技巨头立下了汗马功劳。
page rank是用来衡量网页质量如何的,索引找到的网页会用pagerank算法计算出一个PR值,这个得分在呈现结果排序中占一定权重,得分高的网页将被排在前面显示给用户。那么它是怎么实现的呢?它基于这样一种假设,越多的网页链接到这个网页,说明这个网页的在整个网络中受认可的程度越高,也即这个网页质量越好——这等同于互联网上其他所有网页给它投票,有一个链到它的链接说明给它投了一票,票数越高越好。但是是不是所有的票数权重都一样呢?显然不是的,链接到这个网页的网页本身PR值越高,则其投票的权重应该越大,这就好像董事会投票大股东的一票比小股东的一票更有份量。但这就存在一个问题了,你要知道一个页面的PR值就需要知道链接到它的页面的PR值,而链接到它的页面的PR值又需要依赖于其他页面的PR值,如此一直追究下去就变成了鸡生蛋蛋生鸡的问题了。现假设初始条件,互联网所有页面PR值的初始值为1/N,N是整个网络页面的总数,那么用 公式进行迭代,其中p(j)代表链接到p(i)的网页,Lp(j)代表这个页面中链接的数目,之所以要除以页面链接数量,这是很好理解的,当一个页面链接数量很多时,它每个链接投票的份量就相应地被分散了。可以证明,按照这个公式求取每个页面i的值,并进行迭代,当迭代很多轮时,PRp(i)的值会趋于一个稳定值,在应用中迭代次数一般取10次,超过10次以后PRp(i)的值变化就很小了。
现在还是存在一个问题,如果一个页面没有任何外部页面链接到它,那么它的PR值就是0了呢?为了使PRp(i)函数能够更平滑,我们在公式里增添一个阻尼系数d,在这里我们不妨取0.8,公式变成如下形式:
这样的话即使某个页面孤立存在,它的PR值也不至于变为零,而是一个很小的值。我们也可以从另一个角度理解阻尼系数,PR值衡量的其实是某个页面被访问的可能性的大小,链接到它的网页越多,通过这些链接点开它的可能性当然就越大,但在没有任何外部链接的情况下,一个网页是不是就不可能被访问呢?当然不是,因为还可以从地址框直接键入url访问,而且也会有人希望通过搜索引擎找到这个页面,所以我们在公式里加了阻尼系数,当然这里给1-d取一个比较小的数才可以。
当然了,网页PR值也不是唯一一个决定网页排名的因素,页面和用户搜索词(query)的相关程度也是重要的衡量因素,与搜索词相关度越高的页面应该放在越前面显示给用户。
网页抽取模块,从其中抽取出关键词和图书信息。
页面的信息非常冗杂,我们利用xpath从中提取出图书的作者、出版社、等主要信息,如图2-2。
图2-2 页面图书信息
布尔检索法是指利用布尔运算符链接各个检索词,然后由计算机进行逻辑运算,找出所需信息的一种检索方法。
在本程序的search()函数中,我们对搜索词进行解析,判断其中的逻辑关系,给出搜索结果。
因为在一个div的标签中还存在很多倍的标签,所以直接利用xpath路径通过text()
获取文本出现问题,通过查阅xpath手册,利用string(.)
函数来过滤掉标签。
在爬虫模块中添加了url_valid(url)函数来判断url是否有效,利用正则表达式找出形如https://book.douban.com/subject/25960141/
的有效地址。
利用python的list类型进行url的存储,每次爬取到url,将新值存储在待抓取队列to_crawl
的队尾,对队首的url页面进行爬取,之后利用pop()
函数进行出队操作。
将已抓取的url送入crawled
列表。通过判断列表长度对爬虫爬取过程进行限制。
简单测试,若抓取页面数量超过10个就停止爬虫。
1、建立索引的结果
混入了一个奇怪的东西,可能是因为页面的结构不一致所导致的。
2、进行搜索
print 'ordered_search', ordered_search(index, ranks, u'杨绛')
print index[u'杨绛']
结果如下
将列表内容进行打印:
3、搜索作者给出的结果可能是唯一的。我们再搜索“二手”来看一下结果。
图中给出了部分结果。
4、 多个词同时搜索的结果
结果如下:
1、因为豆瓣读书(https://book.douban.com/)页面的内容极其之多,所以在进行网页爬取时,对已爬取队列的长度进行了限制(测试用长度限制为10),获得的信息有限。可以在具体实现时,将爬取部分与建立索引部分分开,以便大量数据的获取。
2、数据爬取并没有利用数据库进行存储,可以在之后添加数据库部分,以便数据的长期存储。
3、爬虫的多线程实现,提高爬取速度。
4、关键词数量有限。关键词只选择了图书的部分信息,并没有对网页中的每一项进行索引。可以利用python的分词模块,如smallseg等,得到更多的关键词,从而建立索引。
5、程序测试时,检索信息直接写死在代码中,可以在之后对检索的交互方式继续改进。
感谢北邮的两位童鞋提供完成这次实验的机会