Scrapy框架全

架构介绍

Engine:引擎,处理整个系统的数据流处理,触发事物,是整个框架的核心

Item:项目,他定义了爬取结果的数据结构,爬取的数据会被赋值成该Item对象

Scheduler:调度器,接受引擎发过来的请求并将其加入到队列中,在引擎再次请求的时候将请求提供给引擎

Downloader:下载器,下载网页内容,并将网页内容返回给spider

Spider:蜘蛛,其内部定义了爬取的逻辑和网页的解析规则,他负责解析响应并生成提取结果和新的请求

Item Pipline:项目管道,负责处理由蜘蛛从网页中抽取的项目,他的主要任务是清洗,验证和存储数据

Downloader Middlewares:下载器中间件,位于引擎和下载器之间钩子框架,主要处理引擎和下载器之间的请求和响应

Spider Middlewares:蜘蛛中间件,位于引擎和蜘蛛之间的钩子框架,主要处理蜘蛛输入的响应和输出的结果及新的请求

数据流

Scrapy中的数据流由引擎控制,数据流的过程如下:

1.Engine首先打开一个网站,找到处理该网站的Spider,并向该Spider请求第一个要爬取的URL.

2.Engine从Spider中获取到第一个要爬取的URL,并通过Scheduler以Request的形式调度.

3.Engine向Scheduler请求下一个要爬取的URL

4.Scheduler返回下一个要爬取的URL给Engine,Engine将URL通过Downloader Middlewares转发给Downloader下载

5.一旦页面下载完毕,Downloader生成该页面的Response,并将其通过Downloader Middlewares发送给Engine

6.Engine从下载器中接收到Response,并将其通过Spider Middlewares发送给 Spider处理

7.Spider处理Response,并返回爬取到的Item及新的Request给Engine

8.Engine将Spider返回的Item给Item Pipline,将新的Request给Scheduler

9.重复2-8,知道Scheduler中没有更多的Request,Engine关闭该网站,爬取结束

通过多个组件的相互协作,不同组件完成工作的不同,组件对异步处理的支持,Scrapy最大限度的利用了网络带宽,大大提高了数据爬取和处理的效率

 雀氏纸尿裤

创建项目:

scrapy startproject 项目名(可以在几乎任意文件夹下创建,创建的项目就在该文件夹下)在命令行中执行

2:创建Spider:

Spider是自己定义的类,Scrapy用它从网页中抓取内容,并解析抓取的结果.不过这个类必须继承Scrapy提供的Spider类scrapy.Spider,还要定义Spider的名称和起始请求,以及怎样处理爬取结果的方法:(不如命令行方便)

也可以使用命令行创建一个Spider,比如要生成sb这个spider,(示例爬取校花网图片)

执行如下命令:

cd 项目名

scrapy genspider sb news.daxues.cn

进入刚才创建的项目文件夹,然后执行上诉命令,第一个参数是Spider的名称,第二个参数是网站域名,执行完毕之后,spiders文件夹中就多了一个sb.py,他就是刚刚创建的Spider,内容如下:

import scrapy

class SbSpider(scrapy.Spider):
name = 'sb'
allowed_domains = ['news.daxues.cn']
start_urls = ['http://news.daxues.cn/xiaohua/ziliao/'](这里有所改动xiaohua/ziliao/为后来添加的方便爬取)
def parse(self, response):
pass

这里有三个属性--name,allowed_domains和start_urls,还有一个parse方法

name:他是项目中唯一的名字,用于区分不同Spider

allowed_domains,他是允许爬取的域名,如果初始或后续的请求链接不是这个域名下的,则请求链接会被过滤掉

start_urls:它包含了Spider在启动时爬取的url列表,初始请求是由他定义的

parse:他是Spider的一个方法,默认情况下,被调用时start_url里面的链接构成的请求完成下载执行后,返回的响应就会作为唯一的参数传递给这个函数.该方法负责解析返回的响应,提取数据或者进一步生成要处理的请求.

 3.创建Item

Item是保存爬取数据的容器,他的使用方法和字典类似,创建Item需要继承scrapy.Item类,并且定义类型为scrapy.Field的字段

定义Item,此时将items.py修改如下:

class NewsItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
name = scrapy.Field()
info = scrapy.Field()
img = scrapy.Field()

这里定义了三个字段,接下来的爬取将会使用这个Item

4.解析Response

parse()方法的参数response是start_urls里面的链接爬取后的结果,所以在parse()方法中.我们可以直接对response变量包含的内容进行解析,比如浏览请求结果的源代码,或者进一步分析源代码内容,或找出结果中的链接,得到下一个请求

有时网页中既有我们想要的结果又有下一页的链接,都需要进行处理.:

可以在浏览器上查看网页结构:

在类名为xh_list的div下有多个dl标签,里面包含我们想要的数据

