基于Scrapy框架的分布式网络爬虫的研究与实现
2018-10-20华云彬匡芳君
华云彬 匡芳君
Abstract: Aiming at the problems of offensive, defensive, and crawling efficiency in the development of Web crawlers, the paper focuses on analyzing the working principle and implementation of the distributed crawler based on the Scrapy framework, as well as some distributed operating principles, anti-reptiles, and the algorithm of duplicate removal, Redis database, MongoDB database, etc., designs and implements a distributed Web crawler based on Scrapy framework. Finally, through comparative test and analysis of the crawler, it is concluded that how to improve crawling efficiency of the crawler and avoid the anti-crawler strategy of the site.
引言
随着互联网的发展,大数据时代的到来,普通搜索引擎已无法满足人们对信息获取的需求,网络爬虫应运而生,如百度的网络爬虫Baiduspider、谷歌的网络爬虫Googlebot等[1],也陆续涌现了很多成熟的爬虫框架,如本文使用的Scrapy[2]。但其从催生传承演变至今,爬虫开发也已面临着一些问题,对此可阐释分析如下。
(1)网站与爬虫之间的攻防问题 [3]。针对爬虫无限制地爬取所有网页的状况,制定了robots协议[4],但由于该协议并未成为一个严谨规范,只是约定俗成的技术守则,故而不能真正地阻止网络爬虫,因此出现了反爬虫技术。本文将使用分布式技术来解决这个问题。
(2)统一资源地址的去重问题。在爬取的过程中,爬虫经常会遇到相同的URL,如果再次爬取,就造成了资源的浪费。普遍采取的方案是使用md5等哈希后存入set。而本文将使用BloomFilter算法[5]来解决去重问题。
(3)爬虫爬取效率问题。如今的爬虫普遍存在爬取效率较低的问题,为了解决该问题,本文的爬虫采用了分布式[6]、代理池等技术。
综上可知,本文重点围绕前述问题,研究和实现基于Scrapy框架的分布式网络爬虫。
1相关关键技术
1.1网络爬虫技术
网络爬虫,又称为网络蜘蛛,是一种能够根据程序设计者所设计的规则,自动抓取特定网页信息的程序。网络爬虫始于一个或多个事先确定的被称为种子的URL地址,如果能够访问则下载其中的内容,而后利用程序设计者开发的解析模块,提取其中所需要的数据或者其它的URL链接,并将获取到的URL链接加入到待爬取列表中,重复这一步骤,直至待爬列表为空。网络爬虫按照实现原理可分为通用网络爬虫、增量式网络爬虫、聚焦网络爬虫3种类型[7]。本文主要研究聚焦型网络爬虫。
目前,网络爬虫一般分为深度优先和广度优先2种爬取策略算法[8]。前者一般用栈实现,即一个爬虫程序爬取一个HTML文件后,只要找到该文件中的一个链接,就立即去爬取该链接并重复执行深度优先算法,直至到达终点后,重新返至可前溯的HTML文件中找尋其它链接。后者则一般用队列实现,即一个爬虫程序爬取一个HTML文件后,找到该文件中所有链接,再存入队列中,然后根据队列中链接继续爬取并重复执行广度优先算法。本文网络爬虫采用广度优先算法。
1.2Scrapy框架技术
Scrapy是Python开发的一个高层次的、快速的Web抓取框架[9],用于抓取网页并从中提取结构化数据,已广泛应用于自动化测试与数据挖掘领域研究中。Scrapy框架主要由引擎(Engine)、调度器(Scheduler)、Spiders、下载器(Downloader)、Item Pipeline、中间件(Middleware)组成,设计架构即如图1所示。
1.3Redis分布式爬虫相关技术
分布式爬虫,又称集群爬虫。分布式爬虫可实现多台机器同时爬取,爬取速度快、可轻松避开对方的反爬虫机制对IP的封锁检测。网络爬虫是一个偏向IO的任务,研究认为分布式爬虫主要考虑爬虫任务的去重、爬虫任务的统一调度和爬虫的速度、存储等问题,而Redis则能有效解决这些问题。Redis是一个非关系型数据库,支持key-value和list、set、hash等数据结构,同时Redis也是一个内存数据库,具备着读取速度快的优点。本文研究采用了基于Scrapy框架和Redis的分布式爬虫,爬虫调度任务则采用Python的scrapy-redis模块实现。
1.4爬虫去重BloomFilter算法
网络爬虫的效率至关重要,而爬虫的URL链接去重则是影响爬虫效率的主要因素之一。在实际应用中,爬虫在爬取数据时,往往会爬取到许多重复的URL 链接,如果再对这些链接进行爬取,就会造成内存及带宽资源的极大浪费,从而降低爬虫效率。针对这种情况,研究中集结提炼了数种爬虫的去重策略,可分述如下。
(1)将URL保存到MySQL等数据库中,每次爬取之前访问数据库查询,效率低。
(2)将URL保存到set中,访问速度快,但内存占用巨大,且不利于持久化。
(3)将URL使用md5等算法哈希后存入set中。Scrapy框架默认使用的即为该方法。
(4)使用BitMap方法,将URL通过hash进行映射。
(5)使用BloomFilter算法对BitMap方法提供改进。
2分布式网络爬虫的设计与实现
2.1网络爬虫的功能需求分析
本文主要研究基于Scrapy框架的分布式网络爬虫。为了更好测试爬虫的效率,本爬虫并没有采取常见的爬取目标网页,而是采用了API接口的形式进行数据的爬取。爬取的目标选择了知乎网(www.zhihu.com)的公开用户信息,通过API接口的形式爬取大量知乎用户的基本信息。这里,关于该网络爬虫抓取流程的分析将展开如下探讨论述。
首先为了获取知乎的用户信息API接口,使用了Chrome自带的抓包工具对从知乎上随机选取的一名用户的主页进行了分析,最终成功提取到了用户信息的API接口(https://api.zhihu.com/people/xxxxxx,xxxxxx为用户ID),该API返回的信息为JSON格式。这样一来,已爬取得到了该用户的信息。然而API接口中,却仍未得到用户的ID。为了能大批量地获取用户的ID,通过对知乎的深入研究后发现,知乎的每位用户主页中,有一个用户关注他人列表及一个他人关注用户的列表。如此再经过抓包工具的分析,就成功获取到了这2个列表的API接口。这2个接口的API中有一个参数为token值,该值是唯一的,可以从前述的用户信息返回的JSON数据中获取到。至此,就可以大批量地爬取知乎用户信息。
2.2数据爬取的流程设计
分布式网络爬虫根据设定的初始URL地址去爬取数据,使用广度优先算法完成数据的获取任务直至符合网络爬虫结束的条件。本项目中,网络爬虫首先根据给定的初始用户API接口去爬取,获取数据后解析得到用户的token值,然后根据token值去爬取该用户的关注他人列表及一个关注该用户的他人列表,获取这2个列表中所有用户的ID值,再以此为基础去爬取用户的基本信息。重复以上步骤,直至网络爬虫结束终止本次爬取任务。知乎分布式网络爬虫的设计流程可如图2所示。
2.3反反爬的策略设计与实现
很多网站为了防止网络爬虫爬取数据,都纷纷出台了一定的策略来避免爬虫的爬取。然而,在实际应用中,在爬虫与反爬虫的对弈中,爬虫终会胜利。结合本项目的知乎分布式爬虫,研发定制了一些本爬虫所采用的反反爬虫措施,可解析描述如下。
(1)设置User-Agent。这是网站上普遍采取的反爬虫措施,可以直接通过更改header中的User-Agent值来达成目的。在本项目中,使用了fake-useragent模块来动态更改User-Agent。
(2)设置爬虫爬取间隔。很多网站一般都会检测一个IP的单位时间内的访问次数,如果某用戶访问速率偏高,就容易导致IP被封。为了解决这个问题,可以设置爬虫的爬取间隔(如最简单的time.sleep(1)),还可以设置爬虫爬取一段时间后、添入一个睡眠机制等。
而关于方法(2),设置爬虫爬取间隔,虽然可以有效避免被封禁IP,但在实际项目中,考虑到需要大规模地爬取数据,爬取的效率尤为重要,而设置爬取间隔会大大地降低效率。因此在本项目中,采用了更换代理IP的方法进行解决,一旦IP被封禁,就切换代理IP。本项目维护了一个IP代理池(使用Requests库定期爬取网络上的免费代理IP,并定时检测可用性。IP存放在Redis数据库set中,有利于去重及方便调用)。除了前文陈述的本项目中用到的几种反反爬虫措施外,还有很多反反爬虫策略,如模拟登录使用Cookie、验证码识别、PhantomJS+Selenium(数据JS后期渲染)、使用JS的加密库生成动态Token等。
2.4爬虫分布式方案的设计与实现
本项目的分布式爬虫采用Scrapy框架加Redis数据库,分布式调度采用scrapy-redis模块。本项目把分布式爬虫主要分为Master端和Slave端2个部分。其中,Slave端重点是从Master端获取任务进行爬取,在爬取的同时也生成新的任务,并将任务传递给Master端,爬取到的数据则保存到Master端的MongoDB中。而Master端只有一个Redis数据库和一个MongoDB数据库,Redis就是对Slave端提交的任务进行去重及放入待爬取的队列,MongoDB则是用来存储获取到的用户信息[10]。本项目分布式策略逻辑可如图3所示。
2.5Scrapy各功能模块设计与实现
2.5.1Spider模块的设计与实现
Spider模块是爬取、分析知乎数据的类。该模块中设计中涉及多种比较重要的属性和方法,对其内容可阐述如下。
(1)allowed_domains属性:表示爬虫可以爬取该URL下的子链接。
(2)start_urls属性:是一个存放爬虫的初始URL链接的数组。
(3)parse方法:是scrapy.Spider类下的一个方法,每个生成的Response对象都会默认作为参数传递给该方法。在该项目中,使用该方法解析用户信息,获取用户的基本信息,给出用户信息的Item对象,并给出2个爬取用户关注他人列表及关注用户的他人列表数据的Request对象,回调函数(callback)为parse_follower方法。
(4)parse_follower方法:运行解析出2个列表中所有的用户ID,并给出爬取用户信息的Request对象,回调函数(callback)为parse方法。
2.5.2Items、Pipelines模块的设计与实现
Item是一个数据容器,用于保存爬取到的数据。Pipelines则用于设计处理生成的的Item。在本项目中,Items中有一个ZhiHuUserInfoItem类,类中定义了相应的字段Field(有token_url、info2个字段)。Pipelines中却配有一个把Item中的数据保存到数据库的类,该部分将在2.6数据存储中予以详情解析阐述。
2.5.3Middleware模块的设计与实现
Middleware模块有Downloader Middlewares和Spider Middlewares2个类型,本文主要采用Downloader Middlewares(下载中间件)。Middlewares方法设计可见如下。
(1)RandomUserAgentDownloaderMiddleware类:负责更换每个Request对象header中的User-Agent值。这里更换User-Agent使用了fake-useragent模块(https://github.com/hellysmile/fake-useragent),该模块可以随机更换不同操作系统、不同浏览器的User-Agent值。
(2)RandomProxyMiddleware类:负责更换代理IP及代理IP连接异常情况下的应对处理。其中,还将内配数种设计方法,可概述如下。
① process_request():負责更换Request的代理IP。
② cut_proxy():负责从Redis中获取一个代理IP。
③ process_response():负责处理请求结束后返回的信息,如果返回403错误,就意味着代理IP已失效,需要重新切换代理。
④ process_exception():负责处理连接时的异常情况。如TimeoutError、ConnectionRefusedError、ResponseFailed、ResponseNeverReceived、TunnelError、ConnectError等异常或错误情况。若遭遇以上情况,这里同样采用更换代理IP的方法获得解决。
2.6数据存储
网络爬虫需要爬取的数据量一般都十分庞大,且有些是结构化数据,有些则为非结构化数据,因此一个适合的数据库就尤显重要。在知乎用户信息爬虫项目中,获取到的数据为非结构化数据,并不适宜选择MySQL等关系型数据库,因而使用了MongoDB数据库进行数据的存储。
MongoDB 是一个基于分布式文件存储的数据库。在本网络爬虫中,研究仅定义了2个Key。一个为_id,存储用户信息的token_url;另一个为info,存储用户的基本信息。在Pipelines模块中,定义了一个MongoDBPipeline类,可用作数据的存储,数据的存储则采用了MongoDB中的update方法。其中,upsert属性需设置为True,该数据有则更新、无则插入。
3分布式网络爬虫的测试
3.1测试环境
本网络爬虫的测试环境基本信息可见表1。
3.2测试数据与分析
知乎分布式爬虫运行1 h的测试结果可见表2。使用了3台机器来测试分布式爬虫,从数据中可以看到,爬虫的爬取效率达到了预期的目标效果。
爬虫的性能分析折线图即如图4所示。从图4中看到,爬虫的爬虫效率波动仍然较大,究其原因就在于爬虫测试的网络环境不稳定所致。如果还需提升爬取速度,可以增加分布式爬虫的个数。
3.3URL去重策略比较测试分析
URL去重策略运行1 h的比较测试结果可见表3。由表3可以看出,使用BloomFilter算法比Scrapy默认去重(md5哈希后存入set)的去重效率得到了显著的提升。因此在海量数据的爬取上,使用BloomFilter算法(布隆过滤器)最为适宜。
4结束语
本文探讨了基于Scrapy框架的分布式网络爬虫的研究与实现。研究中,不仅论述了提高网络爬虫数据爬取效率的处理解决方法,而且提供了针对网站反爬虫的应对策略。最后,设计实现了一个知乎网用户信息的分布式网络爬虫,并爬取得到了大量的数据。在后续研究中,还将深入研究如何借助数据分析手段对数据进行分析,获取如知乎网用户分布、男女分布、学历分布等等信息,进一步地完善本爬虫。
参考文献
[1] 王岩. 搜索引擎中网络爬虫技术的发展[J]. 电信快报, 2008(10):20-22.
[2] Scrapy. Scrapy 1.5 documentation[EB/OL]. [2018]. https://docs.scrapy.org/en/latest/.
[3] 陈利婷. 大数据时代的反爬虫技术[J]. 电脑与信息技术,2016, 24(6):60-61.
[4] ]Wikipedia.robots.txt词条[EB/OL]. [2017-06-10]. https://en.wikipedia.org/wiki/MediaWiki:Robots.txt.
[5] 张笑天. 分布式爬虫应用中布隆过滤器的研究[D]. 沈阳:沈阳工业大学, 2017.
[6] 朱嵘良. 分布式并行环境下的网络爬虫研究[D] . 北京:中央民族大学,2015.
[7] 孙立伟,何国辉,吴礼发. 网络爬虫技术的研究[J]. 电脑知识与技术,2010,6(15):4112-4115.
[8] 杨定中,赵刚,王泰. 网络爬虫在Web信息搜索与数据挖掘中应用[J]. 计算机工程与设计. 2009,30(24):5658-5662.
[9] 安子建. 基于Scrapy框架的网络爬虫实现与数据抓取分析[D]. 长春:吉林大学, 2017.
[10]赵本本,殷旭东,王伟. 基于Scrapy的GitHub数据爬虫[J]. 电子技术与软件工程,2016(6): 199-202.