16

scrapy-redis分布式爬虫框架详解

随着互联网技术的发展与应用的普及,网络作为信息的载体,已经成为社会大众参与社会生活的一种重要信息渠道。由于互联网是开放的,每个人都可以在网络上发表信息,内容涉及各个方面。小到心情日志,大到国家大事。

互联网已成为思想文化信息的集散地,并具有传统媒体无法相比的优势:便捷性,虚拟性,互动性,多元性。网络新闻热点通常形成迅速,多是人们对于日常生活中的各种问题发表的各种意见,评论,态度,情绪等,随着事件的发展而变化,是反映社会热点的重要载体之一。

相比较而言,编写爬虫程序获取到的海量数据更为真实、全面,在信息繁荣的互联网时代更为行之有效。因此编写爬虫程序成为大数据时代信息收集的必备技能。

本文主要介绍爬虫收集数据优点、爬虫原理、scrapy爬虫框架,并以新闻爬取为例,详细讲解使用scrapy爬取数据的步骤以及scrapy-redis分布式。

一、爬虫收集数据的优点

大数据时代下,人类社会的数据正以前所未有的速度增长,传统的获取数据的方式如问卷调查、访谈法等,其样本容量小、信度低、且受经费和地域范围所限,因而收集的数据往往无法客观反映研究对象,有着较大的局限性。

以不能满足高质量研究的需求。正如Ivor的理论所揭示的,如果输入的是无效信息,无论处理的程序如何精良,输出的都是无用信息“Garbage In,Garbage Out”。可见,对比传统的数据收集方法,立足于海量数据的研究有以下的优点:

(一)数据的真实性
数据的真实性,使用问卷调查法收集的数据,调查者难以了解被调查者是认真填写还是敷衍了事。使得得到的数据真实性不可靠,而通过爬虫技术能快速获取真实、客观的用户信息,如在社交网络上对一个公司的评价显然要比问卷调查真实,淘宝、美团上消费者对卖家的评论就比较客观的反应了商品的质量。
 (二)样本容量
人类当初发明计算机是因为在二战时期工程师们已经无法计算导弹的飞行轨迹,庞大的计算量迫使了计算机的发明,可见计算机天生就是来处理大规模批量的数据,把人们从繁重的劳动中解放出来。在同样的成本下,人工采集和计算机采集的数据量不是一个量级的,爬虫可以对互联网上的海量数据进行收集、分析,能更好的反应客观事实,而数据越全面,分析研究的结果也就越真实。

二、爬虫原理

网络爬虫(Web crawler),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本,我们浏览的网页数以亿计,它们在世界各地的服务器上存储着。用户点击一个网页的超链接以跳转的方式来获取另一个页面的信息,而跳转的页面又有链接存在,网页便由超链接组成一个巨大且错综复杂的网。而Web爬虫(Crawler),也称蜘蛛(Spider),则是穿梭在这巨大的互联网中下载网页解析内容的程序。它们被广泛用于互联网搜索引擎,可以自动采集所有其能够访问到的页面内容,以获取或更新这些网站的内容和检索方式。
(一)爬虫的应用
在商务智能上,企业使用爬虫收集竞争对手的情报或在社交网络、虚拟社区上爬取用户对企业的评价从而在产品服务上做出改进等。在数据研究上,爬虫能快速收集互联网上的信息,为数据分析提供原始资料。
(二)爬虫算法流程
从功能上来讲,爬虫一般分为数据采集,处理,储存三个部分。传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。聚焦爬虫的工作流程较为复杂,需要根据一定的网页分析算法过滤与主题无关的链接,保留有用的链接将其放入等待抓取的URL队列。然后,它将根据一定的搜索策略从队列中选择下一步要抓取的网页URL,并重复上述过程,直到达到系统的某一条件时停止。另外,所有被爬虫抓取的网页将会被系统存贮,进行一定的分析、过滤,并建立索引,以便之后的查询和检索;对于聚焦爬虫来说,这一过程所得到的分析结果还可能对以后的抓取过程给出反馈和指导。

