高并发架构——网页爬虫设计:如何下载千亿级网页?

Java全能学习面试指南:https://javaxiaobear.cn

在互联网早期,网络爬虫仅仅应用在搜索引擎中。随着大数据时代的到来,数据存储和计算越来越廉价和高效,越来越多的企业开始利用网络爬虫来获取外部数据。例如:获取政府公开数据以进行统计分析;获取公开资讯以进行舆情和热点追踪;获取竞争对手数据以进行产品和营销优化等等。

网络爬虫有时候也被称为网络机器人,或者网络蜘蛛。我们准备开发一个全网爬虫,爬取全(中文)互联网的公开网页,以构建搜索引擎和进行数据分析,爬虫名称为“Bajie(八戒)”。

Bajie 的技术挑战包括:如何不重复地获取并存储全网海量 URL?如何保证爬虫可以快速爬取全网网页但又不会给目标网站带来巨大的并发压力?接下来我们就来看看 Bajie 的需求与技术架构。

1、需求分析

Bajie 的功能比较简单,这里不再赘述,主要就是爬取数据

1、性能指标估算

因为互联网网页会不断产生,所以全网爬虫 Bajie 也是一个持续运行的系统。根据设计目标,Bajie 需要每个月从互联网爬取的网页数为 20 亿个,平均每个页面 500KB,且网页需存储 20 年。

Bajie 的存储量和 TPS(系统吞吐量)估算如下。

每月新增存储量

估计平均每个页面 500KB,那么每个月需要新增存储 1PB。20亿 × 500KB = 1PB

总存储空间

网页存储有效期 20 年,那么需要总存储空间 240PB。1PB × 12个月 × 20年 = 240PB

TPS

Bajie 的 TPS 应为 800。20亿 ÷ (30 × 24 × 60 × 60) ≈ 800

2、非功能需求

Bajie 需要满足的非功能需求如下:

  1. 伸缩性:当未来需要增加每月爬取的网页数时,Bajie 可以灵活部署,扩大集群规模,增强其爬取网页的速度。也就是说,Bajie 必须是一个分布式爬虫。
  2. 健壮性:互联网是一个开放的世界,也是一个混乱的世界,服务器可能会宕机,网站可能失去响应,网页 HTML 可能是错误的,链接可能有陷阱……所以 Bajie 应该能够面对各种异常,正常运行。
  3. 去重:一方面需要对超链接 URL 去重,相同的 URL 不需要重复下载;另一方面还要对内容去重,不同 URL 但是相同内容的页面也不需要重复存储。
  4. 扩展性:当前只需要爬取 HTML 页面即可,将来可能会扩展到图片、视频、文档等内容页面。

此外,Bajie 必须是“礼貌的”。爬虫爬取页面,实际上就是对目标服务器的一次访问,如果高并发地进行访问,可能会对目标服务器造成比较大的负载压力,甚至会被目标服务器判定为 DoS 攻击。因此 Bajie 要避免对同一个域名进行并发爬取,还要根据目标服务器的承载能力增加访问延迟,即在两次爬取访问之间,增加等待时间。

并且,Bajie 还需要遵循互联网爬虫协议,即目标网站的 robots.txt 协议,不爬取目标网站禁止爬取的内容。比如 www.zhihu.com 的 robots.txt 内容片段如下。

