返回顶部
首页 > 资讯 > 后端开发 > Python >Scrapy 之中间件(Middleware)的具体使用
  • 577
分享到

Scrapy 之中间件(Middleware)的具体使用

2024-04-02 19:04:59 577人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录一、下载器中间件(Downloader Middleware)process_request(request, spider)process_response(request,

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请求,传递响应给引擎的时候调用,它会返回 ResponseRequestIgnoreRequest三种对象的一种

  • 若返回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

到此这篇关于Scrapy 之中间件(Middleware)的文章就介绍到这了,更多相关Scrapy 之中间件(Middleware)内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Scrapy 之中间件(Middleware)的具体使用

本文链接: https://lsjlt.com/news/118679.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Scrapy 之中间件(Middleware)的具体使用
    目录一、下载器中间件(Downloader Middleware)process_request(request, spider)process_response(request, ...
    99+
    2024-04-02
  • Node.js怎么使用Middleware中间件
    今天小编给大家分享一下Node.js怎么使用Middleware中间件的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。中间件中...
    99+
    2023-07-06
  • Thinkphp6.0中间件的具体使用
    目录全局中间件应用中间件路由中间件控制器中间件中间件传参0中间件分为系统中间件和应用中间件,系统中间件为核心框架内置的中间件,应用中间件是在应用里面创建的中间件。 中间件的主要应用场景可以包括对HTTP请求的数据过滤、权限检测、请求拦截等行...
    99+
    2021-01-07
    Thinkphp6.0中间件 Thinkphp 中间件
  • laravel的中间件middleware怎么用
    这篇文章将为大家详细讲解有关laravel的中间件middleware怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。中间件可以对请求进行过滤,这里可以利用中间件来验证用户是否登录,如果用户登录则可以...
    99+
    2023-06-21
  • Node.js使用Middleware中间件教程详解
    目录中间件依赖注入应用中间件路由通配符中间件消费者路由排除函数式中间件多个中间件全局中间件中间件 中间件是一个在路由处理程序之前被调用的函数。中间件函数可以访问请求和响应对象,以及应...
    99+
    2023-05-15
    Node.js Middleware Node.js 中间件
  • Scrapy的中间件如何使用
    这篇文章主要讲解了“Scrapy的中间件如何使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Scrapy的中间件如何使用”吧!Scrapy 结构概述:一、下载器中间件(Downloader...
    99+
    2023-07-02
  • redux功能强大的Middleware中间件使用学习
    目录引言redux中的Middleware记录日志手动记录redux-sagaGenerator函数实际使用场景引言 上一节我们学习了redux在实际项目的应用细节,这一节我们来学习...
    99+
    2024-04-02
  • PHP异常处理:使用Middleware异常处理中间件
    php 异常处理使用 middleware 异常处理中间件,允许在应用程序的任何层处理异常,无需在每个控制器中显式处理。步骤如下:安装 symfony/error-handler 库。创...
    99+
    2024-05-14
    异常处理 laravel composer
  • python3之Splash的具体使用
    目录1、Scrapy-Splash的安装2、Splash Lua脚本3、Splash对象的属性与方法4、响应对象5、元素对象6、Splash HTTP API调用7、实例Splash...
    99+
    2024-04-02
  • nginx之queue的具体使用
    目录一、简介二、数据结构三、相关API3.1 初始化一个队列3.2 判断队列是否为空3.3 队头插入节点3.4 队尾插入节点3.5 从队列中移除某个节点3.6 将队列从某个节点拆分成...
    99+
    2024-04-02
  • Vue3中事件总线的具体使用
    目录导读事件总线的本质构建一个EventEmitterconfig.globalProperties方法provide/inject结束语导读 在Vue2中,我们遇到复杂的组件通信时...
    99+
    2023-05-15
    Vue3 事件总线 Vue 事件总线
  • Python中Tkinter组件Button的具体使用
    目录简介何时使用 Button 组件?用法参数方法简介 Button(按钮)组件用于实现各种各样的按钮。Button 组件可以包含文本或图像,你可以将一个 Python 的函数或方法...
    99+
    2024-04-02
  • Python中Tkinter组件Listbox的具体使用
    目录何时使用 Listbox 组件?用法参数方法Listbox(列表框)组件用于显示一个选择列表。Listbox 只能包含文本项目,并且所有的项目都需要使用相同的字体和颜色。根据组件...
    99+
    2024-04-02
  • Python中Tkinter组件Frame的具体使用
    目录何时使用 Frame 组件?用法参数Frame(框架)组件是在屏幕上的一个矩形区域。Frame 主要是作为其他组件的框架基础,或为其他组件提供间距填充。 何时使用 Frame 组...
    99+
    2024-04-02
  • Python中Tkinter组件Menu的具体使用
    目录何时使用 Menu 组件?用法参数方法Menu(菜单)组件用于实现顶级菜单、下拉菜单和弹出菜单。 何时使用 Menu 组件? Menu 组件通常被用于实现应用程序上的各种菜单,由...
    99+
    2024-04-02
  • 手把手带你定制.NET 6.0的Middleware中间件
    目录前言技术准备中间件简介编写自定义中间件中间件的潜力使用/map分支管道使用MapWhen分支管道使用中间件构造条件在ASP.NET Core 3.0及更高版本中使用中间件重写终止...
    99+
    2022-12-20
    .net 6.0 middleware中间件 .net中间件有哪些 .net 6.0 中间件
  • shell命令之mv的具体使用
    目录文件间的移动文件到目录的移动目录到目录的移动思考操作系统为 macOS 10.12.4 shell 的 mv 命令为移动(moving)的意思,实际可分为文件间的移动,文件到目录的移动,目录到目录的移动。 mv参数设...
    99+
    2022-06-05
    shell命令mv
  • pytest之assert断言的具体使用
    背景 本文总结使用pytest编写自动化测试时常用的assert断言。 说明 本文将从以下几点做总结: 为测试结果作断言 为断言不通过的结果添加说明信息 ...
    99+
    2024-04-02
  • C++17之std::any的具体使用
    目录1. 使用std::any 2. std::any类型和操作2.1 std::any的类型2.2 std::any操作    一般来说,c++是一种...
    99+
    2024-04-02
  • C++17之std::visit的具体使用
    目录1. 使用对象函数方式访问 2. 使用泛型Lambdas访问3. 使用重载的Lambdas来访问    它们必须明确地为每种可能的类型提供函数调用操...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作