clipboard.png

                         图1:爬虫算法流程图

三、scrapy爬虫框架

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。

(一)scrapy整体架构图

clipboard.png

                                图2:scrapy架构图


(二)Scrapy主要组件
1、引擎(Scrapy): 用来处理整个系统的数据流处理, 触发事务(框架核心)。
2、调度器(Scheduler): 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址。
3、下载器(Downloader): 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)。
4、爬虫(Spiders): 爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面。
5、项目管道(Pipeline): 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
6、下载器中间件(Downloader Middlewares): 位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
7、爬虫中间件(Spider Middlewares): 介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
8、调度中间件(Scheduler Middewares): 介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

四、爬虫实战项目

(一)scrapy安装

Scrapy框架官方网址:http://doc.scrapy.org/en/latest
Scrapy中文维护站点:http://scrapy-chs.readthedocs...

1、Ubuntu下安装
sudo pip install scrapy

2、mac下安装
sudo pip install scrapy

3、Windows下安装

Windows下安装scrapy会出现各种依赖和不兼容,个人建议先安装anaconda,它是一个开源的、免费的python类库的集合,貌似一下就安装了200+的包,各种依赖包各种搞定,专治各种不服。anaconda下载链接。
安装命令: conda install scrapy

4、安装后,只要在命令终端输入 scrapy,提示类似以下结果,代表已经安装成功

clipboard.png

                            图3:scrapy安装成功


(二)新建scrapy项目(scrapy startproject)

在开始爬取之前,必须创建一个新的Scrapy项目。进入自定义的项目目录中,运行下列命令,命令执行完后程序目录结构大致如下:

scrapy startproject tutorial    #创建项目

clipboard.png

            图4:目录结构图

下面来简单介绍一下各个主要文件的作用:
scrapy.cfg:项目的配置文件
tutorial/:项目的Python模块,将会从这里引用代码
tutorial/items.py:项目的字段定义文件
tutorial/pipelines.py:项目的管道文件
tutorial/settings.py:项目的设置文件
tutorial/spiders/:存储爬虫代码目录

(三)定义存储对象(tutorial/items.py)

Items是装载我们抓取数据的容器。它们工作像简单的Python字典,它提供更多的保护,比如对未定义的字段提供填充功能防止出错.
Itmes.py
clipboard.png

(四)制作爬虫 (tutorial/spiders/)

1、在当前目录下输入命令,将在tutorial/spider目录下创建一个名为hsw的爬虫,并指定爬取域的范围:

`scrapy genspider hsw "hsw.cn"`   #创建爬虫

2、打开 tutorial/spider目录里的 hsw.py,默认增加了下列代码:

hsw.py
clipboard.png

3、要建立一个Spider, 你必须用scrapy.Spider类创建一个子类,并确定了三个强制的属性 和 一个方法。

(1) name ="":这个爬虫的识别名称,必须是唯一的,在不同的爬虫必须定义不同的名字。
(2) allow_domains=[]是搜索的域名范围,也就是爬虫的约束区域,规定爬虫只爬取这个域名下的网页,不存在的URL会被忽略。
(3) start_urls=():爬取的URL元祖/列表。爬虫从这里开始抓取数据,所以,第一次下载的数据将会从这些urls开始。其他子URL将会从这些起始URL中继承性生成。
(4) parse(self,
response):解析的方法,每个初始URL完成下载后将被调用,调用的时候传入从每一个URL传回的Response对象来作为唯一参数,主要作用如下:
a) 负责解析返回的网页数据(response.body),提取结构化数据(生成item) b) 生成需要下一页的URL请求。

4、修改parse方法,添加parse_item方法

