Scrapy进阶知识点总结(六)——中间件详解
概述
查看scrapy官网的框架图,可以看出中间件处于几大主要组件之间,类似于生产流水线上的加工过程,将原料按照不同需求与功能加工成成品
其中4,5处于下载器与引擎之间的就是下载中间件,而spider与引擎之间的就是spider中间件。目前scrapy主要的中间件就这两个
下载中间件
下载器中间件是介于Scrapy的request/response处理的钩子框架,是用于全局修改Scrapy request和response的一个轻量、底层的系统。
主要作用:
- 在Scrapy将请求发送到网站之前修改,处理请求,如:更换代理ip,header等
- 在将响应传递给引擎之前处理收到的响应,如:响应失败重新请求,或将失败的做一定处理再返回给引擎
- 忽略一些响应或者请求
默认下载中间件
scrapy内置了一些默认配置,这些是不允许被修改的,通常是_BASE结尾的设置,比如DOWNLOADER_MIDDLEWARES_BASE下载中间件的默认设置,如下
{
'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560,
'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
}
scrapy就是按照上面数字从小到大依次执行的,比如执行完RobotsTxtMiddleware的process_request()方法后会继续执行下面HttpAuthMiddleware等process_request(),可以看作串联的形式依次过流水线
如果我们要添加自定义的下载中间件,需要在settings.py中激活DOWNLOADER_MIDDLEWARES。同时想取消默认的一些中间件,也可以设置为None。注意的是激活DOWNLOADER_MIDDLEWARES并不会覆盖DOWNLOADER_MIDDLEWARES_BASE,而是继续串联起来
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.CustomDownloaderMiddleware': 543,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
}
自定义下载中间件
在创建项目后,再项目文件夹中有一middlewares.py文件,里面自动生成了两个中间件示例或者说模板。我们如果要自定义中间件的话,可以在给的示例上修改,或者新建类实现方法,或者继承已有的中间件重写方法
以下是下载中间件可以实现的方法,在自定义中间件时,可以根据需求实现
1.process_request(self, request, spider)
- 如果其返回 None ,会将处理过后的request丢给中间件链中的下一个中间件的process_request()方法处理,直到丢到下载器,由下载器下载
- 如果其返回 Response 对象,Scrapy将不会调用任何其他的 process_request() 或 process_exception() 方法,也不会丢到下载器下载;直接将其返回的response丢到中间件链的process_response()处理。可以通过scrapy.http.Response构建Response
- 如果其返回 Request 对象,Scrapy则停止调用process_request方法并重新调度返回的request。当新返回的request被执行后, 相应地中间件链将会根据下载的response被调用。
- 如果其raise一个 IgnoreRequest 异常,则安装的下载中间件的 process_exception() 方法会被调用。如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)。
参数:
request(Request 对象)–处理的request
spider(Spider 对象)–该request对应的spider
- 如果其返回一个 Response (可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的 process_response() 方法处理。
- 如果其返回一个 Request 对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样。
- 如果其抛出一个 IgnoreRequest 异常,则调用request的errback(Request.errback)。 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。
参数:
request (Request 对象) – response所对应的request
response (Response 对象) – 被处理的response
spider (Spider 对象) – response所对应的spider
- 如果其返回 None ,Scrapy将会继续处理该异常,接着调用已安装的其他中间件的 process_exception() 方法,直到所有中间件都被调用完毕,则调用默认的异常处理。
- 如果其返回一个 Response 对象,则已安装的中间件链的 process_response() 方法被调用。Scrapy将不会调用任何其他中间件的 process_exception() 方法。
- 如果其返回一个 Request 对象, 则返回的request将会被重新调用下载。这将停止中间件的 process_exception() 方法执行,就如返回一个response的那样。
参数:
request (是 Request 对象) – 产生异常的request
exception (Exception 对象) – 抛出的异常
spider (Spider 对象) – request对应的spider
参数:
crawler(Crawlerobject)- 使用此中间件的爬网程序
设置随机UA中间件
user_agent_list = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
"(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "
"(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5"
] class UserAgent_Middleware():
def process_request(self, request, spider):
ua = random.choice(user_agent_list)
request.headers['User-Agent'] = ua
或者使用faker大魔王
from faker import Faker class UserAgent_Middleware(): def process_request(self, request, spider):
f = Faker()
ua = f.firefox()
request.headers['User-Agent'] = ua
设置代理中间件
proxy_list=[
"http://180.76.154.5:8888",
"http://14.109.107.1:8998",
"http://106.46.136.159:808",
"http://175.155.24.107:808",
"http://124.88.67.10:80",
"http://124.88.67.14:80",
"http://58.23.122.79:8118",
"http://123.157.146.116:8123",
"http://124.88.67.21:843",
"http://106.46.136.226:808",
"http://101.81.120.58:8118",
"http://180.175.145.148:808"]
class proxy_Middleware(object): def process_request(self,request,spider):
proxy = random.choice(proxy_list)
request.meta['proxy'] = proxy
至于代理池,可以自己爬取,或者github上查找,或者编写一套可替换可检查可用性的代理池脚本存在文件或者数据库中
集成selenium
from selenium import webdriver
from scrapy.http import HtmlResponse
import time class SeleniumMiddleware(object):
def __init__(self):
self.driver = webdriver.Chrome() def process_request(self, request, spider):
self.driver.get(request.url)
time.sleep(2)
body = self.driver.page_source
return HtmlResponse(self.driver.current_url,
body=body,
encoding='utf-8',
request=request)
当然不是每一个spider都要用selenium,那样会很慢,可以在spider里的custom_settings单独激活这个中间件,selenium的用法会在其他文章讲述
重试中间件
from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message class CustomRetryMiddleware(RetryMiddleware): def process_response(self, request, response, spider): if request.meta.get('dont_retry', False):
return response
if response.status in self.retry_http_codes:
reason = response_status_message(response.status)
#如果返回了[500, 502, 503, 504, 522, 524, 408]这些code,换个proxy试试
proxy = random.choice(proxy_list)
request.meta['proxy'] = proxy
return self._retry(request, reason, spider) or response return response #RetryMiddleware类里有个常量,记录了连接超时那些异常
#EXCEPTIONS_TO_RETRY = (defer.TimeoutError, TimeoutError, DNSLookupError,
# ConnectionRefusedError, ConnectionDone, ConnectError,
# ConnectionLost, TCPTimedOutError, ResponseFailed,
# IOError, TunnelError)
def process_exception(self, request, exception, spider):
if isinstance(exception, self.EXCEPTIONS_TO_RETRY) and not request.meta.get('dont_retry', False):
#这里可以写出现异常那些你的处理
proxy = random.choice(proxy_list)
request.meta['proxy'] = proxy
time.sleep(random.randint(3, 5))
return self._retry(request, exception, spider)
#_retry是RetryMiddleware中的一个私有方法,主要作用是
#1.对request.meta中的retry_time进行+1
#2.将retry_times和max_retry_time进行比较,如果前者小于等于后者,利用copy方法在原来的request上复制一个新request,并更新其retry_times,并将dont_filter设为True来防止因url重复而被过滤。
#3.记录重试reason
spider中间件
spider中间件用于处理引擎传回的response及spider生成的item和Request
主要作用:
- 处理spider的异常
- 对item在进入管道之前操作
- 根据引擎传入的响应,再进入回调函数前先处理
默认spider中间件
SPIDER_MIDDLEWARES_BASE
{
'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware': 50,
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': 500,
'scrapy.spidermiddlewares.referer.RefererMiddleware': 700,
'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware': 800,
'scrapy.spidermiddlewares.depth.DepthMiddleware': 900,
}
同理,激活中间件
SPIDER_MIDDLEWARES = {
'myproject.middlewares.CustomSpiderMiddleware': 543,
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None,
}
自定义spider中间件
1.process_spider_input(self, response, spider)
对于通过spider中间件并进入spider的每个响应,都会调用此方法进行处理。
process_spider_input()应该返回None或提出异常。
- 如果它返回None,Scrapy将继续处理此响应,执行所有其他中间件,直到最后,响应被交给spider进行处理。
- 如果它引发异常,Scrapy将不会调用任何其他spider中间件的process_spider_input(),将调用请求errback(如果有的话),否则它将进入process_spider_exception()链
参数:
response(Responseobject) - 正在处理的响应
spider(Spiderobject) - 此响应所针对的spider
2.process_spider_output(self, response, result, spider)
在处理完响应之后,使用Spider返回的结果调用此方法。
process_spider_output()必须返回一个可迭代的 Request,dict或Item 对象。
参数:
response(Responseobject) - 从spider生成此输出的响应
result(可迭代的Request,dict或Item对象) - spider返回的结果
spider(Spiderobject) - 正在处理其结果的spider
3.process_spider_exception(self, response, exception, spider)
当spider或process_spider_output() 方法(来自先前的spider中间件)引发异常时,将调用此方法。
process_spider_exception()应该返回一个None或一个可迭代的Request,dict或 Item对象。
- 如果它返回None,Scrapy将继续处理此异常,执行process_spider_exception()以下中间件组件中的任何其他组件,直到没有剩余中间件组件并且异常到达引擎(它被记录并丢弃)。
- 如果它返回一个iterable,那么process_spider_output()管道将从下一个spider中间件开始启动,并且不会process_spider_exception()调用其他任何一个 。
参数:
response(Responseobject) - 引发异常时正在处理的响应
exception(异常对象) - 引发异常
spider(Spiderobject) - 引发异常的spider
4.process_start_requests(self, start_requests, spider)
当spider运行到start_requests()的时候,爬虫中间件的process_start_requests()方法被调用
它接收一个iterable(在start_requests参数中)并且必须返回另一个可迭代的Request对象。
参数:
start_requests(可迭代Request) - 开始请求
spider(Spiderobject) - 启动请求所属的spider
5.from_crawler(cls, crawler)
这个类方法通常是访问settings和signals的入口函数
spider中间件总结
1.spider开始start_requests()的时候,spider中间件的process_start_requests()方法被调用
2.下载response成功后,返回到spider 回调函数parse前,调用process_spider_input()
3.当spider yield scrapy.Request()或者yield item的时候,spider中间件的process_spider_output()方法被调用。
4.当spider出现了Exception的时候,spider中间件的process_spider_exception()方法被调用。
中间件与spider的关系流程图
Scrapy进阶知识点总结(六)——中间件详解的更多相关文章
- JavaScript进阶知识点——函数和对象详解
JavaScript进阶知识点--函数和对象详解 我们在上期内容中学习了JavaScript的基本知识点,今天让我们更加深入地了解JavaScript JavaScript函数 JavaScript函 ...
- CSS进阶内容—浮动和定位详解
CSS进阶内容-浮动和定位详解 我们在学习了CSS的基本知识和盒子之后,就该了解一下网页的整体构成了 当然如果没有学习之前的知识,可以到我的主页中查看之前的文章:秋落雨微凉 - 博客园 CSS的三种布 ...
- net core 中间件详解及项目实战
net core 中间件详解及项目实战 前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际使用的,比较贴合实际应用,算是对中间件的一个深入使用了,不是简单的H ...
- Django 2.0 学习(20):Django 中间件详解
Django 中间件详解 Django中间件 在Django中,中间件(middleware)其实就是一个类,在请求到来和结束后,Django会根据自己的规则在合适的时机执行中间件中相应的方法. 1. ...
- 转 Scrapy笔记(5)- Item详解
Item是保存结构数据的地方,Scrapy可以将解析结果以字典形式返回,但是Python中字典缺少结构,在大型爬虫系统中很不方便. Item提供了类字典的API,并且可以很方便的声明字段,很多Scra ...
- Django中间件详解
Django中间件详解 中间件位置 WSGI 主要负责的就是负责和浏览器和应用之家沟通的桥梁 浏览器发送过来一个http请求,WSGI负责解包,并封装成能够给APP使用的environ,当app数据返 ...
- 我的Android进阶之旅------>HTTP Header 详解
HTTP(HyperTextTransferProtocol)即超文本传输协议,目前网页传输的的通用协议.HTTP协议采用了请求/响应模型,浏览器或其他客户端发出请求,服务器给与响应.就整个网络资源传 ...
- MongoDb进阶实践之三 Mongodb基本命令详解
一.引言 从今天开始,我要正式开始介绍MongoDB的使用方法了.在此之前,我用了两篇文章分别介绍了如何在Linux系统和Windows系统上安装和配置MongoDB系统.如 ...
- Android进阶(十四)Android Adapter详解
Android Adapter详解 Android是完全遵循MVC模式设计的框架,Activity是Controller,layout是View.因为layout五花八门,很多数据都不能直接绑定上去, ...
- 网站开发进阶(十三)window.onload用法详解
window.onload用法详解 网页中的javaScript脚本代码往往需要在文档加载完成后才能够去执行,否则可能导致无法获取对象的情况,为了避免这种情况的发生,可以使用以下两种方式: 一.将脚本 ...
随机推荐
- centos8安装图解
CentOS 8 的新特性 DNF 成为了默认的软件包管理器,同时 yum 仍然是可用的 使用网络管理器(nmcli 和 nmtui)进行网络配置,移除了网络脚本 使用 Podman 进行容器管理 引 ...
- MySQL学习(一)索引的基本认识
MySQL中,所有的数据类型都可以被索引,包括普通索引,唯一性索引,全文索引,单列索引,多列索引和空间索引等. 额外的:我已知的自动创建索引的时机:创建主键,唯一,外键约束的时候 一.索引简介 1.1 ...
- PHP 插入排序 -- 希尔排序
1.希尔排序 -- Shell Insertion Sort 时间复杂度:数学家正在勤劳的探索! 适用条件: 直接插入排序的改进,主要针对移动次数的减少,这取决于"增量队列"的取值 ...
- JS循环+循环嵌套+经典例题+图形题
首先,了解一下循环嵌套的特点:外层循环转一次,内层循环转一圈. 在上一篇随笔中详细介绍了JS中的分支结构和循环结构,我们来简单的回顾一下For循环结构: 1.for循环有三个表达式,分别为: ①定义循 ...
- css涂鸦这样玩
前言 上一次深扒CSS的时候,还说CSS和H5绘制复杂图形很麻烦,看了大神的操作后,感觉茅塞顿开了,哈哈. 就算可能我暂时没有用到的机会,学习一下开发者的设计思路也是受益匪浅呀. 嗯,今天要介绍的是一 ...
- gitlab 提交
gitlab 提交 Git global setup git config --global user.name "lial" git config --global user.e ...
- python使用input().split()接收多个用户输入
1.input() 接收多个用户输入需要与split()结合使用 host, port, username, passwd, dbname = input("请输入服务器地址,端口号,用户名 ...
- Java匹马行天下之新手学习目录
Java匹马行天下之新手学习目录 学习路线 [Java匹马行天下——Java学习路线] [Java匹马行天下——开篇学习计划] 基础篇 [Java匹马行天下之学编程的起点——编程常识知多少] [Jav ...
- Java描述设计模式(16):代理模式
本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 1.场景描述 在电商高速发展的今天,快递的数量十分庞大,甚至出现了快递代理行业,简单的说就是快递的主人没有时间收快递,会指定一个 ...
- LeetCode 十月份题目汇总
开源地址:点击该链接 前言 十月份共有60道题目,全部属于 Easy 难度的,所以公众号中分享出来的并不多,只是挑了一些感觉还可以的才分享了出来,这60道题目我按照不同类别进行了分类整理,所有源码以及 ...