CHAPTER 9: 《DESIGN A WEB CRAWLER》第9章 《设计一个web爬虫》

CHAPTER 9: 《DESIGN A WEB CRAWLER》第九章 设计一个web爬虫

在本章中,我们将重点介绍网络爬虫设计:一种有趣而经典的系统设计
面试问题。
网络爬虫被称为机器人或蜘蛛。它被搜索引擎广泛用于发现网络上的新内容或更新内容。内容可以是网页、图像、视频、PDF文档等。网络爬虫首先收集一些网页,然后跟踪这些网页上的链接页面以收集新内容。图 9-1 显示了爬网过程的直观示例。
在这里插入图片描述
爬虫有多种用途:

  • 搜索引擎索引:这是最常见的用例。爬虫收集网络页面为搜索引擎创建本地索引。例如Googlebot 就是网络谷歌搜索引擎背后的爬虫。
  • Web 存档:这是从 Web 收集信息以保存的过程数据以备将来使用。例如,许多国家图书馆运行爬虫来存档网络网站。值得注意的例子是美国国会图书馆[1]和欧盟网络档案馆[2]。
  • 网络挖矿:网络的爆炸性增长为数据挖掘。网络挖掘有助于从互联网上发现有用的知识。为例如,顶级金融公司使用爬虫下载股东大会和年度报告以了解公司的关键举措。
  • 网络监控。爬虫有助于监控版权和商标侵权行为通过互联网。例如,Digimarc [3] 利用爬虫来发现盗版作品和报告。

开发网络爬虫的复杂性取决于我们打算支持的规模。它可以是一个只需要几个小时就能完成的小型学校项目,也可以是一个巨大的项目需要专门的工程团队持续改进的项目。因此,我们将在下面探讨要支持的规模和功能。

第 1 步 - 了解问题并确定设计范围

网络爬虫的基本算法很简单:

  1. 给定一组 URL,下载这些 URL 链接的所有网页。
  2. 从这些网页中提取 URL
  3. 将新 URL 添加到要下载的 URL 列表中。重复这 3 个步骤。

网络爬虫真的像这个基本算法一样简单吗?不完全是。设计一个可扩展的网络爬虫是一项极其复杂的任务。任何人都不太可能设计面试期间的大型网络爬虫。在开始设计之前,我们必须提出问题以了解需求并确定设计范围:
应聘者:爬虫的主要用途是什幺?它是否用于搜索引擎索引,数据挖掘,还是别的什幺?
面试官:搜索引擎索引。
应聘者:网络爬虫每月收集多少个网页?
面试官:10 亿页。
应聘者:包括哪些内容类型?仅限 HTML 或其他内容类型,例如还有 PDF 和图像?
面试官:仅限 HTML。
应聘者:我们可以考虑新添加或编辑的网页吗?
面试官:是的,我们应该考虑新添加或编辑的网页。
应聘者:我们需要存储从网络上抓取的 HTML 页面吗?
面试官:是的,最长 5 年
应聘者:如何处理重复内容的网页?
面试官:包含重复内容的页面应被忽略

以上是您可以向面试官提出的一些示例问题。重要的是了解要求并澄清歧义。即使你被要求设计一个
像网络爬虫这样的简单产品,你和你的面试官也可能有不一样的想法。

除了与面试官澄清的功能外,记下一个好的网络爬虫的以下特征:

  • 可测量性:网络非常大。那里有数十亿个网页。使用并行化爬网应该非常有效。
  • 鲁棒性:网络充满了陷阱。错误的 HTML、无响应的服务器、崩溃、恶意链接等都很常见。爬网进程必须处理所有这些边缘情况。
  • 人性:爬虫不应在短时间内向网站发出太多请求时间间隔。
  • 可扩展性:系统非常灵活,因此只需进行最少的更改即可支持新的内容类型。例如,如果我们将来想抓取图像文档,我们不需要重新设计整个系统。

封底计算