提取内容的方式可以是CSS选择器或Xpath选择器,这里使用CSS选择器进行选择

parse()方法改写如下:

class SbSpider(scrapy.Spider):
name = 'sb'
allowed_domains = ['news.daxues.cn']
start_urls = ['http://news.daxues.cn/xiaohua/ziliao/']
def parse(self, response):
div_list = response.css('.xh_list').css('dl')
url = 'https://news.daxues.cn'
for div in div_list:
name = div.css('dt').css('a::text').extract_first()
info = div.css('.d::text').extract_first()
img = url+div.css('img::src').extract_first()

5.使用Item

上文定义Item,接下来就要使用它.Item可以理解为一个字典,不过在声明的时候需要实例化,然后依次用刚才解析的结果赋值Item的每一个字段,最后将Item返回即可

SbSpider的改写如下所示:

import scrapy

from news.items import NewsItem
class SbSpider(scrapy.Spider):
name = 'sb'
allowed_domains = ['news.daxues.cn']
start_urls = ['http://news.daxues.cn/xiaohua/ziliao/']
def parse(self, response):
div_list = response.css('.xh_list').css('dl')
url = 'https://news.daxues.cn'
for div in div_list:
item = NewsItem()
item['name'] = div.css('dt').css('a::text').extract_first()
item['info'] = div.css('.d::text').extract_first()
item['img'] = url+div.css('img::attr(src)').extract_first()
yield item

如此一来,首页的所有内容被解析出来,并被赋值成了一个个NewsItem

6.后续Request

上面的操作只实现了从初始页面抓取内容,那么下一页的内容该如何抓取,这需要我们从当前页面中找到信息来生成下一个请求,然后在下一个请求的页面里找到信息再构造再下一个请求,这样循环往复迭代,从而实现整站的爬取.

将页面拉到底部,这里有一个下一页按钮,查看他的源代码发现,他的链接是/xiaohua/ziliao/index_2.html,全链接就是:

http://news.daxues.cn/xiaohua/ziliao/index_2.html

通过这个链接我们就可以构造下一个请求.

构造请求时需要用到scrapy.Request.这里我们传入两个参数---url和callback,这两个参数的说明如下.

url:他是请求链接

callback:它是回调函数,当指定了该回调函数的请求完成之后,获取到响应,引擎会将该响应作为参数传递给这个回调函数.回调函数进行解析或生成下一个请求,回调函数如上文的parse()所示:

