Scrapy 之中间件(Middleware)的具体使用
作者:zombres 发布时间:2023-01-10 19:50:22
Scrapy 结构概述:
一、下载器中间件(Downloader Middleware)
如上图标号4、5处所示,下载器中间件用于处理scrapy的request和response的钩子框架,如在request中设置代理ip,header等,检测response的HTTP响应码等。
scrapy已经自带来一堆下载器中间件。
{
'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 download-middleware
自定义下载器中间件
有时我们需要编写自己的一些下载器中间件,如使用代理池,随机更换user-agent等,要使用自定义的下载器中间件,就需要在setting文件中激活我们自己的实现类,如下:
DOWNLOADERMIDDLEWARES = {
'myproject.middlewares.Custom_A_DownloaderMiddleware': 543,
'myproject.middlewares.Custom_B_DownloaderMiddleware': 643,
'myproject.middlewares.Custom_B_DownloaderMiddleware': None,
}
设置值是个DICT,key是我们自定义的类路径,后面数字是执行顺序,数字越小,越靠近引擎,数字越大越靠近下载器,所以数字越小的,processrequest()优先处理;数字越大的,process_response()优先处理;若需要关闭某个中间件直接设为None即可。
(PS. 如果两个下载器的没有强制的前后关系,数字大小没什么影响)
实现下载器我们需要重写以下几个方法:
对于请求的中间件实现 process_request(request, spider);
对于处理回复中间件实现process_response(request, response, spider);
以及异常处理实现 process_exception(request, exception, spider)
process_request(request, spider)
process_request:可以选择返回None、Response、Request、raise IgnoreRequest其中之一。
如果返回None,scrapy将继续处理该request,执行其他的中间件的响应方法。直到合适的下载器处理函数(downloader handler)被调用,该request被执行,其response被下载。
如果其返回Response对象,Scrapy将不会调用任何其他的process_request()或process_exception()方法,或相应地下载函数; 其将返回该response。已安装的中间件的process_response()方法则会在每个response返回时被调用
如果其返回Request对象,Scrapy则停止调用process_request方法并重新调度返回的request。当新返回的request被执行后,相应地中间件链将会根据下载的response被调用。
如果其raise IgnoreRequest,则安装的下载中间件的process_exception()方法会被调用。如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)。
通常返回None较常见,它会继续执行爬虫下去
process_response(request, response, spider)
当下载器完成HTTP请求,传递响应给引擎的时候调用,它会返回 Response 、Request 、IgnoreRequest三种对象的一种
若返回Response对象,它会被下个中间件中的process_response()处理
若返回Request对象,中间链停止,然后返回的Request会被重新调度下载
抛出IgnoreRequest,回调函数 Request.errback将会被调用处理,若没处理,将会忽略
process_exception(request, exception, spider)
当下载处理器(download handler)或process_request()抛出异常(包括 IgnoreRequest 异常)时, Scrapy调用 process_exception() ,通常返回None,它会一直处理异常
from_crawler(cls, crawler)
这个类方法通常是访问settings和signals的入口函数
例如下面2个例子是更换user-agent和代理ip的下载中间件
# setting中设置
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", \
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", \
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", \
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", \
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", \
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.132 Safari/537.36", \
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0"
]
PROXIES = [
'1.85.220.195:8118',
'60.255.186.169:8888',
'118.187.58.34:53281',
'116.224.191.141:8118',
'120.27.5.62:9090',
'119.132.250.156:53281',
'139.129.166.68:3128'
]
代理ip中间件
import random
class Proxy_Middleware():
def __init__(self, crawler):
self.proxy_list = crawler.settings.PROXY_LIST
self.ua_list = crawler.settings.USER_AGENT_LIST
@classmethod
def from_crawler(cls, crawler):
return cls(crawler)
def process_request(self, request, spider):
try:
ua = random.choice(self.ua_list)
request.headers.setdefault('User-Agent', ua)
proxy_ip_port = random.choice(self.proxy_list)
request.meta['proxy'] = 'http://' + proxy_ip_port
except request.exceptions.RequestException:
spider.logger.error('some error happended!')
重试中间件
有时使用代理会被远程拒绝或超时等错误,这时我们需要换代理ip重试,重写scrapy.downloadermiddlewares.retry.RetryMiddleware
from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message
class My_RetryMiddleware(RetryMiddleware):
def __init__(self, crawler):
self.proxy_list = crawler.settings.PROXY_LIST
self.ua_list = crawler.settings.USER_AGENT_LIST
@classmethod
def from_crawler(cls, crawler):
return cls(crawler)
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)
try:
ua = random.choice(self.ua_list)
request.headers.setdefault('User-Agent', ua)
proxy_ip_port = random.choice(self.proxy_list)
request.meta['proxy'] = 'http://' + proxy_ip_port
except request.exceptions.RequestException:
spider.logger.error('获取讯代理ip失败!')
return self._retry(request, reason, spider) or response
return response
# scrapy中对接selenium
from scrapy.http import HtmlResponse
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from gp.configs import *
class ChromeDownloaderMiddleware(object):
def __init__(self):
options = webdriver.ChromeOptions()
options.add_argument('--headless') # 设置 * 面
if CHROME_PATH:
options.binary_location = CHROME_PATH
if CHROME_DRIVER_PATH: # 初始化Chrome驱动
self.driver = webdriver.Chrome(chrome_options=options, executable_path=CHROME_DRIVER_PATH)
else:
self.driver = webdriver.Chrome(chrome_options=options) # 初始化Chrome驱动
def __del__(self):
self.driver.close()
def process_request(self, request, spider):
try:
print('Chrome driver begin...')
self.driver.get(request.url) # 获取网页链接内容
return HtmlResponse(url=request.url, body=self.driver.page_source, request=request, encoding='utf-8',
status=200) # 返回HTML数据
except TimeoutException:
return HtmlResponse(url=request.url, request=request, encoding='utf-8', status=500)
finally:
print('Chrome driver end...')
二、Spider中间件(Spider Middleware)
如文章第一张图所示,spider中间件用于处理response及spider生成的item和Request
启动自定义spider中间件必须先开启settings中的设置
SPIDER_MIDDLEWARES = {
'myproject.middlewares.CustomSpiderMiddleware': 543,
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None,
}
同理,数字越小越靠近引擎,process_spider_input()优先处理,数字越大越靠近spider,process_spider_output()优先处理,关闭用None
编写自定义spider中间件
process_spider_input(response, spider)
当response通过spider中间件时,这个方法被调用,返回None
process_spider_output(response, result, spider)
当spider处理response后返回result时,这个方法被调用,必须返回Request或Item对象的可迭代对象,一般返回result
process_spider_exception(response, exception, spider)
当spider中间件抛出异常时,这个方法被调用,返回None或可迭代对象的Request、dict、Item
补充一张图:
参考文档:
https://docs.scrapy.org/en/latest/topics/spider-middleware.html
https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
来源:https://blog.csdn.net/zombres/article/details/90080507