以下估计基于许多假设,重要的是与面试官沟通以达成共识。

  • 假设每月下载10亿个网页。
  • QPS:1,000,000,000 / 30 天 / 24 小时 / 3600 秒 = ~400 页/秒。
  • 峰值 QPS = 2 * QPS = 800
  • 假设平均网页大小为 500k。
  • 10 亿页 x 500k = 每月 500 TB 存储。如果您不清楚数字存储单元,请再次浏览第 2 章中的“2 的幂”部分。
  • 假设数据存储 5 年,则 500 TB * 12 个月 * 5 年 = 30 PB。一个 30 PB需要存储来存储五年的内容。

第 2 步 - 提出高级设计并获得支持

一旦明确了需求,我们就会继续进行高级设计。灵感来自以前的在对网络爬虫的研究[4] [5]中,我们提出了一个如图9-2所示的高级设计。
在这里插入图片描述
首先,我们探索每个设计组件以了解它们的功能。然后我们逐步检查爬虫工作流程。
种子网址
网络爬虫使用种子 URL 作为爬网过程的起点。例如,要抓取大学网站上的所有网页,选择种子 URL 的直观方法是使用大学的域名。要抓取整个网络,我们需要创造性地选择种子 URL。一个好的种子网址作为一个很好的起点,爬虫可以利用它来遍历尽可能多的链接。一般策略是将整个 URL 空间划分为更小的空间。第一个提出的该方法基于地点,因为不同的国家可能有不同的流行网站。另一种方法是根据主题选择种子 URL;例如,我们可以划分 URL 空间购物、体育、医疗保健等。种子 URL 选择是一个开放式问题。你应该不会给出完美的答案。开放思考
网址前沿
大多数现代网络爬虫将爬行状态分为两种:待下载和已下载。存储要下载的 URL 的组件称为 URL Frontier。您可以将其称为先进先出 (FIFO) 队列。有关详细信息URL Frontier,请参阅深入研究。
HTML下载器
HTML 下载器从 Internet 下载网页。通过 URL 边界提供了这些 URL。
DNS解析器
要下载网页,必须将 URL 转换为 IP 地址。超文本标记语言下载器调用DNS解析器来获取URL对应的IP地址。为了例如,自 2019 年 3 月 5 日起,URL www.wikipedia.org 已转换为 IP 地址198.35.26.96。
内容解析器
下载网页后,必须对其进行解析和验证,因为格式错误的网页页面可能会引发问题并浪费存储空间。在中实现内容解析器抓取服务器会减慢抓取过程。因此,内容解析器是一个单独的成分。
内容已存在了?
在线研究[6]显示,29%的网页是重复内容,这可能导致相同的内容被存储多次。我们介绍“看到的内容?”数据结构,消除数据冗余并缩短处理时间。它有助于检测新的之前存储在系统中的内容。要比较两个 HTML 文档,我们可以一个接一个的比较他们。然而,这种方法速度慢且耗时,尤其是当涉及数十亿个网页时。完成这项任务的一个有效方法是比较两个网页的哈希值[7]。
内容存储
它是一个用于存储HTML内容的存储系统。存储系统的选择取决于磁盘和内存,数据类型、数据大小、访问频率、寿命等因素。

  • 大多数内容存储在磁盘上,因为数据集太大,无法放入内存。
  • 热门内容保留在内存中以减少延迟。