由于parse()就是解析name,info,img的方法,而下一页的结构和刚才已经解析的页面结构是一样的,所以我们可以再次使用parse(0方法来做页面解析.

接下来就是利用选择器得到下一页链接并生成请求,在parse()方法后追加如下的代码:

next = response.css('.page a::attr(href)').extract_first()(选择器语法可能不正确,后续需要改动)
url2 = url+next
yield scrapy.Request(url=url2,callback=self.parse)

第一句代码首先通过CSS选择器获取下一个链接,,即要获取a超链接中的href属性.这里用到了::attr(href)操作.然后再调用extract_first()方法获取内容

第二句代码生成一个url路径

第三句代码通过url和callback变量构造了一个新的请求,回调函数callback依然使用parse()方法,这个请求完成后,响应会重新经过parse方法处理,得到第二页的解析结果,然后生成第二页的下一页,也就是第三页的请求,这样爬虫就进入了一个循环,直到最后一页.

改写后的整个Spider类如下所示:

import scrapy

from news.items import NewsItem
class SbSpider(scrapy.Spider):
name = 'sb'
allowed_domains = ['news.daxues.cn']
start_urls = ['http://news.daxues.cn/xiaohua/ziliao/']
def parse(self, response):
div_list = response.css('.xh_list').css('dl')
url = 'https://news.daxues.cn'
for div in div_list:
item = NewsItem()
item['name'] = div.css('dt').css('a::text').extract_first()
item['info'] = div.css('.d::text').extract_first()
item['img'] = url+div.css('img::attr(src)').extract_first()
yield item
next = response.css('.page a::attr(href)').extract_first()
url2 = url+next
yield scrapy.Request(url=url2,callback=self.parse)

7运行:

接下来进入目录,运行如下命令:

scrapy crawl news

就可以看到运行结果了

首先Scrapy输出了当前的版本号以及正在启动的项目名称,接着输出了当前settings.py中一些重写后的配置,然后输出了当前所应用的Middlewares和Piplines.Middlewares默认是启用的,可以在settings中修改,Piplines默认为空,同样可以在settings.py中配置.

接下来就是整个页面的抓取结果,可以看到爬虫一边解析,一边翻页,直至将所有内容抓取完毕,然后终止.

最后Scrapy输出了整个抓取过程的统计信息,如请求的字节数,请求次数,响应次数,完成原因等等

整个Scrapy程序成功运行.

由于本人对于Scrapy的CSS选择器不熟悉,因此除了点bug,但问题不大

8保存到文件

Scrapy提供的Feed Exports可以轻松的抓取结果输出

如果想要将输出结果保存成json文件,可以执行以下命令:

scrapy crawl sb -o sb.jl

scrapy crawl sb -o sb.jsonlines

输出格式还支持很多种,例如csv,xml,pickle,marshal等,还支持ftp,s3等远程输出,另外还可以通过自定义ItemExporter来实现其他的输出.

例如下面的命令对应的输出分别为csv,xml,,pickle,marshal格式以及ftp远程输出:

scrapy crawl sb -o sb.csv

scrapy crawl sb -o sb.xml

scrapy crawl sb -o sb.pickle

scrapy crawl sb -o sb.marshal

scrapy crawl sb -o ftp://user:pass@ftp.example.com/path/to/sb.csv

其中,ftp输出需要正确配置用户名,密码,地址,输出路径,否则会报错

通过Scrapy提供的Feed Exports我们可以轻松的输出抓取结果到文件,如果想要更复杂的输出,我们可以使用Item Pipline来完成

9使用Item Pipeline 

如果想进行更复杂的操作,如将结果保存到MongoDB数据库,或者筛选某些有用的Item,我们可以定义Item Pipeline来实现

Item Pipline为项目管道,当Item生成后,他会自动被送到Item Pipline进行处理,我们常用Item Pipline来做如下操作:

清理HTML数据

验证爬取数据,检查爬取字段

查重并丢弃重复内容

将爬取结果保存到数据库.

要实现Item Pipeline很简单只需要定义一个类并实现process_item()方法即可,启用Item Pipeline后,Item Pipeline会自动调用这个方法,process_item()方法必须返回包含数据的字典或Item对象,或者抛出DropItem异常.

process_item()方法有两个参数,一个参数是item,每次Spider生成的Item都会作为参数传递过来,另一个参数是spider,就是Spider的实例.

接下来实现一个Item Pipeline,筛掉info长度大于200的Item,并将结果保存到MongoDB.

修改项目里的pipelines.py文件,之前用命令行自动生成的文件内容可以删掉,增加一个TextPipeline类,内容如下所示:

from scrapy.exceptions import DropItem
class InfoPipeline():
def __init__(self):
self.limit = 200
def process_item(self,item,spider):
if item['info']:
if len(item['info']) > self.limit:
item['info'] = item['info'][0:self.limit].rstrip()+'...'
return item
else:
return DropItem('Missing Info')

这段代码在构造方法里定义了限制长度为200,实现了process_item()方法,其参数是item和spider,首先该方法判断item的info属性是否存在,不存在则抛出DropItem异常,存在则判断是否大于200,大于则删减,然后返回item

接下来将处理后的item存入MongoDB,定义另外一个Pipeline,同样在pipelines.py中,我们实现零一个类MongoPipeline,内容如下所示:

import pymongo
class MongoPipeline():
def __init__(self,mongo_uri,mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls,crawler):
return cls(mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DB'))
def open_spider(self,spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def process_item(self,item,spider):
name = item.__class__.__name__
self.db[name].insert(dict(item))
return item
def close_spider(self,spider):
self.client.close()

MongoPipeline类实现了API定义的另外几个方法.

from_crawler:他是一个类方法,用@classmethod标识,是一种依赖注入的方式,他的参数就是crawler,通过crawler我们可以拿到全局配置的每个配置信息,在全局配置settings.py中,我们定义MONGO_URI和MONGO_DB来指定MongoDB连接需要的地址和数据库名称,拿到配置信息后返回类对象即可,所以这个方法主要用于获取settings.py中的配置的

open_spider:当Spider开启时,这个方法被调用,上文程序主要进行了一些初始化操作

close_spider:当Spider关闭时,这个方法被调用,上文程序中将数据库连接关闭

最主要的process_item()方法则执行了数据插入操作

定义好InfoPipeline和MongoPipeline这两个类后,我们需要在settings.py中使用他们,MongoDB的连接信息还需要定义.

我们在settings.py中加入如下内容:

ITEM_PIPELINES={'sb.pipelines.InfoPipeline':300,

'sb.pipelines.MongoPipeline':400,}

MONGO_URI = 'localhost'

MONGO_DB = 'sb'

赋值ITEM_PIPELINES字典,键名是Pipeline的类名称,键值是调用优先级,是一个数字,数字越小则对应的Pipeline越先被调用.

再重新爬取,命令如下所示:

scrapy crawl sb

爬取结束后,MongoDB中创建了一个news的数据库,sbitem的表,

原文地址:https://www.cnblogs.com/suncunxu/p/10764365.html