Scrapy分布式、去重增量爬虫的开发与设计

1,357
原文链接: click.aliyun.com

基于 python 分布式房源数据抓取系统为数据的进一步应用即房源推荐系统做数据支持。本课题致力于解决单进程单机爬虫的瓶颈,打造一个基于 Redis 分布式多爬虫共享队列的主题爬虫。本系统采用 python 开发的 Scrapy 框架来开发,使用 Xpath 技术对下载的网页进行提取解析,运用 Redis 数据库做分布式,使用MongoDb 数据库做数据存储,利用 Django web 框架和 Semantic UI开源框架对数据进行友好可视化,最后使用了Docker对爬虫程序进行部署。设计并实现了针对 58 同城各大城市租房平台的分布式爬虫系统。

30415a5e7f03337f342c17a2708881c671b5113d

分布式爬虫抓取系统主要包含以下功能:

1.爬虫功能: 爬取策略的设计 内容数据字段的设计 增量爬取 请求去重

2.中间件: 爬虫防屏蔽中间件 网页非200状态处理 爬虫下载异常处理

3.数据存储: 抓取字段设计 数据存储

4.数据可视化

完整项目源码

关注微信公众号 datayx 然后回复 分布式 即可获取。

二、系统分布式架构

分布式采用主从结构设置一个Master服务器和多个Slave服务器,Master端管理Redis数据库和分发下载任务,Slave部署Scrapy爬虫提取网页和解析提取数据,最后将解析的数据存储在同一个MongoDb数据库中。分布式爬虫架构如图所示。

a271733a08e05ac8f0473902ad12b3e88737294d

应用Redis数据库实现分布式抓取,基本思想是Scrapy爬虫获取的到的detail_request的urls都放到Redis Queue中,所有爬虫也都从指定的Redis Queue中获取requests,Scrapy-Redis组件中默认使用SpiderPriorityQueue来确定url的先后次序,这是由sorted set实现的一种非FIFO、LIFO方式。因此,待爬队列的共享是爬虫可以部署在其他服务器上完成同一个爬取任务的一个关键点。此外,在本文中,为了解决Scrapy单机局限的问题,Scrapy将结合Scrapy-Redis组件进行开发,Scrapy-Redis总体思路就是这个工程通过重写Scrapu框架中的scheduler和spider类,实现了调度、spider启动和redis的交互。实现新的dupefilter和queue类,达到了判重和调度容器和redis的交互,因为每个主机上的爬虫进程都访问同一个redis数据库,所以调度和判重都统一进行统一管理,达到了分布式爬虫的目的。

三、系统实现

1)爬取策略的设计 由scrapy的结构分析可知,网络爬虫从初始地址开始,根据spider中定义的目标地址获的正则表达式或者Xpath获得更多的网页链接,并加入到待下载队列当中,进行去重和排序之后,等待调度器的调度。 在这个系统中,新的链接可以分为两类,一类是目录页链接,也就是我们通常看到的下一页的链接,一类是内容详情页链接,也就是我们需要解析网页提取字段的链接,指向的就是实际的房源信息页面。网络需从每一个目录页链接当中,提取到多个内容页链接,加入到待下载队列准备进一步爬取。

ccadaab55c9af454440339e57662fd70621ccd48

此处是Master端的目标链接的爬取策略,因为采取的分布式主从模式,Master端爬虫主要爬取下载到内容详情页链接,通过redis分享下载任务给其他slave端的爬虫。Slave端主要是负责对详情页链接的进一步解析提取存储到数据库中。 本论文以58同城租房为例,其初始页链接,其实也就是每个分类的第一页链接,主要有(以广东省几个城市为例):

a5595174ee1b7a0d81ca3a509c18a5f747bff8c3

综上所述,网络房源爬取系统使用以下爬取策略:

1) 对于Master端: 最核心模块是解决翻页问题和获取每一页内容详情页链接。

Master端主要采取以下爬取策略:

1. 向redis往key为nest_link插入初始链接,从初始页链接开始

2. 爬虫从redis中key为next_link中取到初始链接,开始运行爬虫

3. 将下载器返回的Response,爬虫根据spider定义的爬取规则识别是否有下一页链接,若有链接,存储进redis中,保存key为next_link,同时根据匹配规则是否匹配到多个内容详情页链接,若匹配到,则存储进Redis,保存key为detail_request插入下载链接,给slave端的spider使用,即是Slave端的下载任务。

4. 爬虫继续从redis中key为next_link取值,若有值,继续步骤2,若为空,爬虫则等待新的链接。

2) 对于Slave端: 最核心模块是从redis获得下载任务,解析提取字段。Slave端主要采取以下爬取策略:

1.爬虫从redis中key为detail_request中取到初始链接,开始运行爬虫

2.将下载器返回的Response,爬虫根据spider定义的爬取规则识别是否有匹配规则的内容字段,若有将字段存储,返回到模型中,等待数据存储操作。 重复步骤1,直到带爬取队列为空,爬虫则等待新的链接。

2)爬虫的具体实现

爬虫程序的包含四个部分,分别是对象定义程序,数据抓取程序,数据处理程序和下载设置程序,此处的组成是Slave端,Master少了对象定义程序以及数据处理程序,Master端主要是下载链接的爬取。

(1)数据抓取程序 数据抓取程序分Master端和Slave端,数据抓取程序从Redis中获得初始地址,数据抓取程序中定义了抓取网页的规则和使用Xpath提取字段数据的方法等,这里着重介绍Xpath提取字符数据的方法,Xapth使用路径表达式来选取网页文档中的节点或者节点集。在Xpath中有其中类型的几点:元素、属性、文本、命名空间、处理指令、注释和文档节点。网页文档是被当做节点树来对待,树的跟被称为文档节点和根节点,通过Xpath表达式定位目标节点即可抽取网页文档的字段数据。

