爬虫系统构架演进-从脚本到服务的蜕变

最近和很多朋友聊天,有即将毕业的实习生,有资深工程师,也有资深产品经理。大家对于爬虫的认知也有非常大的不同(这个是显而易见的)。很多不了解这项技术的朋友甚至把爬虫技术和黑客技术混为一谈。甚至很多非法倒卖个人隐私数据的公司都被媒体描述为利用了爬虫技术非法获取和买卖数据,这不免让爬虫技术无辜背锅。本文就不解释爬虫的起源,有兴趣的朋友可以在网上搜索一下雅虎(Yahoo),谷歌(Google)和百度(Baidu)的故事,应该会对爬虫的起源有所了解。本文将以新的视角,通过需求的不断演变,描述一个简单的爬虫平台到一个数据服务平台的演进过程。

人工智能背景下的数据饥渴

进入21世纪第二个十年,人工智能春风吹遍了全球。随着人工智能的需求大增,作为人工智能这枚高速升空的火箭燃料,数据的需求与日俱增。凡是和数据有点关系的公司都突然发现了自己原来坐在金矿上。如果说21世纪的第一个十年是全球倡导开放的十年,那么第二个十年,各大公司关起门来攒数据成为了大趋势。之前烂大街的公开信息突然变成了石油。一夜人工智能的春风,各大公司都想借着风口飞上天,突然之间各大公司都得了数据饥渴症。爬虫技术和人工智能被紧紧地捆绑在了一起。可以这样说,没有爬虫技术的人工智能公司不是好科技公司。

忽如一夜春风来

突然之间,一段使用requests的python脚本成为了爬虫技术的敲门砖。人生苦短,我用Python的口号也响亮了起来。几乎每一个面试过的候选人,都会有一段爬取豆瓣评论的过往经验。实际上基于二八定律,那一段简单的requests脚本可以解决当前80%的数据需求。唯一的问题是——成本

任何开发都是以需求驱动的。在立项前需要评估需求技术可行性,人力成本,硬件成本和交付日期。而其中的技术可以性和交付日期又直接影响到人力成本。所以最终使用什么解决方案,完全取决于最终的成本是否可以负担。

简单的解决方案与巨大的人力成本

做过爬虫的都知道,最简单的爬虫脚本加上数据保存的代码超不过10行。而且这招“亢龙有悔”几乎打遍天下无敌手。技术难度系数几乎为0,一个网站理论上的开发时间不超过2天(已经非常保守,熟练工2个小时能搞定)。看似简单省力的解决方案背后隐藏着巨大的成本陷阱。随着需求的进一步深入,慢慢的开发人员会发现这个方案是一个巨坑。

#1. 需求通常不是一个小网站
如果有人认为写过豆瓣爬虫就算会爬虫,那就有点图样图森破了。通常需求都是很有霸气的,比如说:“我要新浪的所有新闻”。首先这就不是2个小时的工作量了,可以说2天都不止,2个礼拜应该没问题。于是开发人员欣然接受这个霸气的挑战。

#2. 需求通常不是一个网站
既然一个“新浪”难不住,“搜狐”应该也不是问题的。从技术上说,的确是没问题的。保守估计2个礼拜应该没问题。当然,这次不是一个“搜狐”。需求包括了今日头条,搜狐,网易,腾讯新闻,雅虎。初步估计需要2个半月吧。加上修Bug,爬数据,传文件。满打满算3个月。这样的交付时间是完全不能接受的。为了缩短时间,在加两个人吧。一共三个人,一个月时间。产品经理欣然接受交付时间,项目经理对于3个人月的成本有些为难。

#3. 需求通常不是一次性的
过了半个月,交付了一半的成果。突然需求变了。由于前期的数据质量不错,领导非常满意,因此希望增加更多的数据源网站。产品经理给出了一个列表,里面有100多个大大小小不同的网站。此时的开发已经累成狗了。产品与开发的矛盾已经逐渐暴露。实际上这个矛盾还是开发成本逐渐增高的矛盾。

问题总结

这种看似只要通过堆人力就能解决的问题,随着业务量的增加和需求的变化,最终会体现为成本的不断增长。然而,成本是有天花板的。做项目不可能亏本做,最后不可支撑的成本会倒逼技术团队做技术上的革新。简单的脚本是不能提供系统级别的服务的,更无法支撑长期的服务成本。因此一个更加通用,更加高效,更加低成本的解决方案呼之欲出。

几种不同的爬虫实现方案

一劳永逸的解决方案是没有的;既要保证低成本投入,又要保证数据准确性,还要保证高效率运行的解决方案是无法实现的。因此在方案选型的过程中,必须根据具体的需求,在能够承受的成本范围内,选择一套最适合的解决方案。以下就为大家介绍几款比较常见的方案。

