从这篇博客开始来具体的说搜索引擎的每一个核心模块,先从爬虫系统说起吧!
先来个大纲:
1、从爬虫的设计角度看,优秀的爬虫应该具备高性能,好的扩展性、健壮性和友好性。
2、从用户体验角度来说,对爬虫的工作效果评价标准包括:抓取网页的覆盖率,抓取网页的时新性和抓取网页的重要性。
3、抓取策略,网页更新策略,暗网抓取和分布式策略是爬虫系统至关重要的四个方面内容,基本决定了爬虫系统的质量和性能。
爬虫的分类
1、批量型爬虫
批量型爬虫有比较明确的抓取范围和目标,当爬虫达到这个设定好的目标后,就会停止抓取过程。具体的目标可能会有差异,也许是设定抓取一定数量的网页,也有可能是设定抓取的时间等。
2、增量型爬虫
通用的商业搜索引擎基本都属于增量型爬虫,增量型爬虫会保持持续不断的抓取行为,对于抓取到的网页,要定期更新。为啥要定期更新呢?因为互联网网页是动态的,不断的进行变化,可能会新增网页,可能网页被删除,也有可能原有的网页内容被修改。
你应该有那么一次在使用百度搜索网页的时候,点开某个链接,出现了404,这就说明百度对这个网页的定期更新没有做好,从而造成用户体验不好。不过对于海量的网页进行定期更新,难度是很大的,所以出现这种现象是可以理解的。
3、垂直型爬虫
垂直型爬虫关注特定的主题内容或属于某个行业的网页,比如对于健康网站来说,只需要从互联网上抓取和健康相关的网页内容就可以,其他行业的内容不予以考虑。不过这种特殊的需求也造成了它的技术难点,怎么识别出网页内容是否属于指定行业或者主题。而且垂直型爬虫最后需要的网页内容肯定是互联网中的一小部分网页,为了节约系统的资源,不可能把互联网上的所有网页都下载下来以后再去做特定主题网页的筛选,那么就需要爬虫在抓取阶段就能够动态的识别某个网址是否与主题相关,并尽量不去抓取无关页面,从而去节省资源。
通用搜索引擎的话,主要就是说增量型爬虫了。
搜索引擎的通用爬虫架构
1、在爬虫开始抓取时,互联网那么大,它不知道从何处开始抓取,所以需要给爬虫一些起始url,比如那些流量非常大的网站,hao123这种,上面有好多优质网站的链接,让爬虫从hao123这些网站开始爬取,感觉比较靠谱哈!这些起始url也叫做种子url。
2、爬虫中一个最重要的部分就是待抓取url队列,因为它决定了爬虫是否停止抓取,因为当队列为空的时候,也就是没有url可供爬虫使用了,所以爬虫抓取任务就完成了!
3、爬虫从待抓取url队列中获得即将抓取的url,然后DNS域名解析得到网站主机的实际网络地址,这个时候爬虫就要用这个地址去进行正儿八经的抓取数据操作了,也就是下载网页。
4、下载完网页之后,要把这个网页进行存储,就是存储到网页库中,然后还要把该网页的url地址加入到已抓取url队列中,这个队列主要是为了防止同一个url的网页重复下载,起到一个去重作用。
5、其实最重要的一步就是从刚刚下载的网页中抽取出在该网页中出现的所有url,然后把这些url加入到待抓取的url队列中,以保证爬虫下一次的抓取。
其实很好理解,我如果要认识世界上的所有人,那么一开始我会首先选择一个社交能力很强,人缘好的人进行交往,在这里就是hao123,通过hao123我认识了一点资讯,百度,京东,淘宝网等好朋友,一点资讯这些大网站就是从hao123这个网页中抽取出来的,下一步我就要通过一点资讯,百度等人来认识更多的人来扩大我的交际圈,那么此时就需要把一点资讯等url加入到待抓取url队列中去完成这个任务,然后一直重复这个流程,直到你认识了所有人,当你认识了所有人之后,这个队列就变空了,你的目标已经完成了!
再来扯点淡吧!说说爬虫的特性,怎么才算是好的爬虫?
1、高性能:互联网太大了,网页太多了,怎么才能在短时间下载完这些网页?就是说下载要快。对于url队列也要优化,网站那么多,url数量浩如烟海,需要设计高效的数据结构来实现队列。
2、可扩展性:就是你的爬虫系统对分布式是不是很友好,能不能实现动态添加机器和下架机器。目前实用的大型爬虫系统一定是分布式运行的,就是有多台机器专门做抓取,每台服务器部署多个爬虫,每个爬虫多线程运行,通过多种方式增加并发性,上边说的是所有抓取服务器在同一个数据中心的情况。甚至说大型的搜索引擎在全球有不同的数据中心,在欧洲的数据中心专门负责抓取英国,法国等国家的网页,在亚洲的数据中心专门负责抓取中国,日本等国家的网页,因为访问本地的网页比访问远程网页速度快,所以不同地域数据中心分别抓取性能会更好。
3、健壮性:也就是你的爬虫程序不会轻易崩溃,要能处理多种异常情况。
4、友好性:要保护被抓网站的部分私密性,也要减小被抓网站的压力。你说你抓取人家的网站,本来就是有点厚脸皮的事情,你还把别人的后台链接等私密性东西抓出来公之于众,要不要点逼脸?人家的网站本来运行的好好的,你用那么多机器去访问人家网站,不停的搞,不停的搞,最后形成DDOS分布式拒绝服务攻击了,人家网站被你搞挂了,你心里能不能有点逼数?哈哈,来点粗口的更加容易理解!
保护网站的私密性,主要是靠道德来约束的,体现在网站有一个robots.txt文件,这个文件告诉所有搜索引擎,搜索引擎A可以抓取我的哪些内容,不能抓取哪些内容,搜索引擎B可以抓取我的哪些内容,不能抓取哪些内容。。。
就拿简书来看一下吧,访问https://www.jianshu.com/robots.txt
User-agent是指定搜索引擎,*代表所有的搜索引擎。
Disallow代表禁止抓取的内容,一般是后台目录。
再来一个内容更加清晰的robots.txt
这些内容其实都是网站自己写的,任何一个搜索引擎都可以无视这些内容,对网站进行肆意的抓取。不过在道德层面的话,做人,不,做搜索引擎也是要脸面的,所以一个友好的爬虫要访问被抓网站的robots.txt先看一下,访问别人要有礼貌,看看网站主人对你的待客之道是什么?如果人家都不欢迎你,你还赖着,我靠,要不要脸面了?当然,你是的确可以赖着不走的,毕竟腿长在你自己身上!
抓取策略
1、广度优先遍历策略
我们大学学的数据结构中关于二叉树的遍历你是不是还记得?
分为广度优先遍历和深度优先遍历,广度优先就是把现在的几个页面都访问完,然后再依次访问这几个页面的子页面(抽取出来的链接);而深度优先遍历则是一个页面干到底,访问一个页面,直接再访问这个页面的子页面,然后访问子页面的子页面,如此种种。。。
这个策略直接看图已经很清楚了,不再解释了。
2、非完全PageRank策略
这个方法是在PageRank上改的,因为PageRank方法需要所有的网页数据才能进行计算,也就是要对网页进行PageRank,那么就需要全网的网页数据,显然达不到这个要求,爬虫的目的就是去爬取全网数据,如果得到全网数据了,还要爬虫干鸡毛。所以就产生了这种非完全的计算网页链接关系排名的方法。
不可能每下载一个网页就重新计算一下PageRank值,所以一般是攒够K个网页才重新计算排名,所以在下次重新计算排名之前,那么新抽取出来的网页链接怎么办,它们关于排名没有任何数据啊?所以会给这些新网页一个临时PageRank值,具体就是将所有入链该网页的PageRank值进行汇总充当它的临时PageRank值。
全网的网页是P1-P9,我们现在已经下载了P1-P3,待下载url队列里边是P4-P6,那么我们要用PageRank计算的就是在P4,P5,P6中,应该优先下载谁?主要是利用P1-P6的数据计算PageRank,P7-P9的数据不用,因为属于未知的。计算后的结果是P5优先下载,然后把P5中的链接抽出来,就是P8了,此时赋予P8一个临时的PageRank值,如果P8的值大于P4,P6,那么就优先下载P8。
这个算法主要是计算出网页的排名从而确定下载的优先级,算法看起来很麻烦,而广度优先遍历就很简单啊,简单暴力,但是效果未必就比PageRank差啊!
3、大站优先策略
这个就更好理解了,大网站肯定比小网站下载优先了!通过在url队列中判断url所对应网站的知名度来确定下载顺序。效果比广度优先遍历好!
4、OCIP策略(Online Page Importance Computation)
在线页面重要性计算,核心思想就是给互联网的每一个网页赋予一定的“金钱”,每当下载某个页面P后,P就将自己的所有金钱p平均分给页面中包含的链接页面,然后把自己的金钱清空。在url待下载队列中,通过比较每个url的金钱多少来进行排序,优先下载金钱多的网页。效果还是很不错的,比广度优先遍历好!
网页更新策略
上面说了,我们主要是讲增量型爬虫,那么就需要对网页的信息进行定期更新,从而保证搜索的质量。
1、历史参考策略
过去频繁更新的网页,那么将来也会频繁更新。
2、用户体验策略
注重用户体验,搜索的结果有好多页,用户一般倾向于只看前3页,所以后面的内容不用更新的太着急,只要注重前3页的更新就可以!
3、聚类抽样策略
前两种方法都是注重网页的历史数据,聚类是不需要历史数据的,物以类聚嘛!我们只需要找到某一类网页的特征,那么用这一类网页中的代表性网页更新行为来代表该网页的行为就可以,如果代表性网页频繁更新,那么该网页也频繁更新!
暗网抓取
又看到一个新鲜的概念,比较好奇吧?暗网是什么?
暗网就是搜索引擎的爬虫抓不到的网页区域,而且这个区域很大很大,你平常能访问到的区域(SurfaceWeb)跟暗网(DeepWeb)相比只是冰山一角,看一下百度暗网的结果多么可怕:
哈哈,别害怕,作为一个技术人,不要被这些表面性的东西所迷惑,要从技术上进行分析,为什么搜索引擎访问不到暗网的这部分网页资源。
我们看完了上面关于搜索引擎爬虫抓取的原理,想必已经知道了搜索引擎爬虫是依赖于网页中的链接关系来发现新的页面,只要爬虫不停的爬取,不断的抽取出链接,只要时间足够长,按道理来说不会有漏网之鱼的,因为一个网站总是要与其他网站有所联系的,除非你真的是一个信息孤岛!那么为什么暗网的区域还那么大,这么多的网页资源无法进行抓取到,难道都是信息孤岛吗?当然不是,但是你要知道不是所有网站的网页内容都是静态的,有些网页是靠用户输入然后查询数据库才显示数据的!
举个例子来说一下,就说12306吧,我们访问它的主页时,就得到下面的网页,搜索引擎只能做到下载它的首页。
但是如果我们现在要查询车票呢?比如查询从北京到上海的车票,显示下面的网页结果,这个网页搜索引擎怎么去抓取?因为这个网页的产生是依赖我们手动输入出发城市和目的地城市的,不同的输入,结果就不同,这个不是静态的网页资源,搜索引擎的爬虫无法抓取,所以就产生很大的暗网区域了!
现在的很多商业搜索引擎都在努力的进行暗网挖掘,把数据从数据库中抓出来,增加信息的覆盖程度。比如百度的阿拉丁计划。
难点主要有:
1、对数据库的查询组合太多,比如12306,出发城市和目的地城市那么多,还有日期,一一组合的话,效率会很低。解决方法是用谷歌提出的富含信息查询模板,就相当于你在12306查询车票时候,只输入出发城市,不输入目的地城市,这个属于一维模板,因为只填了一种信息。怎么评价这个模板是不是富含信息的呢?主要在出发城市这个属性里,填入北京,上海各种城市,比较一下返回的信息是否差异性很大,这里显然是差异性很大的,所以这个模板就是富含信息的模板,否则就不是富含信息的模板,利用富含信息的模板来抓取比较重要的内容。但是查询模板本身可能数量也会很大,所以可以用Google的ISIT算法来减小提交查询的数量。思想是从一维模板开始试探,如果是富含信息的模板,就转到二维模板继续试探,然后三维,四维......(存在的情况下),这样可以找到绝大多数的富含信息的查询模板。减少查询次数,提高效率。
2、文本框问题,比如你要爬取的系统也是一个搜索引擎呢?比如你要抓取百度的数据库,我靠,搜索引擎的数据库可大啊,怎么才能把百度的数据库内容都抓出来呢?我们知道访问百度无非就是输入搜索查询词就行了,那么我们需要一直输入各种查询词来抓对应的数据,所以这个也很难!
对于这种带文本输入框的,我们需要让爬虫自动填写文本框,但是爬虫一开始也不知道自动填写啥内容啊?怎么办?老办法,还是人为的先给他来点种子,比如用小团圆,许三观卖血记等种子来作为初始的输入条件,下载查询出来的页面,然后从页面抽取关键词,比如抽取出了张爱玲,小说等关键词,然后迭代查询,并下载相应的页面。
分布式爬虫
最后来说一下爬虫的分布式。
大型的分布式爬虫分为三个等级:分布式数据中心、分布式抓取服务器、分布式爬虫程序。
同一数据中心的抓取服务器之间的分布式架构有两种:
主从式分布爬虫
对等式分布爬虫
主从式分布爬虫
master/slave中的master是url服务器,主要负责维护url待下载队列和对所有slave的url分发,还需要考虑分发任务的负载均衡问题。而slave就是负责具体的下载任务。所以整个爬虫的系统瓶颈就在url服务器上。
对等式分布爬虫
这种情况下,每个服务器之间的关系都是对等的,没有url服务器了,每个服务器都要负责url队列的维护工作和自己的下载任务,但是没有了url服务器,就会出现问题,就是服务器的任务分工问题。
解决办法是哈希取模,比如有3台服务器,当接到www.google.com的网址时,计算网址的哈希值,并对3取模,发现取模后是1,那么就让服务器1下载它。当有www.baidu.com网址出现时,哈希后取模得到2,那么就让服务器2下载它。如此,就可以把任务大致均分到三个服务器上。
但是,如果需要对爬虫系统进行扩展呢?比如加台机器来增加抓取能力。因为现在只有3台机器,所以取模时是按照3计算的,现在多了一台,那么应该对4取模了,那么之前比如服务器2负责的抓取任务现在可能就不属于它了,加了一台机器完全把现有的系统抓取任务搞乱了。
一致性哈希算法
如下图所示,对网址进行哈希,映射到0 - 2的32次方,大量的网址会被均匀的分布到各个位置,只需要让每台机器负责一个氛围的网址进行下载就行了。当加机器时,只需要给它划定范围就行,不会对其他服务器的抓取url造成太大影响。当出现服务器宕机时,将会被转发给宕机主机顺时针下一个机器处理。