3)去重与增量爬取

去重与增量爬取,对于服务器有很重大的意义,能够减少服务器的压力以及保证数据的准确性。如果不采取去重处理,那么抓取的内容会抓取大量重复内容,让爬虫效率极大的下降。其实去重流程很简单,核心就是每次请求的时候,先判断这个请求是否在已经爬取的队列当中。如果已存在,则舍弃当前请求。 具体实现步骤:

70a2081c70f00b514fe0959369d1338ff8d3034d

 (1) 从待爬队列中获取url

(2) 将即将请求的url判断是否已经爬取,若已爬取,则将请求忽略,未爬取,继续其他操作并将url插入已爬取队列中

(3) 重复步骤1 这里我们使用scrapy-redis的去重组件,所以也没有实现,不过原理还是要看懂的,具体可以看源码。

4)爬虫中间件

爬虫中间件能够帮助我们在scrapy抓取流程中自由的扩展自己的程序,以下有爬虫防屏蔽中间件,下载器异常状态中间件以及非200状态中间件。

(1)爬虫防屏蔽组件的实现 访问一个网站的网页的时候,会给网站带了一定的负载,而爬虫程序则是模拟了我们正常访问网页的过程,但是。大规模的爬虫会给网站增加大量的负载,影响正常用户的访问。为保证网页能够别大多数正常用户的访问,大多数网站都有相应的防爬虫策略。一旦访问行为被认定为爬虫,网站将会采取一定的措施,限制你的访问,比如提示你,访问过于频繁让你输入验证码,更严重者,会封掉你的ip,禁止你访问该网站。本系统定向抓取网页数据的时候,将不间断的访问网站内容,如果不采取伪装措施,很容易被网站识别为爬虫行为而屏蔽掉。 本系统采用以下方法来防止爬虫被屏蔽:

3a12a3faffb8c25211059442808e3e9bf96f9ecb

(a)模拟不同浏览器行为实现思路及代码

原理:

从scrapy的介绍我们可以知道,scrapy有下载中间件,在这个中间件我们可以对请求跟响应进行自定义处理,类似于spring面向切面编程,像一个钩子嵌入到程序的运行前后。核心就是对请求的属性进行修改 首先主要是对下载中间件进行了扩展,首先在seetings.py上面增加中间件, 其次,扩展中间件,主要是写一个useragent列表,将常用的浏览器请求头保存为一个列表.

再让请求的头文件随机在列表中取一个agent值,然后到下载器进行下载。

6912318a032698d9be37a44f00aa8193b386f5a2

综上,每次发出请求的时候模拟使用不同的浏览器对目标网站进行访问。

(b)使用代理ip进行爬取的实现思路及代码。 首先在seetings.py上面增加中间件,扩展下载组件请求的头文件随机从代理ip池中取出一个代理值然后到下载器进行下载。

1. 代理ip池的设计与开发流程如下:

5fea6b31835da575f6268ef173de91c811d33d47

a. 对免费代理ip网站进行抓取。

b. 对代理ip进行存储并验证

c. 验证通过存储进数据库

d. 如果满足ip最大数量,则停止爬去,一定时间后验证数据的ip有效性,将失效的ip删除 e. 直到数据库ip小于0,继续爬取ip,重复步骤a。

代理ip爬虫运行截图:

4cd49734da56c8e0df8bb46ac7a5f3761084d2a4

(c)爬虫异常状态组件的处理 爬虫没有被屏蔽运行时,访问网站不是一直都是200请求成功,而是有各种各样的状态,像上述爬虫被禁的时候,其实返回的状态是302,防止屏蔽组件就是捕捉到302状态加以实现的。同时异常状态的处理有利于爬虫的健壮性。 在settings中扩展中间件捕捉到异常的情况之后,将请求Request重新加入到待下载队列当中流程如下:

4e0d716df09cc8dacb6cdf4a4ae30cca5b8afc4a

d)数据存储模块 数据存储模块主要负责将slave端爬取解析的页面进行存储。使用Mongodb对数据进行存储。 Scrapy支持数据存储的格式有json,csv和xml等文本格式,用户可以在运行爬虫时设置,例如:scrapy crawl spider -o items.json -t json,也可以在Scrapy工程文件额ItemPipline文件中定义,同时,Scrapy也支持数据库存储,如Monogdb,Redis等,当数据量大到一定程度时,可以做Mongodb或者Reids的集群来解决问题,本系统数据存储如下图所示:

7f9abd0d7d0684ccfd852e4d2d9d821b26d41038

(g)数据可视化设计 数据的可视化其实也就是,将数据库的数据转换成我们用户容易观察的形式,本系统使用Mongodb对数据进行存储。对数据进行可视化基于Django+Semantiui,效果如下图所示:

fceb24c5a1363d2be2e047e2c528a6edfe00b626

四、系统部署

因为分布式部署所需环境都是类似的,如果一个服务器部署程序都需要在配置下环境显得很麻烦,这里使用了docker镜像对爬虫程序进行部署,使用了Daocloud上的scrapy-env对程序进行了部署 。系统以58同城租房平台为抓取目标,运行十小时之后,持续抓取网页数量共计几万条房源数据。


原文发布时间为:2018-09-22

本文来自云栖社区合作伙伴“大数据挖掘DT机器学习”,了解相关信息可以关注“ 大数据挖掘DT机器学习”。