User-agent: bingbot
Disallow: /appview/
Disallow: /login
Disallow: /logout
Disallow: /resetpassword
Disallow: /terms
Disallow: /search
Allow: /search-special
Disallow: /notifications
Disallow: /settings
Disallow: /inbox
Disallow: /admin_inbox
Disallow: /*?guide*

Zhihu 约定 Bing 爬虫可以访问和不可以访问的路径都列在 robots.txt 中,其他的 Google爬虫等也在 robots.txt 中列明。

robots.txt 还可以直接禁止某个爬虫,比如淘宝就禁止了百度爬虫,淘宝的 robots.txt 如下。

User-agent: Baiduspider
Disallow: /
User-agent: baiduspider
Disallow: /

淘宝禁止百度爬虫访问根目录,也就是禁止百度爬取该网站所有页面

robots.txt 在域名根目录下,如 www.taobao.com/robots.txt。Bajie 应该首先获取目标网站的 robots.txt,根据爬虫协议构建要爬取的 URL 超链接列表。

2、概要设计

Bajie 的设计目标是爬取数千亿的互联网页,那么 Bajie 首先需要得到这千亿级网页的URL,该如何获得呢?

全世界的互联网页面事实上是一个通过超链接连接的巨大网络,其中每个页面都包含一些指向其他页面的 URL 链接,这些有指向的链接将全部网页构成一个有向(网络)图。如下图所示,每个节点是一个网页,每条有向的边就是一个超链接。

image-20231129143250079

上图中,www.a.com 包含两个超链接,分别是 www.b.com 和 www.c.com,对应图中就是节点 www.a.com 指向节点 www.b.com 和节点 www.c.com 的边。同样地,www.b.com 节点也会指向 www.d.com 节点。

如果我们从这个图中的某个节点开始遍历,根据节点中包含的链接再遍历其指向的节点,再从这些新节点遍历其指向的节点,如此下去,理论上可以遍历互联网上的全部网页。而将遍历到的网页下载保存起来,就是爬虫的主要工作。

所以,Bajie 不需要事先知道数千亿的 URL,然后再去下载。Bajie 只需要知道一小部分URL,也就是所谓的种子 URL,然后从这些种子 URL 开始遍历,就可以得到全世界的URL,并下载全世界的网页。

Bajie 的处理流程活动图如下:

image-20231129143423420

首先 Bajie 需要构建种子 URL,它们就是遍历整个互联网页面有向图的起点。种子 URL 将影响遍历的范围和效率,所以我们通常选择比较知名的网站的主要页面(比如首页)作为种子 URL。

然后,URL 调度器从种子 URL 中选择一些 URL 进行处理。后面将在详细介绍中说明 URL调度器的算法原理。

Bajie 对选择出来的 URL 经过域名解析后,下载得到 HTML 页面内容,进而解析 HTML页面,分析该内容是否已经在爬虫系统中存在。因为在互联网世界中,大约有三分之一的内容是重复的,下载重复的内容就是在浪费计算和存储资源。如果内容已存在,就丢弃该重复内容,继续从 URL 调度器获取 URL;如果不存在,就将该 HTML 页面写入 HDFS 存储系统。

然后,Bajie 进一步从已存储的 HTML 中提取其内部包含的超链接 URL,分析这些 URL 是否满足过滤条件,即判断 URL 是否在黑名单中,以及 URL 指向的目标文件类型是否是爬虫要爬取的类型。

如果 HTML 中的某些 URL 满足过滤条件,那么就丢弃这些 URL;如果不满足过滤条件,那么,进一步判断这些 URL 是否已经存在,如果已经存在,就丢弃该 URL,如果不存在,就记录到待下载 URL 集合。URL 调度器从待下载 URL 集合中选择一批 URL 继续上面的处理过程。

这里需要注意,想判断 URL 是否已经存在,就要判断这个 URL 是否已经在待下载 URL 集合中。此外,还需要判断这个 URL 是否已经下载得到 HTML 内容了。只有既不是待下载,也没被下载过的 URL 才会被写入待下载 URL 集合。

可以看到,在爬虫的活动图里是没有结束点的,从开始启动,就不停地下载互联网的页面,永不停息。其中,URL 调度器是整个爬虫系统的中枢和核心,也是整个爬虫的驱动器。爬虫就是靠着 URL 调度器源源不断地选择 URL,然后有节奏、可控地下载了整个互联

网,所以 URL 调度器也是爬虫的策略中心

据此,Bajie 的部署图如下:

image-20231129143827666

Bajie 系统中主要有两类服务器,一类是 URL 调度器服务器;一类是 URL 下载处理服务器集群,它是一个分布式集群。

URL 调度器从种子 URL 或待下载 URL 集合中载入 URL,再根据调度算法,选择一批 URL发送给 URL 下载处理服务器集群。这个下载处理服务器集群是由多台服务器组成的,根据需要达到的 TPS,集群规模可以进行动态伸缩,以实现需求中的伸缩性要求。

每台 URL 下载处理服务器先得到分配给自己的一组 URL,再启动多个线程,其中每个线程处理一个 URL,按照前面的流程,调用域名解析组件、HTML 下载组件、HTML 内容解析组件、内容去重组件、URL 提取组件、URL 过滤组件、URL 去重组件,最终将 HTML 内容写入 HDFS,并将待下载 URL 写入待下载 URL 集合文件。

分布式爬虫

需要注意的是,URL 下载处理服务器采用分布式集群部署,主要是为了提高系统的吞吐能力,使系统满足伸缩性需求。而 URL 调度器则只需要采用一台高性能的服务器单机部署即可。

事实上,单机 URL 调度器也完全能够满足目前 800TPS 的负载压力,以及将来的伸缩要求。因为 800TPS 对于 URL 调度器而言其实就是每秒产生 800 个 URL 而已,计算压力并不大,单台服务器完全能够满足。

同时 URL 调度器也不需要考虑单服务器宕机导致的可用性问题,因为爬虫并不是一个实时在线系统,如果 URL 调度器宕机,只需要重新启动即可,并不需要多机部署高可用集群。

相对应地,每个 URL 在 URL 下载处理服务器上的计算负载压力要大得多,需要分布式集群处理,也因此大规模爬虫被称为分布式爬虫,Bajie 就是一个分布式爬虫。

3、详细设计

Bajie 详细设计关注 3 个技术关键点:URL 调度器算法、去重算法、高可用设计。

1、URL调度器算法

URL 调度器需要从待下载 URL 集合中选取一部分 URL 进行排序,然后分发给 URL 下载服务器去下载。待下载 URL 集合中的 URL 是从下载的 HTML 页面里提取出来,然后进行过滤、去重得到的。一个 HTML 页面通常包含多个 URL,每个 URL 又对应一个页面,因此,URL 集合数量会随着页面不断下载而指数级增加。

待下载 URL 数量将远远大于系统的下载能力,URL 调度器就需要决定当前先下载哪些URL

如果调度器一段时间内选择的都是同一个域名的 URL,那就意味着我们的爬虫将以 800TPS 的高并发访问同一个网站。目标网站可能会把爬虫判定为 DoS 攻击,从而拒绝请求;更严重的是,高并发的访问压力可能导致目标网站负载过高,系统崩溃。这样的爬虫是“不貌”的,也不是 Bajie 的设计目标。

前面说过,网页之间的链接关系构成一个有向图,因此我们可以按照图的遍历算法选择URL。图的遍历算法有深度优先和广度优先两种,深度优先就是从一个 URL 开始,访问网页后,从里面提取第一个 URL,然后再访问该 URL 的页面,再提取第一个 URL,如此不断深入。

深度优先需要维护较为复杂的数据结构,而且太深的下载深度导致下载的页面非常分散,不利于我们构建搜索引擎和数据分析。所以我们没有使用深度优先算法。

那广度优先算法如何呢?广度优先就是从一个 URL 开始,访问网页后,从中得到 N 个URL,然后顺序访问这个 N 个 URL 的页面,然后再从这 N 个页面中提取 URL,如此不断深入。显然,广度优先实现更加简单,获取的页面也比较有关联性。

图的广度优先算法通常采用队列来实现。首先,URL 调度器从队列头出队列(dequeue)取一个 URL,交给 URL 下载服务器,下载得到 HTML,再从 HTML 中提取得到若干个URL 入队列(enqueue)到队列尾,URL 调度器再从队列头出队列(dequeue)取一个URL……如此往复,持续不断地访问全部互联网页,这就是互联网的广度优先遍历。

事实上,由于待下载 URL 集合存储在文件中,URL 下载服务器只需要向待下载 URL 集合文件尾部追加 URL 记录,而 URL 调度器只需要从文件头顺序读取 URL,这样就天然实现了先进先出的广度优先算法,如下图:

image-20231129144749501

但是,广度优先搜索算法可能会导致爬虫一段时间内总是访问同一个网站,因为一个HTML 页面内的链接常常是指向同一个网站的,这样就会使爬虫“不礼貌”。

通常我们针对一个网站,一次只下载一个页面,所以 URL 调度器需要将待下载 URL 根据域名进行分类。此外,不同网站的信息质量也有高低之分,爬虫应该优先爬取那些高质量的网站。优先级和域名都可以使用不同队列来区分,如下图:

image-20231129144855047

首先优先级分类器会根据网页内容质量将域名分类(后面专栏会讲 PageRank 质量排名算法),并为不同质量等级的域名设置不同的优先级,然后将不同优先级记录在“域名优先级表”中。

接下来,按照广度优先算法,URL 列表会从待下载 URL 集合文件中装载进来。根据“域名优先级表”中的优先级顺序,优先级分类器会将 URL 写入不同的队列中。

下一步,优先级队列选择器会根据优先级使用不同的权重,从这些优先级队列中随机获取URL,这样使得高优先级的 URL 有更多机会被选中。而被选中的 URL 都会交由域名分类器进行分类处理。域名分类器的分类依据就是“域名队列映射表”,这个表中记录了不同域名对应的队列。所以域名分类器可以顺利地将不同域名的 URL 写入不同的域名队列中。

最后,域名队列选择器将轮询所有的域名队列,从其中获得 URL 并分配给不同的 URL 下载服务器,进而完成下载处理。

2、去重算法

爬虫的去重包括两个方面,一个是 URL,相同 URL 不再重复下载;一个是内容,相同页面内容不再重复存储。去重一方面是提高爬虫效率,避免无效爬取;另一方面提高搜索质量,避免相同内容在搜索结果中重复出现。URL 去重可以使用布隆过滤器以提高效率。

内容去重首先要判断内容是否重复,由于爬虫存储着海量的网页,如果按照字符内容对每一个下载的页面都去和现有的页面比较是否重复,显然是不可能的。

Bajie 计算页面内容的 MD5 值,通过判断下载页面的内容 MD5 值是否已经存在,判断内容是否重复。

如果把整个 HTML 内容都计算 MD5,那么 HTML 中的微小改变就会导致 MD5 不同,事实上,不同网站即使相同内容的页面,也总会改成自己的 HTML 模板,导致 HTML 内容不同

所以,比较内容重复的时候,需要将 HTML 里面的有效内容提取出来,也就是提取出去除HTML 标签的文本信息,针对有效内容计算 MD5。更加激进的做法是从有效内容中抽取一段话(比如最长的一句话),计算这段话的 MD5,进而判断重复。

而一个内容 MD5 是否存在,需要在千亿级的数据上查找,如果用 Hash 表处理,计算和内存存储压力非常大,我们将用布隆过滤器代替 Hash 表,以优化性能。

3、高可用设计

Bajie 的可用性主要关注两个方面,一是 URL 调度器或 URL 下载处理服务器宕机,二是下载超时或内容解析错误。

由于 Bajie 是一个离线系统,暂时停止爬取数据的话,不会产生严重的后果,所以 Bajie 并不需要像一般互联网系统那样进行高可用设计。但是当服务器宕机后重启时,系统需要能够正确恢复,保证既不会丢失数据,也不会重复下载。

所以,URL 调度器和 URL 下载处理服务器都需要记录运行时状态,即存储本服务器已经加载的 URL 和已经处理完成的 URL,这样宕机恢复的时候,就可以立刻读取到这些状态数据,进而使服务器恢复到宕机前的状态。对于 URL 下载处理服务器,Bajie 采用 Redis 记录运行时状态数据。

此外,为了防止下载超时或内容解析错误,URL 下载处理服务器会采用多线程(池)设计。每个线程独立完成一个 URL 的下载和处理,线程也需要捕获各种异常,不会使自己因为网络超时或者解析异常而退出。

4、总结

架构设计是一个权衡的艺术,不存在最好的架构,只存在最合适的架构。架构设计的目的是解决各种业务和技术问题,而解决问题的方法有很多种,每一种方法都需要付出各自的代价,同时又会带来各种新的问题。架构师就需要在这些方法中权衡选择,寻找成本最低的、代价最小的、自己和团队最能驾驭得住的解决方案。

但是如果 URL 调度器采用分布式集群架构提高可用性,多服务器共同进行 URL 调度,就需要解决数据一致性和数据同步问题,反而会导致系统整体处理能力下降。而 Bajie 采用单机部署的的方式,虽然宕机时系统无法正常运行,但是只要在运维上保证能快速重新启动,长期看,系统整体处理能力反而更高。

此外,对于一个千亿级网页的爬虫系统而言,最主要的技术挑战应该是海量文件的存储与计算,这也确实是早期搜索引擎公司们的核心技术。但是,自从 Google 公开自己的大数据技术论文,而 Hadoop 开源实现了相关技术后,这些问题就变得容易很多了。Bajie 的海量文件存储就使用了 Hadoop 分布式文件系统 HDFS,后面我们会继续详细讲解。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/185779.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣刷题-122买卖股票的最佳时机

题目要求如上,这里可以有两种解题思路,一种是利用动态规划去求解,一种是用贪心去求解。 首先看下动态规划的方法。 用动归去解决 动态规划最重要的就是要想出来递推公式(这个真的很难),但是一旦想清楚递推…

VMware与Linux安装

VM与Linux安装 1、安装VMware ​ 这里安装Vm主要是为了安装Linux系统,除了相对云服务器,比较大众化的操作,当然更多的是熟悉Linux操作 1、Windows安装 ​ (1) 下载链接,目前版本上下载VM15的版本即可https://www.vmware.com/p…

阿里云服务器部署node和npm

目录 1.链接服务器2.找到node 下载地址3获取链接地址4下载到linux5.解压6.重命名 解压后的文件7.配置环境变量7.1复制当前的bin目录7.2vim /etc/profile7.3在按下ESC按键 8.重启环境变量9.输入node10.npm配置加速镜像 1.链接服务器 2.找到node 下载地址 https://nodejs.org/d…

CTO对生活和工作一点感悟

陌生人,你好啊。 感谢CSDN平台让我们有了隔空认识,交流的机会。 我是谁? 我呢,毕业快11年,在网易做了几年云计算,后来追风赶上了大数据的浪潮,再到后来混迹在AI、智能推荐等领域。 因为有一颗…

eNSP实验

前言 本文记录了使用eNSP进行组网,学习、巩固一些之前学的网络基础知识和协议。 一:同网段、网关互通 网络拓扑如下: AR1的配置: interface G0/0/0 ip address 192.168.10.1 24 PC1和PC2的配置(IP地址和网关设置) 最终实现PC1…

强芯铸魂,生态共赢!麒麟信安出席2023龙芯产品发布暨用户大会

11月28日,“到中流击水——2023龙芯产品发布暨用户大会”在北京国家会议中心隆重举办,会上发布新一代通用处理器龙芯3A6000、打印机主控芯片龙芯2P0500重磅成果。主管部门领导、专家学者、权威媒体等4000余人齐聚大会,麒麟信安作为龙芯合作伙…

【Linux学习】文件描述符重定向缓冲区

目录 九.文件描述符 9.1 文件描述符概念 9.2 文件描述符的分配规则 9.3 重定向 9.3.1 常见的重定向操作 9.3.2 重定向的原理 9.4 缓冲区 9.4.1 缓冲区概念 9.4.2 缓冲区刷新策略 9.4.3 C语言的缓冲区在哪里? 九.文件描述符 9.1 文件描述符概念 在上一篇讲到基础IO时,我们说到…

【C++】: unordered_map的使用

1、概念 key 键值的类型。unordered_map中的每个元素都是由其键值唯一标识的。 T 映射值的类型。unordered_map中的每个元素都用来存储一些数据作为其映射值。 Hash 一种一元函数对象类型,它接受一个key类型的对象作为参数,并根据该对象返回size_t类型…

Flask SocketIO 实现动态绘图

Flask-SocketIO 是基于 Flask 的一个扩展,用于简化在 Flask 应用中集成 WebSocket 功能。WebSocket 是一种在客户端和服务器之间实现实时双向通信的协议,常用于实现实时性要求较高的应用,如聊天应用、实时通知等,使得开发者可以更…

java系列:什么是SSH?什么是SSM?SSH框架和SSM框架的区别

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 什么是SSH?什么是SSM?SSH框架和SSM框架的区别 前言一、什么是SSH?1.1 Struts2具体工作流程:Struts2的缺点: 1.2 Sp…

【Linux】firewall防火墙配置-解决Zookeeper未授权访问漏洞

背景: zookeeper未授权访问漏洞,进行限制访问,采用防火墙访问策略 配置步骤: ##查看firewall配置清单 firewall-cmd --list-all ##查到为关闭态,启动防火墙 systemctl start firewalld ## 添加端口,这里…

Python入门06布尔值

目录 1 什么是布尔值2 怎么生成布尔值3 在控制程序中使用布尔值4 数据过滤、排序和其他高级操作总结 1 什么是布尔值 首先我们要学习一下布尔值的定义,布尔值是一种数据类型,它只有两个可能的值:True(真)或 False&…

rabbitmq消息队列实验

实验目的:实现异步通信 实验条件: 主机名 IP地址 组件 test1 20.0.0.10 rabbitmq服务 test2 20.0.0.20 rabbitmq服务 test3 20.0.0.30 rabbitmq服务 实验步骤: 1、安装rabbitmq服务 2、erlang进入命令行,查看版本 …

瑜伽学习零基础入门,各种瑜伽教学方法全集

一、教程描述 练习瑜伽的好处多多,能够保证平衡健康的身体基础,提升气质、塑造形体、陶冶情操,等等。本套教程是瑜伽的组合教程,共由33套视频教程组合而成,包含了塑身纤体,速效瘦身,四季养生&a…

双通道 H 桥 5V 4A驱动芯片

SS6951A 为电机一体化应用提供一种双通道集成电机驱动方案。SS6951A 有两路 H 桥驱动,每个 H 桥可提供最大峰值电流 4.0A,可驱动两个刷式直流电机,或者一个双极步进电机,或者螺线管或者其它感性负载。双极步进电机可以以整步、2 细…

字节大佬整理测试用例编写规范

目录 1.1目的 1.2使用范围 二 测试用例编写原则 2.1系统性 2.2连贯性 2.3全面性 2.4正确性 2.5符合正常业务惯例 2.6仿真性 2.7容错性(健壮性) 三 测试用例设计方法 3.1 等价类划分法: 3.2 边界值分析法: 3.3 因果图…

Nginx基线检查

扩展知识: Nginx主配置文件:/etc/nginx/nginx.conf 这是Nginx的主要配置文件,用于配置全局的设置、HTTP块、事件处理、邮件等内容。 打开并编辑配置文件 vim /etc/nginx/nginx.conf 一、关于禁止显示服务器版本号和操作系统版本信息: 简介: 在错误页面和响应头中显示…

多路转接<select>和<poll>使用手册

select int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout); 参数说明 返回值 返回值>0 表示成功返回可访问的文件描述符个数&#xff0c;返回值0 表示标识等待时间到期返回值<0 表示出现错误…

[蓝桥杯习题]———位运算、判断二进制1个数

⭐Hello!这里是欧_aita的博客。 ⭐今日语录&#xff1a;行动胜过一切。 ⭐个人主页&#xff1a;欧_aita ψ(._. )>⭐个人专栏&#xff1a; 数据结构与算法&#xff08;内含蓝桥杯习题&#xff09; MySQL数据库 位运算 位运算位运算的定义简单运用 实战刷题题目思路代码实现声…

Apipost推出IDEA插件,代码写完直接调试

IDEA是一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它可以帮助开发人员更加高效地编写、调试和部署软件应用程序。我们在编写完接口代码后需要进行接口调试等操作&#xff0c;一般需要打开额外的调试工具。 今天给大家介绍一款IDEA插件&#xff1a;Api…