基于Newspaper3K的新闻通用爬虫

Newspaper3K是一个开源的智能提取页面内容的项目。能够从新闻页面中提取包括标题,内容,作者,时间,摘要和关键词等。理论上说,一次开发就能匹配所有新闻页面。然而该项目并不能百分百的识别所有新闻页面,对于作者,时间的识别率也比较低。另外,也不能混合识别中文和英文的文章。需要在识别前设置language参数。对于数据的精确性要求不是很高,并且需要大量的新闻内容,可以考虑使用该方案。具体见Newspaper3K官网

基于模板的文本通用爬虫

对于文本数据的精确性要求高,或者文本数据需要结构化的输出。那么可以使用基于模板的通用爬虫方案。与NP3k新闻通用爬虫不同的是,这种方案适用于各种文本类型的数据抓取。精准度相当高,速度快。但是开发周期长,开发成本高(主要是人力投入配模板)。几乎是每一个数据源需要写一个模板,但是相同结构的页面可以共用模板。一个人力投入,大概每天可以出10个不同的模板(如果xpath和re熟练度高的话,速度会更快)。模板爬虫可以使用XMLS技术实现。

脚本爬虫

基本上就是最上面举得例子。每一个需求需要写一个脚本实现。这种方案一般针对于单一特殊需求,没有复用能力的解决方案。成本最高,难度大,但是运行效率应该是最高的。具体实现可以根据具体的项目需求,因此没有特别的技术选型。

分布式爬虫系统

对于大型项目来说,不可能靠一台机器爬取数据。一个一天10万以上数据量的爬虫系统至少需要30台以上的服务器同步进行。因此,除了爬虫程序本身的开发以外,还需要设计和搭建一套完成系统。同样,一个爬虫系统也可以很简单,也可以很复杂。具体还是需要根据需求,因地制宜地选择。

基于Scrapy的分布式爬虫系统

熟悉爬虫技术的朋友都听说过大名鼎鼎的Scrapy项目,一个开源的Python爬虫项目。该项目可以快速的开发一个爬虫脚本,但是之前我们已经详细描述了这种解决方案的弊端。一个基于Scrapy的分布式爬虫系统Scrapy Redis可以了解一下。

简单的分布式爬虫系统Scrapy Redis

具体的Scrapy Redis作为一个开源项目系是比较成熟的。在Scrapy的基础上添加了Redis内存型数据库的支持,初步实现了一个简单的分布式爬虫系统。Redis主要作为分布式系统的任务调度和地址去重等。一个8核32GB内存的Redis服务器可以轻松支持50台以上分布式爬虫机器,每台服务器上跑5个以上爬虫进程也是完全没有问题的。

Scrapy Cluster集成Kafka分布式爬虫系统

相对于Scrapy Redis分布式系统,Scrapy Cluster集成了Kafka消息队列,增强了系统的横向扩展能力,以及数据接受和数据流处理能力。复杂度也高了很多。Scrapy Cluster是一个开源项目(官网)。本文只是介绍该系统的设计思路,并不是推荐该项目。主要是在调研了该项目后,发现该项目并没有比较大型项目实施,Scrapy Cluster存在着非常大的高并发问题,高可用问题,和性能问题。在作此文时,项目已经很久没有更新了,还是Python2.7的版本(2020年以后Python2.7停止维护)。因此,在大家构建自己的项目过程中,之可借鉴其思路。
scrapy cluster design

Scrapy Cluster各组件基本工作原理
本文主要介绍爬虫系统的构架设计,不会详细的介绍Scrapy Cluster构架。有兴趣的朋友可以参考之前几篇关于Scrpay Cluster的文章。Scrapy Cluster主要由Kafka,Redis,Scrapy组成。Kafka和Redis之间主要由一个kafka-monitor的工具连接。主要是从kafka队列中获取爬取任务,并格式化以后发送给Redis。Redis和Scrapy是直连的,由Scrapy中的distributed_scheduler.py(该文件并不是Scrapy项目中的默认Scheduler,是Scrpay Cluster项目自建的)直接访问Redis。Redis负责缓存爬取任务,去重任务,下载优先级,流量控制等等。Scrapy下载完数据以后,由Scrapy Cluster项目中自建的Kafka Pipeline把数据推送会Kafka,待后续业务处理。

Scrapy Cluster系统升级-跨区域部署