网址提取器
URL Extractor 从 HTML 页面中解析并提取链接。图 9-3 显示了一个示例链接提取过程。通过添加以下内容将相对路径转换为绝对 URL“https://en.wikipedia.org”前缀。
在这里插入图片描述
URL 过滤器
URL 筛选器排除了“列入黑名单”的网站。
URL Seen?
“URL Seen?”是一种数据结构,用于跟踪之前或已经访问过的 URL最尾端地址。“URL Seen?”有助于避免多次添加相同的 URL增加服务器负载并导致潜在的无限循环。布隆过滤器和哈希表是实现“URL Seen?”组件。我们不会详细介绍 bloom 过滤器和哈希表的实现这里。有关详细信息,请参阅参考资料 [4] [8]。
URL 存储
URL 存储存储已访问的 URL。到目前为止,我们已经讨论了每个系统组件。接下来,我们把它们放在一起来解释工作流。
网络爬虫工作流程
为了更好地逐步解释工作流程,在设计中添加了串行号如图9-4所示。
avatar第 1 步:将种子 URL 添加到 URL 边界
第 2 步:HTML 下载器从 URL Frontier 获取 URL 列表。
第 3 步:HTML 下载器从 DNS 解析器获取 URL 的 IP 地址并启动下载。
第 4 步:内容解析器解析 HTML 页面并检查页面是否格式不正确。
第 5 步:解析和验证内容后,将其传递给“看到的内容”组件。
第 6 步:“看到的内容”组件检查 HTML 页面是否已在存储中。

  • 如果它在存储中,这意味着不同 URL 中的相同内容已经处理。在这种情况下,HTML 页面将被丢弃。
  • 如果它不在存储中,则系统之前没有处理过相同的内容。这内容被传递给链接提取器。

第 7 步:链接提取器从 HTML 页面中提取链接。
第 8 步:提取的链接将传递到 URL 过滤器。
第 9 步:过滤链接后,它们被传递到“URL Seen?”组件。
第 10 步:“URL Seen”组件检查 URL 是否已经在存储中,如果是,则为之前处理过,不需要做任何事情。
第 11 步:如果之前未处理过 URL,则会将其添加到 URL 边界。

第 3 步 - 深入探究设计

到目前为止,我们已经讨论了高级设计。接下来,我们将讨论最多的重要的建筑构件和技术深度:

  • 深度优先搜索 (DFS) 与广度优先搜索 (BFS)
  • URL 边界
  • HTML 下载器
  • 鲁棒性
  • 扩展
  • 检测并避免有问题的内容

DFS 与 BFS

您可以将 Web 视为有向图,其中网页充当节点,超链接(URL) 作为边。爬网过程可以看作是从一个 Web 遍历有向图页面给其他人。两种常见的图遍历算法是 DFS 和 BFS。但是,DFS 是通常不是一个好的选择,因为DFS的深度可能很深。BFS 通常由网络爬虫使用,并由先进先出 (FIFO) 实现队列。
在 FIFO 队列中,URL 按照其排队顺序取消排队。但是,这实现有两个问题:

  • 来自同一网页的大多数链接都链接回同一主机。在图 9-5 中,所有wikipedia.com 中的链接是内部链接,使爬虫忙于处理 URL来自同一主机 (wikipedia.com)。当爬虫尝试下载同时,维基百科服务器将充斥着请求。这被认为是“不礼貌”。
    avatar

  • 标准 BFS 不考虑 URL 的优先级。网络很大并非每个页面都具有相同的质量和重要性水平。因此,我们可能想要根据URL的页面排名、网络流量、更新频率等确定URL的优先级。
    URL 边界
    URL 边界有助于解决这些问题。URL 边界是一种存储的数据结构要下载的 URL。URL 边界是确保合理的重要组成部分,URL 优先级和新鲜度。书中提到了一些关于URL前沿的值得注意的论文在参考资料[5] [9]中。这些论文的发现如下:
    礼貌
    通常,网络爬虫应避免向同一托管服务器发送过多请求在短时间内。发送过多的请求被认为是“不礼貌”的,甚至被对待作为拒绝服务 (DOS) 攻击。例如,在没有任何约束的情况下,爬虫可以发送每秒有数千个请求发送到同一个网站。这可能会使网络不堪重负服务器。强制礼貌的一般思路是一次从同一页面下载一页主机。可以在两个下载任务之间添加延迟。礼貌约束是通过维护从网站主机名到下载(工作线程)线程的映射来实现。每个下载器线程都有一个单独的 FIFO 队列,并且仅下载从那个队列。图 9-6 显示了这个队列合理(礼貌)的设计。
    avatar

  • 队列路由器:确保每个队列(b1、b2、…bn) 仅包含来自同一主机。

  • 映射表:它将每个主机映射到一个队列。
    avatar

  • FIFO 队列 b1、b2 到 bn:每个队列都包含来自同一主机的 URL。

  • 队列选择器:每个工作线程都映射到一个 FIFO 队列,并且它只下载该队列中的 URL。队列选择逻辑由队列选择器完成。

  • 工作线程 1 到 N。工作线程从同一个线程中一个接一个地下载网页主机。可以在两个下载任务之间添加延迟。

