devise一个networking爬虫

我遇到了一个面试问题“如果你正在devise一个networking爬虫,你将如何避免陷入无限循环?”我正试图回答这个问题。

它是如何从一开始就开始的。 说谷歌开始与一些中心网页说,成百上千(首先如何find这些中心网页是一个不同的子问题)。 由于谷歌页面等链接,是否继续制作一个哈希表,以确保它不跟随早先访问过的页面。

如果同一页面有两个名称(URL),如果在我们有URL缩写器等这些日子里说。

我以Google为例。 虽然谷歌不泄漏其networking爬虫algorithm和网页排名等工作,但任何猜测?

如果你想得到一个详细的答案,请参阅本文的3.8节 ,它描述了一个现代刮板的URLtesting:

在提取链接的过程中,任何Web爬虫都会遇到到同一文档的多个链接。 为了避免多次下载和处理文档,必须在每个提取的链接上执行一个URL查看testing,然后将其添加到URL前端。 (另一种devise是在URL从边界移除时执行URLtesting,但这种方法会导致更大的边界。)

为了执行URLtesting,我们将所有由Mercator看到的URL以规范forms存储在一个名为URL集的大表中。 同样,它们的条目太多,以适应内存,所以像文档指纹集,URL集主要存储在磁盘上。

为了节省空间,我们不会将每个URL的文本表示存储在URL集中,而是存储一个固定大小的校验和。 与呈现给内容看到的testing的文档指纹集的指纹不同,根据URL集testing的URLstream具有非平凡的局部性。 为了减less后备磁盘文件的操作次数,我们保留了stream行URL的内存caching。 这种caching的直觉是,链接到一些URL是相当普遍的,所以在内存中cachingstream行的链接会导致很高的内存命中率。

实际上,使用2 ^ 18条目的内存caching和类似LRU的时钟replace策略,我们在内存caching中的总体命中率达到了66.2%,命中率达到了9.5%最近添加的url,净点击率为75.7%。 而且,在stream行的URLcaching和最近添加的URL中都有24.3%的请求中,在我们的随机访问文件实现中,大约1 = 3的缓冲区产生了点击,这也存在于用户空间中。 所有这些缓冲的最终结果是,我们在URL集上执行的每个成员资格testing的平均结果是0.16个seek和0.17个读取内核调用(其中一部分从内核的文件系统缓冲区中提供)。 因此,每个URL集成员testing都会导致六分之一的内核调用作为文档指纹集上的成员资格testing。 这些节省纯粹是由于爬网期间遇到的URLstream中固有的URL地址量(即重复stream行的URL)所致。

基本上,他们用散列函数来散列所有的URL,保证每个URL的唯一散列,并且由于URL的局部性,很容易findURL。 Google甚至开源了他们的散列函数: CityHash

警告!
他们可能也在谈论机器人陷阱! 僵尸networking陷阱是一个网页的一部分,不断产生新的链接与唯一的url,你会基本上被困在一个“无限循环”通过该网页服务的链接。 这不完全是一个循环,因为循环将是访问相同的URL的结果,但它是一个无限的URL链,你应该避免爬行。

2012 12月13 更新– 世界应该结束的一天

根据Fr0zenFyr的评论:如果使用AOPICalgorithm来select页面,那么避免无限循环types的僵尸陷阱是相当容易的。 以下是AOPIC工作原理的总结:

  1. 获取一组N个种子页面。
  2. 为每个页面分配X额度的信用额度,使得每个页面在爬行开始之前都具有X / N信用(即相等的信用额度)。
  3. select一个页面P,其中P具有最高的信用额度(或者如果所有页面具有相同的信用额度,则抓取随机页面)。
  4. 抓取页面P(假设P抓取时有100个点数)。
  5. 从页面P中提取所有的链接(假设有10个链接)。
  6. 将P的信用设置为0。
  7. 采取10%的“税”,并分配到一个Lambda页面。
  8. 从P的原始信用 – 税收:100(P信用) – 10(10%税))/ 10(链接)=每个链接9个学分,在页面P上find相同数量的学分。
  9. 从第3步开始重复。

由于Lambda页面不断收取税款,因此最终将成为信用额度最高的页面,我们必须“抓取”该页面。 我用引号的方式说“抓取”,因为我们实际上并没有为Lambda页面发出HTTP请求,我们只是把它们放在一起,然后分配给我们数据库中的所有页面。

由于机器人陷阱只能提供内部链接信用,而且很less从外部获得信用,所以它们将不断从信用税(从税收)泄漏到Lambda页面。 Lambda页面会将这些信用点均匀分配到数据库中的所有页面,并且在每个周期内,Bot陷阱页面将丢失越来越多的信用点,直到信用额度如此之less以至于几乎不会再被抓取。 好的页面不会发生这种情况,因为他们经常从其他页面上的反向链接获得信用。 这也导致了一个dynamic的页面排名,你会发现,任何时候你对你的数据库进行快照,按照他们拥有的信用量来sorting页面,那么他们很可能会根据页面真实排名

这只能避免无限循环types的机器人陷阱,但还有许多其他的机器人陷阱应该注意,也有办法绕过它们。

虽然这里的每个人都已经build议如何创build您的networking抓取工具,但下面是Google如何排列网页。

Google会根据回叫链接的数量(其他网站上指向特定网站/页面的链接数量)为每个页面提供排名。 这就是所谓的相关性分数。 这是基于这样一个事实,如果一个页面有很多其他页面链接到它,这可能是一个重要的页面。