猜你喜欢
- 首先下载selenium模块,pip install selenium,下载一个浏览器驱动程序(我这里使用谷歌)。#导入#注意python各
- 迭代dict的key和value我们了解了如何迭代 dict 的key和value,那么,在一个 for 循环中,能否同时迭代 key和va
- iframe的背景颜色在IE下为window的当前窗口颜色(默认白色,可以在显示外观处对其进行设置),这导致在大幅背景上插入iframe时,
- 下面我们来看下,如何利用它来删除一个表中重复记录: If Exists(Select * From tempdb.Information_S
- 我们已经知道Application和Session对象的OnStart、OnEnd事件的脚本,都必须是在 Global.asa&n
- 重复触发就是防止用户重复点击提交数据了,我们一般都是点击之后没反应会再次点击了,这个不但要从用户体验上来做好,还在要js或php程序脚本上做
- 前言首先来讲一下服务端渲染,直白的说就是在服务端拿数据进行解析渲染,直接生成html片段返回给前端。具体用法也有很多种比如:传统的服务端模板
- 对于regex库的使用不难,因为本身就是python中自带的库,所以在调用上也是常见的库使用类型,大部分时候都是用于搜索上下文信息的,但是有
- 切片是 Python 中最迷人最强大最 Amazing 的语言特性(几乎没有之一),在《Python进阶:切片的误区与高级用法》中,我介绍了
- 给出列表切片的格式:[开头元素::步长] # 输出直到最后一个元素,(最后一个冒号和步长可以省略,下同)[开头元素:结尾元素(不含):步长]
- 1.使用效果如下面动图所示,点击取色按钮后,将鼠标移动到想要取色的位置,等待两秒即可取色:点击save color按钮可以将颜色存储到col
- 背景中秋的时候,一个朋友给我发了一封邮件,说他在爬链家的时候,发现网页返回的代码都是乱码,让我帮他参谋参谋(中秋加班,真是敬业= =!),其
- 背景:在需求开发过程中,有的接口返回的结果中有很多字段需要展示到页面上。通常可以将这些字段在.vue文件中封装为计算属性,或者重新将对应字段
- ASP.net处理文件上传就简单的多了,我呢也是在学习中,顺便写写学习笔记。 先在表单中添加enctype="multipart/
- 目录urllib库urllib.request模块Request对象1 . 请求头添加2. 操作cookie3. 设置代理urllib.pa
- ExtJS可以用来开发RIA也即富客户端的AJAX应用,是一个用javascript写 的,主要用于创建前端用户界面,是一个与后台技术无关的
- 序 言哈喽兄弟们,好久不见!最近实在太忙了,所以又双叒叕断更了~表弟大学快毕业了,学了一个学期Python居然还不会写学生管理系统,真的给我
- 在日常工作中,除了会涉及到使用Python处理文本文件,有时候还会涉及对压缩文件的处理。通常会涉及到的压缩文件格式有:rar:Windows
- 本文实例讲述了python获取目录下所有文件的方法。分享给大家供大家参考。具体分析如下:os.walk()函数声明:walk(top,top
- 本文实例讲述了php+mysql开发的最简单在线题库。分享给大家供大家参考,具体如下:题库,对于教育机构,学校,在线教育,是很有必要的,网上