现在我们修改hsw.py文件将start_urls的值修改为需要爬取的第一个url
start_urls = ["http://finance.hsw.cn/hyxw/index.shtml"]
这里先简单介绍下xpath语法(详见官方文档:http://www.w3school.com.cn/xm...

(1) //div[@class="listleft"]/ul/li/h3/a/@href
表示选择所有含有属性class等于listleft的dev元素下方的ul->li->h3下方a标签的herf属性,取列表页所有内容页url。
(2) //div[@class="hd"]/h1/text()
表示选择所有含有属性class等于hd的dev元素下方的h1标签,取文章标题。
(3) //div[@class="photoarea"]//p/text()|//div[@class="contentBox
cf"]//p/text() 同上,由于网页中存在多种不同的页面结构,需要兼容,故使用“|”语法,表示或的意思,取文章正文。
(4) //span[@class="article-time"]/text() 语法同上,取文章的发布时间。

clipboard.png

5、然后运行一下看看,在tutorial目录下执行:

scrapy crawl hws -o hws.csv

输出信息保存csv格式,用Excel打开数据如下图所示:

clipboard.png

                                    图6:Excel数据

scrapy保存信息的最简单的方法主要有四种,-o 输出指定格式的文件,命令如下:
json格式,默认为Unicode编码

scrapy crawl hws -o hws.json

json lines格式,默认为Unicode编码

scrapy crawl hws -o hws.jsonl

csv 逗号表达式,可用Excel打开

scrapy crawl hws -o hws.csv

xml格式

scrapy crawl hws -o hws.xml

五、scrapy-redis分布式

scrapy是python界出名的一个爬虫框架。Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。
虽然scrapy能做的事情很多,但是要做到大规模的分布式应用则捉襟见肘。有能人改变了scrapy的队列调度,将起始的网址从start_urls里分离出来,改为从redis读取,多个客户端可以同时读取同一个redis,从而实现了分布式的爬虫。

(一)scrapy-redis安装


安装:pip install scrapy-redis 官方站点:https://github.com/rolando/scrapy-redis

(二)scrapy-redis架构

clipboard.png

                    图7:scrapy-redis架构
                    
                   

(三)scrapy-Redis组件详解

如上图所示,scrapy-redis在scrapy的架构上增加了redis,基于redis的特性拓展了如下四种组件:Scheduler,Duplication Filter,Item Pipeline,Base Spider

1、Scheduler: scrapy改造了python本来的collection.deque(双向队列)形成了自己的Scrapy
queue,但是Scrapy多个spider不能共享待爬取队列Scrapy
queue,即Scrapy本身不支持爬虫分布式,scrapy-redis 的解决是把这个Scrapy
queue换成redis数据库(也是指redis队列),从同一个redis-server存放要爬取的request,便能让多个spider去同一个数据库里读取。Scrapy中跟“待爬队列”直接相关的就是调度器Scheduler,它负责对新的request进行入列操作(加入Scrapy
queue),取出下一个要爬取的request(从Scrapy
queue中取出)等操作。它把待爬队列按照优先级建立了一个字典结构,然后根据request中的优先级,来决定该入哪个队列,出列时则按优先级较小的优先出列。为了管理这个比较高级的队列字典,Scheduler需要提供一系列的方法。但是原来的Scheduler已经无法使用,所以使用Scrapy-redis的scheduler组件。

2、Duplication Filter

Scrapy中用集合实现这个request去重功能,Scrapy中把已经发送的request指纹放入到一个集合中,把下一个request的指纹拿到集合中比对,如果该指纹存在于集合中,说明这个request发送过了,如果没有则继续操作。这个核心的判重功能是这样实现的:

clipboard.png

在scrapy-redis中去重是由Duplication Filter组件来实现的,它通过redis的set不重复的特性,巧妙的实现了DuplicationFilter去重。scrapy-redis调度器从引擎接受request,将request的指纹存入redis的set检查是否重复,并将不重复的request push写入redis的 request queue。

引擎请求request(Spider发出的)时,调度器从redis的request queue队列里根据优先级pop 出⼀个request 返回给引擎,引擎将此request发给spider处理。

3、Item Pipeline:

引擎将(Spider返回的)爬取到的Item给Item Pipeline,scrapy-redis 的Item Pipeline将爬取到的
Item 存入redis的 items queue。修改过Item Pipeline可以很方便的根据 key 从 items queue
提取item,从而实现 items processes集群。

4、Base Spider

不在使用scrapy原有的Spider类,重写的RedisSpider继承了Spider和RedisMixin这两个类,RedisMixin是用来从redis读取url的类。

当我们生成一个Spider继承RedisSpider时,调用setup_redis函数,这个函数会去连接redis数据库,然后会设置signals(信号):一个是当spider空闲时候的signal,会调用spider_idle函数,这个函数调用schedule_next_request函数,保证spider是一直活着的状态,并且抛出DontCloseSpider异常。一个是当抓到一个item时的signal,会调用item_scraped函数,这个函数会调用schedule_next_request函数,获取下一个request。

5、 总结

总结一下scrapy-redis的总体思路:这套组件通过重写scheduler和spider类,实现了调度、spider启动和redis的交互;

实现新的dupefilter和queue类,达到了判重和调度容器和redis的交互,因为每个主机上的爬虫进程都访问同一个redis数据库,所以调度和判重都统一进行统一管理,达到了分布式爬虫的目的;当spider被初始化时,同时会初始化一个对应的scheduler对象,这个调度器对象通过读取settings,配置好自己的调度容器queue和判重工具dupefilter;

每当一个spider产出一个request的时候,scrapy引擎会把这个reuqest递交给这个spider对应的scheduler对象进行调度,scheduler对象通过访问redis对request进行判重,如果不重复就把他添加进redis中的调度器队列里。当调度条件满足时,scheduler对象就从redis的调度器队列中取出一个request发送给spider,让他爬取;

当spider爬取的所有暂时可用url之后,scheduler发现这个spider对应的redis的调度器队列空了,于是触发信号spider_idle,spider收到这个信号之后,直接连接redis读取strart_url池,拿去新的一批url入口,然后再次重复上边的工作。

(四)从零搭建scrapy-redis分布式爬虫

1、scrapy-Redis分布式策略:

Slaver端从Master端拿任务(Request/url/ID)进行数据抓取,在抓取数据的同时也生成新任务,并将任务抛给Master。Master端只有一个Redis数据库,负责对Slaver提交的任务进行去重、加入待爬队列。

优点:scrapy-redis默认使用的就是这种策略,我们实现起来很简单,因为任务调度等工作scrapy-redis都已经帮我们做好了,我们只需要继承RedisSpider、指定redis_key就行了。

缺点:scrapy-redis调度的任务是Request对象,里面信息量比较大(不仅包含url,还有callback函数、headers等信息),导致的结果就是会降低爬虫速度、而且会占用Redis大量的存储空间。当然我们可以重写方法实现调度url。

clipboard.png

                        图6:scrapy-redis分布式策略
                        

2、安装Redis

下载redis:http://redis.io/download

安装完成后,拷贝一份Redis安装目录下的redis.conf到任意目录,建议保存到:/etc/redis/redis.conf 打开你的redis.conf配置文件,示例: 非Windows系统: sudo vim /etc/redis/redis.conf Master端redis.conf里注释bind 127.0.0.1,Slave端才能远程连接到Master端的Redis数据库。

3、创建项目

使用上面的scrapy的项目我们来修改一下,这个爬虫继承了RedisSpider,它能够支持分布式的抓取,采用的是basic spider,需要写parse函数。其次就是不再有start_urls了,取而代之的是redis_key,scrapy-redis将key从Redis里pop出来,成为请求的url地址。
修改spiders/hsw.py

clipboard.png

修改settings.py

clipboard.png

4、执行程序

通过runspider方法执行爬虫的py文件(也可以分次执行多条),爬虫(们)将处于等待准备状态:

scrapy runspider hsw.py

在Master端的redis-cli输入push指令,参考格式:

$redis > lpush myspider:start_urls http://finance.hsw.cn/hyxw/

clipboard.png

5、获取数据

所有Slaver端将开始爬取数据,数据将保存在Redis数据库中,并共享Redis数据库的请求队列、请求指纹集合和数据队列。

clipboard.png


白色肆意
52 声望4 粉丝

关注白色肆意,一览微博精华.安徽安庆