简介
中间件是Scrapy里面的一个核心概念。使用中间件可以在爬虫的请求发起之前或者请求返回之后对数据进行定制化修改,从而开发出适应不同情况的爬虫。
在Scrapy中有两种中间件:下载器中间件(Downloader Middleware)和爬虫中间件(Spider Middleware)。
安装
上一章已经安装过了,所以这一步可以跳过
知识
爬虫中间件与下载中间件
在项目工程中,有一个middlewares.py文件,如下:
关于每个函数调用的时刻,可以通过调试看日志输出,会更清晰
class JobspiderSpiderMiddleware(object):
@classmethod
def from_crawler(cls, crawler):
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
print("类名:JobspiderSpiderMiddleware 方法:from_crawler 触发条件(调用时间):创建爬虫对象时调用")
return s
def process_spider_input(self, response, spider):
print("类名:JobspiderSpiderMiddleware 方法:process_spider_input 触发条件(调用时间):应答输入时调用")
return None
def process_spider_output(self, response, result, spider):
print("类名:JobspiderSpiderMiddleware 方法:process_spider_output 触发条件(调用时间):应答输出时调用")
for i in result:
yield i
def process_spider_exception(self, response, exception, spider):
print("类名:JobspiderSpiderMiddleware 方法:process_spider_exception 触发条件(调用时间):解析或处理输入的时候代码异常调用")
pass
def process_start_requests(self, start_requests, spider):
print("类名:JobspiderSpiderMiddleware 方法:process_start_requests 触发条件(调用时间):爬虫发起请求时调用")
for r in start_requests:
yield r
def spider_opened(self, spider):
print("类名:JobspiderSpiderMiddleware 方法:spider_opened 触发条件(调用时间):爬虫打开时调用")
spider.logger.info('Spider opened: %s' % spider.name)
lass JobspiderDownloaderMiddleware(object):
@classmethod
def from_crawler(cls, crawler):
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
print("类名:JobspiderDownloaderMiddleware 方法:from_crawler 触发条件(调用时间):创建爬虫对象时调用")
return s
def process_request(self, request, spider):
print("类名:JobspiderDownloaderMiddleware 方法:process_request 触发条件(调用时间):请求通过下载器时调用 在下载之前连接Internet网页信息之前调用 ")
return None
def process_response(self, request, response, spider):
print("类名:JobspiderDownloaderMiddleware 方法:process_response 触发条件(调用时间):下载器返回应答时调用")
return response
def process_exception(self, request, exception, spider):
print("类名:JobspiderDownloaderMiddleware 方法:process_exception 触发条件(调用时间):请求异常时调用")
pass
def spider_opened(self, spider):
print("类名:JobspiderDownloaderMiddleware 方法:spider_opened 触发条件(调用时间):爬虫打开时时调用")
spider.logger.info('Spider opened: %s' % spider.name)
在中间件中,可以实现换ip,处理ua,cookie等反爬措施,一般会放在process_request中处理。如果需要自定义的话,也很简单,只要保证函数名一直,然后添加到配置文件中启动就行了。比如:
class MyDownloaderMiddleware(object):
def process_request(self, request, spider):
print("自定义中间件 process_request 方法调用")
然后在setting中,打开即可
# Enable or disable spider middlewares
# See https://doc.scrapy.org/en/latest/topics/spider-middleware.html
SPIDER_MIDDLEWARES = {
'JobSpider.middlewares.JobspiderSpiderMiddleware': 543,
}
# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
'JobSpider.middlewares.MyDownloaderMiddleware': 400,
'JobSpider.middlewares.JobspiderDownloaderMiddleware': 543,
}
利用中间修改请求头
只需要修改下载中间件中的process_requests方法即可,参数request是整个框架传递的请求,你可以在原来的请求基础之上,进行请求头修改
def process_request(self, request, spider):
print("类名:JobspiderDownloaderMiddleware 方法:process_request 触发条件(调用时间):请求通过下载器时调用 在下载之前连接Internet网页信息之前调用")
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"
}
request.headers = Headers(headers)
return None
各个模块详细的说明
item对象
用于定义数据字段,用来处理爬虫模块与管道模块的数据传输
spider爬虫对象
yield item 用来传递信息对象,传给pipeline管道
yield request 生成新的请求,传给调度器进行新的url处理
request对象
Scrapy使用request对象来爬取web站点
request对象:
scrapy.http.Request(url[,callback,method=‘GET’,headers,body,cookies,meta,encoding=‘utf-8’,priority=0,dont_filter=False,errback])
一个request对象代表一个HTTP请求,通常有Spider产生,经Downloader执行从而产生一个Response。
参数:
url: 用于请求的URL
callback:指定一个回调函数,该回调函数以这个request对应的response作为第一个参数。如果未指定callback,则默认使用spider的parse()方法。
method:HTTP请求的方法,默认为GET
meta:指定Request.meta属性的初始值,字典类型。可以在回调函数们之间传递参数
body:请求体
headers:request的头信息
cookies:cookie
encoding:请求的编码,默认为utf-8
priority:请求的优先级
dont_filter(boolean):指定该请求是否被Scheduler过滤。该参数可以是request重复使用(Scheduler默认过滤重复请求)。谨慎使用!!errback:处理异常的回调函数。
Response应答对象
一个Response对象表示的HTTP响应,这通常由下载器提供给到爬虫进行处理
常见属性
url包含响应的URL的字符串。
status表示响应的HTTP状态的整数。示例:200,404。
headers包含响应标题的类字典对象。
body正文。
meta获得Request.meta从您的爬虫发送的原始属性。
使用ImagesPipeline下载图片
ImagesPipeline简介
Scrapy用ImagesPipeline类提供一种方便的方式来下载和存储图片。
可以将下载图片转换成通用的JPG和RGB格式
ImagesPipeline重载的方法
需要在自定义的ImagePipeline类中重载的方法:
方法一:get_media_requests(self,item, info):
Pipeline将从item中获取图片的URLs并下载它们,并返回一个Request对象所以必须重载get_media_requests
方法二:item_completed(self, results, item, info):
当完成下载后,结果将以元组形式发送到item_completed方法,图片下载完毕后,处理结果会以元组的方式返回给item_completed()函数。这个元组定义如下:
(success, image_info_or_failure)
其中,第一个元素表示图片是否下载成功;第二个元素是一个字典。
如果success=true,表示成功下载,image_info_or_error词典包含以下键值对:url:原始URL
,path:本地存储路径,checksum:校验码。
如果success=false,表示下载失败,image_info_or_error则包含一些出错信息。
方法三:file_path(self, request, response=None, info=None):
返回图片存储路径
爬取斗鱼直播图片
创建scrapy项目
scrapy startproject douyuspider
编写items.py
class DouyuItem(scrapy.Item):
# 昵称 为后边修改文件名准备
nickname = scrapy.Field()
# 图片链接地址 这个参数名如果不修改源文件的话 必须定义为image_urls
image_urls = scrapy.Field()
创建爬虫模板
cd douyuspider
scrapy genspider douyu http://capi.douyucdn.cn
编写爬虫文件
import scrapy
import json
from douyu.items import DouyuItem
class DouspiderSpider(scrapy.Spider):
name = 'douspider'
start_urls = ["http://capi.douyucdn.cn/api/v1/getVerticalRoom?limit=20&offset="]
def parse(self, response):
jsonInfo = json.loads(response.text)
dataList = jsonInfo["data"]
for data in dataList:
item = DouyuItem()
item["nickname"] =data["nickname"]
item["image_urls"] = data["vertical_src"]
yield item
编写管道文件
使用request中的meta属性,传递参数,用来当做存储路径。
class DouyuPipeline(ImagesPipeline):
# 传递url下载的
def get_media_requests(self, item, info):
meta = {"item":item }
yield Request(url=item["image_urls"],meta=meta)
# 返回下载结果信息
def item_completed(self, results, item, info):
print(results)
print(type(results))
# 修改文件名
def file_path(self, request, response=None, info=None):
nickname = request.meta["item"]["nickname"]
return nickname + ".jpg"
修改设置文件
ITEM_PIPELINES = {'douyuSpider.pipelines.DownloadimgPipeline': 1}# Images的存放位置,之后会在pipelines.py里调用
# 设置图片存储路径
IMAGES_STORE = './Image'
测试
from scrapy import cmdline
name = "douspider"
cmd = "scrapy crawl {0}".format(name)
cmdline.execute(cmd.split())
任务
爬取表情包 斗图啦前10页图片
http://www.doutula.com/