Scrapy Cluster默认是部署在一个局域网中的,Kafka,Redis等都配置私有地址,Scrapy机器配置共有IP即可。但是这样的结构有很大的局限性。例如,在华南地区的主机访问华东或者华北地区的网站就非常慢。如果系统有全球任务的话,访问合法国外网站就会更慢。因此,可能还需要国外节点。在这种情况下,有两种解决方案:

  • 在不同的地区配置整套系统
  • 这种方案的优点是方案简单,容易部署,任务独立。缺点是,管理相对复杂,成本高,横向扩展比较麻烦。另外对于后期的数据处理也比较繁琐(需要接入不同区域的Kafak主机消费数据。

  • 在一个区域配置中心系统,分区域配置爬虫节点
  • 这种方案的有点是易扩展,成本低。理论上是一套系统,横向扩展爬虫节点即可。缺点是,任务的管理相对复杂。什么任务发送到什么节点,什么节点获取什么任务,什么节点配置什么爬虫,等等。另外就是爬虫节点连接到中心Kafka和Redis的速度不一致。另外Kafka节点和Redis节点都需要配置公网IP地址,对于安全方面要求也相对较高。

Scrapy Cluster分布式系统公网方案

之前已经介绍过,该方案需要为Kafka集群和Redis都配置公网IP。这样就非常方便横向扩展Scrapy集群主机,几乎没有任何限制。

Kafka集群公网配置
Kafka集群可以实现公网访问和局域网访问同时配置。即处于不同区域的Scrapy节点机器可以通过公网访问Kafka集群,处于相同区域的Scrapy节点机器可以通过局域网访问Kafka集群。Kafka集群各个节点通过局域网连接,保证服务稳定。具体可以参考:

Redis集群公网配置
Redis的配置相对简单,集群配置的方案也比较容易找到,这里就不重复搬砖了。

Scrapy分布式公网配置
在Scrapy中只需要指定Kafka的地址和Redis的地址即可。原则上遵从处于不同区域节点机器可以通过公网访问,处于相同区域的节点机器可以通过局域网访问。

安全配置
由于Kafka集群,Redis集群都配置了公网IP,可以通过公网访问服务。因此存在非常高的安全隐患。因此在安全策略配置方面需要非常重视。我们可以使用IPtable(netfilter)实现访问策略设置。由于整个分布式系统的主机都是已知的,所以设置也非常方便。这里简单描述以下:首先Kafka的只允许固定的Scrapy机器可以访问估计的服务端口,其它地址和端口都限制访问。Redis也采取同样的操作。Scrapy节点机器只配置可以出方向访问所有的80/443端口和对应的Kafka端口,Redis端口等。另外,根据需求适当开放22端口(最好修改默认的22端口为高阶端口,防止扫描和暴力强攻)。

分布式系统的几个问题

分布式系统和单台爬虫系统的最本质区别在于规模。一切问题都与规模相关,以下简要分享一些分布式系统所面临的问题。

爬虫脚本的部署维护

系统上了规模以后,爬虫脚本的部署和维护就是一个绕不过的坑。部署或者升级一台机器的爬虫程序和部署升级50台机器的爬虫程序是完全不一样的。这里提供一个最简单的思路就是使用SSH加上Bash脚本。为每台服务器配置好管理员公钥,通过免密码使用SSH远程执行,SFTP远程传输或者SCP远程传输文件实现部署升级工作。

监控日志

系统上了规模以后另外一个具有挑战的任务就是监控服务器的性能和日志。每台服务器上都同时运行着若干个爬虫进程,每个进程的状态和日志如何方便的查看是非常具有挑战的任务。我们在实际情况中使用的是ELK日志系统,即Elasticsearch, Logstash和Kibana。另外,每台机器上还要配置filebeat服务。

服务器配置更新

这个当前还没有找到好的方案。例如已经有100台机器在运行服务,突然要在现有机器上安装新的Python包。当前的方案是使用SSH远程调用终端命令。

Redis服务器CPU使用率100%问题

Scrapy-Cluster项目是基于Scrapy的一个开源项目,其中使用Redis作为任务缓存。然而在项目的这个设计思路中存在很严重的性能问题。当Scrapy节点主机增加,Scrapy进程数增加,Redis服务器CPU使用率就会越来越高。究其原因,最后检察源代码发现在Scrapy-clustr的distributed_schedular.py文件中使用了Redis的keys函数,这是一个非常低级的错误。

MongoDB服务器CPU使用率100%问题

Scrapy-Cluster项目本身得构架中是没有MongoDB的。爬取数据以后会通过Pipeline推送到Kafak中等待消费。为了解决最后一公里的数据使用问题,我们简单的使用一个脚本从MongoDB中把数据拿出来,并推送进MongoDB数据库。由于数据量的日益增长,MongoDB会碰到越来越多的问题,比如Too Many Open Files问题。在读取MongDB的过程中,还会遇到CPU使用率100%的问题。问题主要集中在建立索引和查询条件优化上,具体可以参考《提升MongoDB查询速度测试》。

Captain QR Code

扫码联系船长

发表回复

您的电子邮箱地址不会被公开。