欧拉七桥问题
把一块连通的陆地作为一个顶点,每一座桥当成图的一条边,那么就把哥尼斯堡的七座桥抽象成图中所示
对于图中的每一个顶点,将与之相连的边的数量定义为它的度(Degree)
定理:如果一个图能够从一个顶点出发, 每条边不重复地遍历一遍回到这个顶点,那么每一顶点的度必须为偶数。
证明:假如能够遍历图的每条边各一次, 那么对于每个顶点,需要从
某条边进人顶点,同时从另一条边离开这个顶点。进人和离开顶点的次
数是相同的,因此每个顶点有多少条进入的边,就有多少条出去的边。
也就是说,每个顶点相连的边的数量是成对出现的,即每个顶点的度都
是偶数。
在图9.5中,有多个顶点的度为奇数,因此,这个图无法从一个顶点出发,
遍历每条边各一次然后回到这个顶点。
构建网络爬虫的工程要点
“如何构建一个网络爬虫”Google常用的一道面试题。
网络爬虫在工程实现上要考虑的细节非常多,其中大的方面有这样几点。
首先,用BFS?还是DFS?
虽然从理论上讲,这两个算法(在不考虑时间因素的前提下)都能够在大致相同的时间里“爬下”整个“静态”互联网上的内容,但是工程上的两个假设——不考虑时间因素,互联网静态不变,都是现实中做不到的。搜索引擎的网络
爬虫问题更应该定义成“如何在有限时间里最多地爬下最重要的网页”。显然各个网站最重要的网页应该是它的首页。在最极端的情况下,如果爬虫非常小,只能下载非常有限的网页,那么应该下载的是所有网站的首页,如果把爬虫再扩大些,应该爬下从首页直接链接的网页(就如同和北京直接相连的城市),因为这些网页是网站设计者自认为相当重要的网页。在这个前提下,显然BFS明显优于DFS。事实上在搜索引擎的爬虫,虽然不是简单地采用BFS,但是先爬哪个网页,后爬哪个网页的调度程序,原理上基本上是BFS。
那么是否DFS就不使用了呢?也不是这样的。这跟爬虫的分布式结构以及网络通信的握手成本有关。所谓“握手”就是指下载服务器和网站的服务器建立通信的过程。这个过程需要额外的时间(Overhead Time )如果握手的次数太多,下载的效率就降低了。实际的网络爬虫都是由成百上千甚至成干上万台服务器组成的分布式系统。对于某个网站,一般是由特定的一 一台或者几台服务器专门下载。 这些服务器下载完网站,然后再进人下一个网站,而不是每个网站先轮流下载5%,然后回过头来下载第二批,这样可以避免握手的次数太多。要是下载完第个网站再下载第二个,那么这又有点像DFS,虽然下载同一个网站(或者子网站)时,还是需要用BFS的。
总结起来,网络爬虫对网页通历的次序不是简单的BFS或者DFS有一个相对复杂的下载优先级排序的方法。管理这个优先级排用序的子系统般称为调度系统 (Scheduler),由它来决定当一个网页下载完成后,接下来下载哪个当然在调用系统里需要存储那些已经发现但是尚未下载的网页的URL,它们一般存在一个优先级队列 ( Priorty Queue)里。而用这种方式遍历整个互联网,在工程上和BFS更相似。因此,在爬虫中,BFS的成分多一些。
第二,页面的分析和URL的提取。
在上一部分中提到,当一个网页下载完成后,需要从这个网页中提取其中的URL。把它们加入到下载的队列中。这个工作在互联网的早期不难,因为那时的网页都是直接用HTML语言书写的。那些URL都以文本的形式放在网页中,前后都有明显的标识,很容易提取出来。但是现在很多URL的提取就不那么直接了,因为很多网页如今是用一些脚本语言(比如JavaScript) 生成的。打开网页的源代码,URL不是直接可见的文本,而是运行这一段脚本后才能得到的结果。因此,网络爬虫的页面分析就变得复杂很多,它要模拟浏览器运行一个网页,才能得到里面隐含的URL。有些网页的脚本写得非常不规范,以至于解析起来非常困难。可是,这些网页还是可以在浏览器中打开,说明浏览器可以解析。因此,需要做浏览器内核的工程师来写网络爬虫中的解析程序,可惜出色的浏览器内核工程师在全世界数量并不多。因此,若你发现些网页明明存在,但搜索引擎就是没有收录,一个可能的原因是网络爬虫中的解析程序没能成功解析网页中不规范的脚本程序。
第三,记录哪些网页已经下载过的小本本——URL表。
在互联网上,一个网页可能被多个网页中的超链接所指向,即在互联网这张大图上,有很多弧(链接)可以走到这个节点(网页)。这样在遍历互联网这张图时,这个网页可能被多次访问到。为了防止一个网页被下载多次,我们可以用个哈希表记录哪些网页已经下载过。再遇到这我们就可以跳过它。采用哈希表的好处是,判断一个网页的URL是否在表中,平均只需一次(或者略多的)查找。当然。如果遇到还未下载的网页。除了下载该网页,还要适时将这个网页的URL存入哈希表中,这个操作对哈希表来讲也非常简单。在一台下载服务器上建立和维护一张哈希表并不是难事。但是如果同时有上千台服务器一起下载网页。维护一张统一的哈希表就不那么简单了。 首先,这张哈希表会大到一台服务器存储不下。其次,由于每个下载服务器在开始下载前和完成下载后都要访问和维护这张表,以免不同的服务器做重复的工作,这
个存储哈希表的服务器的通信就成了整个爬虫系统的瓶颈。
这里有各种解决办法,没有绝对正确的,但是却也有好坏之分。好的方法都采用了这样两个技术: 首先明确每台下载服务器的分工,也就是说在调度时一看到某个URL就知道要交给哪台服务器去下载,以免很多服务器都要重重复判断某个URL是否需要下载。然后,在明确分工的基础上,判断URL是否下载就可以批处理了,比如每次向哈希表(一组独立的服务器)发送一大批询问, 或者每次更新一批哈希表的内容。 这样通信的次数就大大减少了。
转载于:《数学之美》