每个网站/页面在graphics中被视为一个节点。 其他页面的链接是有向边。 顶点的度数被定义为input边的数量。 具有较高数量的传入边缘的节点排名较高。

这是如何确定PageRank的。 假设页面Pj有Lj链接。 如果其中一个链接是页面Pi,那么Pj会将其重要性的1 / Lj传递给Pi。 Pi的重要性排名就是链接到页面的所有贡献的总和。 所以如果我们表示由Bi连接到Pi的一组页面,那么我们有这个公式:

Importance(Pi)= sum( Importance(Pj)/Lj ) for all links from Pi to Bi 

等级被放置在称为超链接matrix的matrix中:H [i,j]

如果有从Pi到Bi的链接,matrix中的一行是0或1 / Lj。 这个matrix的另一个性质是,如果我们总结列中的所有行,我们得到1。

现在我们需要把这个matrix乘以一个特征向量,称为I(具有特征值1),使得:

 I = H*I 

现在我们开始迭代:I H,I H,I I I H …. I ^ K * H,直到解收敛。 即在步骤k和k + 1中,我们得到的matrix几乎相同。

现在,无论是在我的向量是每个页面的重要性。

有关简单的课堂作业示例,请参阅http://www.math.cornell.edu/~mec/Winter2009/RalucaRemus/Lecture3/lecture3.html

至于解决面试问题中的重复问题,请在整个页面上执行一次校验和,然后在地图上使用校验和或bash校验和来跟踪访问页面。

取决于他们的问题的意图是多么深刻。 如果他们只是试图避免来回地使用相同的链接,那么哈希URL就足够了。

怎么样的字面上成千上万的URL导致相同的内容的内容? 就像一个不影响任何内容的QueryString参数,但是可以有无数次的迭代。 我想你也可以散列页面的内容,并比较url,看它们是否相似,以捕获由多个URL标识的内容。 例如,参见@ Lirik的文章中提到的Bot Traps。

你必须有一些散列表来存储结果,你只需要在每次加载页面之前检查它。

这里的问题是不抓取重复的URL,这是通过使用从URL获得的散列的索引来解决的。 问题是抓取重复的内容。 “爬虫陷阱”的每个url都是不同的(年,日,SessionID …)。

没有一个“完美”的解决scheme…但是你可以使用一些这样的策略:

•保持url在网站内的完美等级的字段。 对于从一个页面获取url的每个网页,增加等级。 它会像一棵树。 你可以停下来爬到一定的水平,像10(我想谷歌使用这个)。

•您可以尝试创build一种可以比较的HASH来查找类似的文档,因为您无法与数据库中的每个文档进行比较。 有谷歌SimHash,但我找不到任何实施使用。 然后我创造了我自己的。 我的哈希计数在html代码中的低频和高频字符,并生成一个20bytes的哈希,这是一个AVLTree内的近邻search与一些宽容(约2)的最后爬网页的小caching进行比较。 你不能在这个散列中使用任何对字符位置的引用。 在“识别”陷阱之后,您可以logging重复内容的url模式,并开始忽略具有该模式的页面。

•像谷歌,你可以创build一个排名,每个网站和“信任”比其他更多。

抓取工具保留一个包含所有要抓取的URL的URL池。 为了避免“无限循环”,其基本思想是在添加到池之前检查每个URL的存在。

但是,当系统扩展到一定水平时,这并不容易实现。 天真的做法是保持所有的url在一个哈希集,并检查每个新的URL的存在。 当存在太多的URL以适应内存时,这将不起作用。

这里有几个解决scheme。 例如,我们不应该将所有的URL存储到内存中,而应该保存在磁盘中。 为了节省空间,应该使用URL哈希代替原始URL。 还值得一提的是,我们应该保留URL的规范forms,而不是原来的forms。 所以如果url被像bit.ly这样的服务缩短,最好是得到最终的url。 为了加快检查过程,可以build立一个caching层。 或者您可以将其视为分布式caching系统,这是一个单独的主题。

“ 构buildWeb爬网程序 ”的post详细分析了这个问题。

那么networking基本上是一个有向图,所以你可以构build一个graphics的URL,然后做一个BFS或DFS遍历,同时标记访问节点,所以你不访问同一页面两次。

这是一个networking爬虫的例子。 哪些可以用来收集mac地址进行mac spoofing。

 #!/usr/bin/env python import sys import os import urlparse import urllib from bs4 import BeautifulSoup def mac_addr_str(f_data): global fptr global mac_list word_array = f_data.split(" ") for word in word_array: if len(word) == 17 and ':' in word[2] and ':' in word[5] and ':' in word[8] and ':' in word[11] and ':' in word[14]: if word not in mac_list: mac_list.append(word) fptr.writelines(word +"\n") print word url = "http://stackoverflow.com/questions/tagged/mac-address" url_list = [url] visited = [url] pwd = os.getcwd(); pwd = pwd + "/internet_mac.txt"; fptr = open(pwd, "a") mac_list = [] while len(url_list) > 0: try: htmltext = urllib.urlopen(url_list[0]).read() except: url_list[0] mac_addr_str(htmltext) soup = BeautifulSoup(htmltext) url_list.pop(0) for tag in soup.findAll('a',href=True): tag['href'] = urlparse.urljoin(url,tag['href']) if url in tag['href'] and tag['href'] not in visited: url_list.append(tag['href']) visited.append(tag['href']) 

更改url来抓取更多网站……祝你好运