优先权
来自一个关于苹果产品的论坛的随机帖子具有非常不同的分量而不是 Apple 主页上的帖子。尽管它们都有“Apple”关键字,但它是爬虫首先抓取 Apple 主页是明智的。我们根据有用性对 URL 进行优先级排序,这可以通过 PageRank [10] 来衡量,网站流量、更新频率等。 “优先级排序器”是处理 URL 优先级的组件。有关此概念的深入信息,请参阅参考资料 [5] [10]。图 9-7 显示了管理 URL 优先级的设计。
avatar

  • 优先级排序器:它将 URL 作为输入并计算优先级。
  • 队列 f1 到 fn:每个队列都有一个分配的优先级。具有高优先级的队列是以更高的概率被选中。
  • 队列选择器:随机选择一个偏向于较高队列的队列优先权。

图 9-8 显示了 URL 边界设计,它包含两个模块:

  • 前队列:管理优先级
  • 后排:管理合理(礼貌)
    avatar

新鲜
网页不断地被添加、删除和编辑。网络爬虫必须定期重新抓取下载的页面以保持我们的数据集最新。重新抓取所有 URL 是时候了 -消耗和资源密集型。下面列出了一些优化新鲜度的策略:

  • 根据网页的更新历史重新抓取。
  • 优先考虑URL 并首先且更频繁地重新抓取重要页面。

URL Frontier 的存储
在现实世界的搜索引擎抓取中,前沿的 URL 数量可能有数百个数百万[4]。将所有内容放入内存既不持久也不可扩展。保存磁盘中的所有内容都是不受欢迎的,因为磁盘速度很慢;它可以很容易地成为爬虫的瓶颈。

我们采用了混合方法。大多数URL都存储在磁盘上,因此存储空间不是问题。为了降低从磁盘读取和写入磁盘的成本,我们在内存中维护缓冲区以进行入队/出队操作。缓冲区中的数据为定期写入磁盘。
HTML下载器
HTML 下载器使用 HTTP 协议从 Internet 下载网页。
在讨论 HTML 下载器之前,我们首先看一下机器人排除协议。
Robots.txt
Robots.txt,全称为机器人排除协议,是网站用于通信的标准与爬虫。它指定允许爬虫下载哪些页面。在尝试之前爬虫抓取网站时,首先要检查其对应的robots.txt并遵循其规则。

为了避免重复下载 robots.txt 文档,我们缓存该文档的结果。该文档是定期下载并保存到缓存。这是一段 robots.txt 文档,取自https://www.amazon.com/robots.txt。某些目录(例如 Creatorhub)是不允许的对于谷歌机器人。

用户代理:Googlebot
禁止:/creatorhub/*
禁止:/rss/people//reviews
禁止:/gp/pdp/rss/
/reviews
禁止:/gp/cdp/member-reviews/
禁止:/gp/aw/cr/

除了 robots.txt 之外,性能优化是我们将介绍的另一个重要概念
HTML 下载器。
性能优化
以下是 HTML 下载器的性能优化列表。
1.分布式抓取
为了获得高性能,爬虫作业被分发到多台服务器上,每台服务器运行多个线程。 URL空间被分割成更小的部分;所以,每个下载者负责 URL 的子集。图 9-9 显示了分布式爬网的示例。
在这里插入图片描述
2.缓存DNS解析器
DNS 解析器是爬虫的瓶颈,因为 DNS 请求可能会由于以下原因而花费时间:许多 DNS 接口的同步特性。 DNS 响应时间范围从 10 毫秒到200毫秒。一旦某个爬虫线程执行了对 DNS 的请求,其他线程就会被阻塞直到第一个请求完成。
维护我们的 DNS 缓存以避免调用 DNS频繁是速度优化的有效技术。我们的 DNS 缓存保留域名名称到 IP 地址的映射,并由 cron 作业定期更新。
3. 地点
按地理位置分布爬网服务器。当爬行服务器距离网站主机较近时,爬虫体验更快的下载时间。设计局部性适用于系统的大部分组件:爬取服务器、缓存、队列、存储等。
4、超时时间短
一些网络服务器响应缓慢或者可能根本不响应。为了避免长时间等待,指定了最长等待时间。如果主机在预定时间内没有响应,爬虫将停止该作业并爬取其他一些页面。
鲁棒性
除了性能优化之外,鲁棒性也是一个重要的考虑因素。我们提出提高系统鲁棒性的几种方法:

  • 一致的散列:这有助于在下载者之间分配负载。一个新的可以使用一致性哈希来添加或删除下载服务器。参见第五章:设计一致的哈希以获取更多细节。
  • 保存爬网状态和数据:为了防止失败,爬网状态和数据被写入一个存储系统。通过加载保存的状态可以轻松重新启动中断的爬网数据。
  • 异常处理:在大型系统中,错误是不可避免且常见的。这爬虫必须优雅地处理异常而不导致系统崩溃。
  • 数据验证:这是防止系统错误的重要措施。

可扩展性
随着几乎每个系统的发展,设计目标之一就是使系统变得灵活足以支持新的内容类型。爬虫可以通过插入新的扩展模块。图9-10显示了如何添加新模块。
在这里插入图片描述

  • 插入 PNG 下载器模块以下载 PNG 文档。
  • 添加Web监控模块,监控网页,防止版权和商标侵权。

检测并避免有问题的内容本节讨论如何检测和预防冗余、无意义或有害的内容。
1. 冗余内容
如前所述,近 30% 的网页是重复的。哈希值或校验和帮助检测重复 [11]。
2. 蜘蛛陷阱
蜘蛛陷阱是导致爬虫无限循环的网页。例如,无穷深层目录结构如下:
www.spidertrapexample.com/foo/bar/foo/bar/foo/bar/…
可以通过为 URL 设置最大长度来避免此类蜘蛛陷阱。然而,没有人-存在适合所有尺寸的解决方案来检测蜘蛛陷阱。包含蜘蛛陷阱的网站很容易由于在此类网站上发现了异常大量的网页而进行识别。它是难以开发自动算法来避免蜘蛛陷阱;但是,用户可以手动验证并识别蜘蛛陷阱,然后从爬虫中排除这些网站或应用一些自定义的 URL 过滤器。
3.数据噪声
有些内容几乎没有价值,例如广告、代码片段、垃圾邮件URL 等。这些内容对爬虫没有用处,应尽可能排除。

第 4 步 - 结束

在本章中,我们首先讨论了一个好的爬虫的特征:可扩展性、人性(礼貌)、可扩展性和健壮性。然后,我们提出了一个设计方案,并讨论了关键组件。构建可扩展的网络爬虫并非易事,因为网络非常庞大,而且到处都是陷阱。尽管我们已经涵盖了许多话题,但我们仍然错过了许多相关的谈话积分:

  • 服务器端渲染:许多网站使用 JavaScript、AJAX 等脚本来即时生成链接。如果我们直接下载和解析网页,我们将无法检索动态生成的链接。为了解决这个问题,我们执行服务器端在解析页面之前先渲染(也称为动态渲染)[12]。
  • 过滤掉不需要的页面:凭借有限的存储容量和抓取资源,反垃圾邮件组件有助于过滤掉低质量和垃圾页面 [13] [14]。
  • 数据库复制和分片:复制和分片等技术用于提高数据层的可用性、可扩展性和可靠性。
  • 水平扩展:对于大规模爬虫,数百甚至数千台服务器是需要执行下载任务。关键是保持服务器无状态。
  • 可用性、一致性和可靠性:这些概念是任何大型系统的成功。我们在第 1 章中详细讨论了这些概念。刷新你的关于这些主题的记忆。
  • 分析:收集和分析数据是任何系统的重要组成部分,因为数据是微调的关键成分。

恭喜你走到这一步!鼓励下自己。干得好!

参考资料
[1] 美国国会图书馆:https://www.loc.gov/websites/
[2] 欧盟网络档案馆:http://data.europa.eu/webarchive
[3] Digimarc:https://www.digimarc.com/products/digimarc-services/piracy-intelligence
[4] Heydon A., Najork M. Mercator:可扩展、可扩展的网络爬虫万维网,2(4) (1999),第219-229页
[5] 作者:Christopher Olston、Marc Najork:网络爬虫http://infolab.stanford.edu/~olston/publications/crawling_survey.pdf
[6] 29% 的网站面临重复内容问题:https://tinyurl.com/y6tmh55y
[7] Rabin M.O. 等人。随机多项式指纹识别 Center for Research in大学艾肯计算实验室计算技术(1981)
[8] B. H. Bloom,“具有允许误差的哈希编码中的空间/时间权衡”,《ACM通讯》,第13卷,第7期,第422-426页,1970年。
[9] 唐纳德·帕特森(Donald J. Patterson),网络爬虫:https://www.ics.uci.edu/~lopes/teaching/cs221W12/slides/Lecture05.pdf
[10] L. Page、S. Brin、R. Motwani 和 T. Winograd,“PageRank 引文排名:为网络带来秩序,“技术报告,斯坦福大学,1998.
[11] 伯顿·布鲁姆。哈希编码中的空间/时间权衡与允许的误差。ACM 通讯,13(7),第 422–426 页,1970 年 7 月。
[12] 谷歌动态渲染:
https://developers.google.com/search/docs/guides/dynamic-rendering
[13] T. Urvoy、T. Lavergne 和 P. Filoche,“使用隐藏样式跟踪网络垃圾邮件相似性“,载于第二届对抗性信息国际研讨会论文集网络上检索,2006 年。
[14] H.-T.Lee、D. Leonard、X. Wang 和 D. Loguinov,“IRLbot:扩展到 60 亿页及其他“,第 17 届国际万维网会议论文集,2008 年。

  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python有很多GUI库可以用来设计爬虫界面,比如Tkinter、PyQt、wxPython等等。这里以Tkinter为例,介绍如何设计一个带图形界面的爬虫。 首先,需要安装Tkinter库。在Python 3中,Tkinter是内置的,不需要额外安装。如果你使用的是Python 2.x版本,需要安装Tkinter库。 接下来,我们来设计一个简单的爬虫界面。界面上有一个输入框,用来输入要爬取的网站的URL,还有一个“开始爬取”按钮,点击后会开始爬取指定的网站。 下面是一个简单的代码示例: ```python import tkinter as tk import requests from bs4 import BeautifulSoup class CrawlerGUI: def __init__(self, master): self.master = master master.title("爬虫") self.label = tk.Label(master, text="请输入要爬取的网站URL:") self.label.pack() self.url_entry = tk.Entry(master) self.url_entry.pack() self.start_button = tk.Button(master, text="开始爬取", command=self.start_crawl) self.start_button.pack() def start_crawl(self): url = self.url_entry.get() response = requests.get(url) soup = BeautifulSoup(response.text, 'html.parser') # 在这里写爬虫代码 root = tk.Tk() crawler_gui = CrawlerGUI(root) root.mainloop() ``` 在这个示例中,我们首先导入了tkinter、requests和BeautifulSoup库。然后,我们定义了一个CrawlerGUI类,该类包含了GUI界面的所有组件,包括标签、输入框和按钮。当用户点击“开始爬取”按钮时,会调用start_crawl()方法,该方法会从输入框中获取URL,然后使用requests库发起HTTP请求,获取网站的HTML内容。最后,使用BeautifulSoup库解析HTML内容,你可以在这里编写你的爬虫代码。 最后,我们创建一个Tkinter窗口,并实例化CrawlerGUI类,然后调用mainloop()方法来显示窗口。这样,我们就完成了一个简单的爬虫GUI界面的设计

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

后端工匠之道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值