手记

Scrapy下载器中间件学习:从入门到实践

概述

本文详细介绍了Scrapy下载器中间件的使用方法,包括添加请求头、处理异常、设置请求超时时间等功能,帮助开发者增强爬虫的灵活性和扩展性。通过配置和使用下载器中间件,可以实现自定义的抓取逻辑,提高数据抓取的效率和稳定性。此外,文章还提供了示例代码和推荐的学习资源,帮助读者深入理解和应用Scrapy下载器中间件学习。

Scrapy简介与基础架构

Scrapy 是一个强大的 Python 爬虫框架,专为大规模数据抓取而设计。它采用异步模型,支持多种数据抓取任务,包括网站抓取、API 接口数据抓取等。Scrapy 通过结构化的项目请求和响应数据来定义爬虫行为,使得开发者能够专注于数据的提取和处理,而不必关心底层网络通信的细节。

Scrapy的工作流程

Scrapy 的工作流程可以分为以下几个步骤:

  1. 启动:当 Scrapy 爬虫启动时,首先会调用 start_requests 方法,生成初始的请求队列。
  2. 请求生成start_requests 方法会返回一个包含初始请求的生成器,这些请求会被 Scrapy 的调度器接收。
  3. 调度:调度器将接收到的请求放入待处理队列,调度器根据一定的策略选择请求并发送给下载器。
  4. 下载:下载器从调度器获取请求,发送 HTTP 请求并接收响应。
  5. 响应处理:下载器将响应传递给相应处理器(如下载器中间件、Spider中间件)进行处理。
  6. 解析数据:Spider 对接收到的响应进行解析,提取所需的数据。
  7. 数据处理:Spider 把解析出来的数据传递给 Item Pipeline 进行进一步处理。
  8. 结束:当所有请求都处理完毕后,Scrapy 爬虫会关闭。
Scrapy下载器的作用与位置

Scrapy 下载器是 Scrapy 框架中的核心组件之一,主要负责发送 HTTP 请求和接收响应。下载器位于 Scrapy 的架构中,介于调度器和相应处理器之间。

Scrapy 下载器位置图示

+-------------------+     +-------------------+     +-------------------+
|   Request Queue   | --> |    Scheduler      | --> |  Downloader       |
|    (调度器)       |     +-------------------+     +-------------------+
+-------------------+     +-------------------+     +-------------------+
                                                     +-------------------+
                                                     |  Response Queue   |
                                                     |  (响应队列)       |
                                                     +-------------------+

下载器的主要职责包括生成 HTTP 请求、发送请求到网络、接收响应,并将响应传递给相应的处理器。下载器还支持并发处理多个请求,提高了数据抓取的效率。

Scrapy下载器的基本功能

Scrapy 下载器实现了发送 HTTP 请求和接收响应的基本功能。在 Scrapy 中,请求和响应对象是通过 RequestResponse 类来表示的。这些对象提供了丰富的方法和属性,方便开发者进行数据抓取。

HTTP请求的发送

在 Scrapy 中,发送 HTTP 请求是通过 Request 类来实现的。Request 类的构造函数接受多个参数,其中最常用的参数包括:

  • url:请求的目标 URL。
  • callback:一个回调函数,当请求成功时将调用此函数。
  • errback:一个回调函数,当请求失败时将调用此函数。
  • method:HTTP 请求方法,默认值为 'GET'
  • headers:HTTP 请求头。

以下是一个简单的 Request 对象的示例:

from scrapy import Request

# 创建一个 GET 请求
req = Request(url='https://example.com', callback=self.parse)

# 创建一个 POST 请求
req = Request(url='https://example.com', method='POST', callback=self.parse)
HTTP响应的接收

Scrapy 下载器会发送 HTTP 请求并将接收到的响应传递给相应的处理器。响应对象 Response 包含了服务器返回的信息,如状态码、请求头、响应体等。Response 类的常用属性如下:

  • status:服务器响应的状态码。
  • headers:响应头。
  • body:响应体,即服务器返回的数据。
  • url:请求的目标 URL。
  • request:生成该响应的请求。

以下是一个简单的 Response 对象的示例:

from scrapy import Request
from scrapy.http import HtmlResponse

def start_requests(self):
    # 发送请求
    req = Request(url='https://example.com', callback=self.parse)
    yield req

def parse(self, response):
    # 获取响应的状态码
    status_code = response.status

    # 获取响应体
    body = response.body

    # 获取响应头
    headers = response.headers
Request和Response对象的基本操作

RequestResponse 对象提供了丰富的操作方法,使得开发者能够灵活地处理 HTTP 请求和响应。

Request对象的操作

  • url:请求的 URL。
  • method:请求的方法,如 'GET''POST'
  • callback:回调函数,用于处理响应。
  • errback:错误处理函数,用于处理异常。

Response对象的操作

  • status:响应的状态码。
  • headers:响应的头信息。
  • body:响应的正文内容。
  • request:生成该响应的请求。

以下是一些常用的 RequestResponse 对象的操作示例:

from scrapy import Request
from scrapy.http import HtmlResponse
from scrapy.item import Item, Field

def start_requests(self):
    # 创建一个 GET 请求
    req = Request(url='https://example.com', callback=self.parse)
    yield req

def parse(self, response):
    # 获取响应的状态码
    status_code = response.status

    # 获取响应体
    body = response.body

    # 获取响应头
    headers = response.headers

    # 创建一个新的响应对象
    new_response = HtmlResponse(url=response.url, status=response.status, body=response.body, encoding='utf-8')

    # 创建一个新的请求对象
    new_req = Request(url='https://example.com', callback=self.parse)

    # 创建一个新的 Item 对象
    item = Item()
    item['url'] = response.url
    item['status'] = response.status
    item['body'] = response.body.decode('utf-8')

    return item
Scrapy下载器中间件的介绍
什么是下载器中间件

下载器中间件是一组类,这些类定义了在请求发送前和响应接收后需要执行的操作。这些类中的方法会在请求发送前或响应接收后被调用,允许开发者修改请求和响应的内容。

下载器中间件的类通常位于项目的 middlewares 目录下,并且需要继承自 scrapy.downloadermiddlewares.DownloaderMiddleware 类。每个中间件类中定义了几个特定的方法,这些方法会在特定的时间点被调用。

下载器中间件的定义

下载器中间件的类需要定义以下方法:

  • process_request(request, spider):在请求发送前调用,可以修改请求对象或决定是否继续处理该请求。
  • process_response(request, response, spider):在响应接收后调用,可以修改响应对象或决定是否继续处理该响应。
  • process_exception(request, exception, spider):在请求异常时调用,可以处理异常或决定是否重新发送请求。

下载器中间件的注册

下载器中间件类需要在项目的 settings.py 文件中进行注册,以便 Scrapy 能够识别并使用这些中间件。

# settings.py

# 启用下载器中间件
DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.MyCustomDownloaderMiddleware': 543,
}
下载器中间件的作用

下载器中间件在 Scrapy 的请求处理流程中起到非常重要的作用,主要有以下几点:

  • 添加请求头:通过修改请求头,可以添加如 User-AgentCookie 等信息。
  • 处理异常:可以捕获请求过程中可能出现的异常,并进行相应的处理。
  • 修改请求参数:可以在请求发送前修改请求中的参数。
  • 修改响应内容:可以在响应接收后修改响应的内容。

通过这些操作,下载器中间件可以增强 Scrapy 的灵活性和扩展性,使得爬虫能够应对更加复杂的数据抓取需求。

下载器中间件的组成与工作原理

下载器中间件模块由多个中间件类组成,每个类定义了特定的方法来处理请求和响应。中间件的调用顺序由 settings.py 中的 DOWNLOADER_MIDDLEWARES 字典中的键值对决定。键为中间件类的导入路径,值为中间件的优先级,优先级越低表示优先级越高。

下载器中间件的调用顺序

中间件的调用顺序由 DOWNLOADER_MIDDLEWARES 中的优先级决定:

  1. 在请求发送前调用 process_request 方法。
  2. 在响应接收后调用 process_response 方法。
  3. 在请求异常时调用 process_exception 方法。

下载器中间件的工作流程

  1. 当请求被调度器取出时,会依次通过每个中间件的 process_request 方法。
  2. 如果某个中间件返回 None,则会继续下一层中间件的处理。
  3. 如果某个中间件返回 Response 对象,则会终止后续中间件的调用,直接传递这个响应对象。
  4. 如果请求成功,响应会依次通过每个中间件的 process_response 方法。
  5. 如果某个中间件返回 None,则会继续下一层中间件的处理。
  6. 如果某个中间件返回 Request 对象,则会触发一个新的请求。
  7. 如果请求失败,异常会依次通过每个中间件的 process_exception 方法。
  8. 如果某个中间件返回 ResponseRequest 对象,则会终止后续中间件的调用,并传递这个对象。

示例代码

以下是一个简单的下载器中间件示例,演示了如何添加请求头和处理异常:

# myproject/middlewares.py

from scrapy import signals
from scrapy.exceptions import IgnoreRequest
from scrapy.http import HtmlResponse

class MyCustomDownloaderMiddleware:

    @classmethod
    def from_crawler(cls, crawler):
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

    def process_request(self, request, spider):
        # 添加自定义请求头
        request.headers['User-Agent'] = 'Custom User-Agent'

    def process_response(self, request, response, spider):
        # 检查响应的状态码
        if response.status == 404:
            return HtmlResponse(url='https://example.com/404', status=404)
        return response

    def process_exception(self, request, exception, spider):
        # 处理请求异常
        if isinstance(exception, TimeoutError):
            spider.logger.warning(f"Request to {request.url} timed out")
            return HtmlResponse(url=request.url, status=408)
Scrapy下载器中间件的配置和使用

Scrapy 下载器中间件的配置和使用是实现自定义抓取逻辑的重要步骤。通过配置和使用下载器中间件,开发者能够灵活地处理请求和响应,增强爬虫的功能和效率。

如何设置下载器中间件

为了启用和配置下载器中间件,开发者需要在项目的 settings.py 文件中定义 DOWNLOADER_MIDDLEWARES 字典。这个字典包含了所有需要使用的下载器中间件类及其优先级。

下载器中间件字典的格式

DOWNLOADER_MIDDLEWARES 字典的键是中间件类的完整导入路径,值是中间件的优先级。优先级越低,中间件的优先级越高。中间件的优先级决定了它们在请求处理过程中的调用顺序。

# settings.py

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.MyCustomDownloaderMiddleware': 543,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 123,
    'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
}

示例代码

以下是一个示例代码,定义了一个简单的自定义下载器中间件:

# myproject/middlewares.py

from scrapy import signals
from scrapy.http import HtmlResponse
from scrapy.exceptions import IgnoreRequest

class MyCustomDownloaderMiddleware:

    @classmethod
    def from_crawler(cls, crawler):
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

    def process_request(self, request, spider):
        # 添加自定义请求头
        request.headers['User-Agent'] = 'Custom User-Agent'

    def process_response(self, request, response, spider):
        # 检查响应的状态码
        if response.status == 404:
            return HtmlResponse(url='https://example.com/404', status=404)
        return response

    def process_exception(self, request, exception, spider):
        # 处理请求异常
        if isinstance(exception, TimeoutError):
            spider.logger.warning(f"Request to {request.url} timed out")
            return HtmlResponse(url=request.url, status=408)

如何启用中间件

为了启用自定义的下载器中间件,需要在 settings.py 中设置 DOWNLOADER_MIDDLEWARES 字典,并指定中间件的优先级。

# settings.py

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.MyCustomDownloaderMiddleware': 543,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 123,
    'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
}
自定义下载器中间件的方法

自定义下载器中间件可以通过继承 scrapy.downloadermiddlewares.DownloaderMiddleware 类来实现。在中间件类中,需要定义以下几个方法:

  • process_request(request, spider):在请求发送前调用,可以修改请求对象或决定是否继续处理该请求。
  • process_response(request, response, spider):在响应接收后调用,可以修改响应对象或决定是否继续处理该响应。
  • process_exception(request, exception, spider):在请求异常时调用,可以处理异常或决定是否重新发送请求。

示例代码

以下是一个自定义下载器中间件的完整示例,演示了如何添加请求头和处理异常:

# myproject/middlewares.py

from scrapy import signals
from scrapy.http import HtmlResponse
from scrapy.exceptions import IgnoreRequest

class MyCustomDownloaderMiddleware:

    @classmethod
    def from_crawler(cls, crawler):
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

    def process_request(self, request, spider):
        # 添加自定义请求头
        request.headers['User-Agent'] = 'Custom User-Agent'

    def process_response(self, request, response, spider):
        # 检查响应的状态码
        if response.status == 404:
            return HtmlResponse(url='https://example.com/404', status=404)
        return response

    def process_exception(self, request, exception, spider):
        # 处理请求异常
        if isinstance(exception, TimeoutError):
            spider.logger.warning(f"Request to {request.url} timed out")
            return HtmlResponse(url=request.url, status=408)
常见的下载器中间件示例

Scrapy 内置了一些常用的下载器中间件,这些中间件提供了很多有用的功能,如代理 IP 轮换、Cookie 管理等。

HttpProxyMiddleware

HttpProxyMiddleware 中间件用于处理 HTTP 代理请求。它会根据配置的代理 IP 列表来轮换代理 IP。

# settings.py

DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 123,
}

# 使用代理 IP
HTTP_PROXY_LIST = [
    'http://proxy1.example.com:8080',
    'http://proxy2.example.com:8080',
]

CookiesMiddleware

CookiesMiddleware 中间件用于管理 Cookies。它可以自动处理登录会话。

# settings.py

DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
}

# 配置 Cookie
COOKIES_DEBUG = True

示例代码

以下是一个使用 HttpProxyMiddlewareCookiesMiddleware 的示例:

# settings.py

DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 123,
    'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
}

# 使用代理 IP
HTTP_PROXY_LIST = [
    'http://proxy1.example.com:8080',
    'http://proxy2.example.com:8080',
]

# 配置 Cookie
COOKIES_DEBUG = True
Scrapy下载器中间件实战

在实际应用中,下载器中间件可以帮助我们实现许多常用的功能,比如添加请求头、设置请求超时时间、处理不同类型的响应等。通过这些功能,我们可以更加灵活地控制 Scrapy 的抓取行为。

添加User-Agent以避免被服务器识别

在进行网页抓取时,服务器可能会根据请求头中的 User-Agent 字段来判断请求是否来自浏览器。如果请求被识别为爬虫,服务器可能会拒绝服务或限制抓取频率。通过在下载器中间件中添加自定义 User-Agent,可以有效避免被服务器识别为爬虫。

示例代码

以下是一个简单的示例,演示了如何在下载器中间件中添加自定义 User-Agent

# myproject/middlewares.py

from scrapy import signals
from scrapy.http import HtmlResponse
from scrapy.exceptions import IgnoreRequest

class MyCustomDownloaderMiddleware:

    @classmethod
    def from_crawler(cls, crawler):
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

    def process_request(self, request, spider):
        # 添加自定义请求头
        request.headers['User-Agent'] = 'Custom User-Agent'

    def process_response(self, request, response, spider):
        # 检查响应的状态码
        if response.status == 404:
            return HtmlResponse(url='https://example.com/404', status=404)
        return response

    def process_exception(self, request, exception, spider):
        # 处理请求异常
        if isinstance(exception, TimeoutError):
            spider.logger.warning(f"Request to {request.url} timed out")
            return HtmlResponse(url=request.url, status=408)

如何启用中间件

为了启用自定义的下载器中间件,需要在 settings.py 中设置 DOWNLOADER_MIDDLEWARES 字典,并指定中间件的优先级。

# settings.py

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.MyCustomDownloaderMiddleware': 543,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 123,
    'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
}
设置请求超时时间

在进行网络抓取时,有时需要设置请求超时时间,以防止请求长时间阻塞。Scrapy 允许通过下载器中间件来设置请求超时时间,从而控制抓取的效率和稳定性。

示例代码

以下是一个示例代码,演示了如何在下载器中间件中设置请求超时时间:

# myproject/middlewares.py

from scrapy import signals
from scrapy.http import HtmlResponse
from scrapy.exceptions import IgnoreRequest
from scrapy.utils.project import get_project_settings

class MyCustomDownloaderMiddleware:

    @classmethod
    def from_crawler(cls, crawler):
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

    def process_request(self, request, spider):
        # 添加自定义请求头
        request.headers['User-Agent'] = 'Custom User-Agent'

        # 设置请求超时时间
        settings = get_project_settings()
        request.meta['download_timeout'] = settings.get('DOWNLOAD_TIMEOUT', 10)

    def process_response(self, request, response, spider):
        # 检查响应的状态码
        if response.status == 404:
            return HtmlResponse(url='https://example.com/404', status=404)
        return response

    def process_exception(self, request, exception, spider):
        # 处理请求异常
        if isinstance(exception, TimeoutError):
            spider.logger.warning(f"Request to {request.url} timed out")
            return HtmlResponse(url=request.url, status=408)

如何设置超时时间

settings.py 文件中设置 DOWNLOAD_TIMEOUT 参数来设置请求超时时间。

# settings.py

DOWNLOAD_TIMEOUT = 15
处理不同类型的响应数据

在进行网页抓取时,可能会遇到不同类型的响应数据,如 JSON 数据、XML 数据等。下载器中间件可以用来处理这些不同类型的响应数据,并将其转换为适合进一步处理的格式。

示例代码

以下是一个示例代码,演示了如何在下载器中间件中处理 JSON 数据:

# myproject/middlewares.py

import json
from scrapy import signals
from scrapy.http import HtmlResponse
from scrapy.exceptions import IgnoreRequest

class MyCustomDownloaderMiddleware:

    @classmethod
    def from_crawler(cls, crawler):
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

    def process_request(self, request, spider):
        # 添加自定义请求头
        request.headers['User-Agent'] = 'Custom User-Agent'

        # 设置请求超时时间
        settings = get_project_settings()
        request.meta['download_timeout'] = settings.get('DOWNLOAD_TIMEOUT', 10)

    def process_response(self, request, response, spider):
        # 检查响应的状态码
        if response.status == 404:
            return HtmlResponse(url='https://example.com/404', status=404)

        # 处理 JSON 数据
        if response.headers.get('Content-Type') == b'application/json':
            json_data = json.loads(response.text)
            return HtmlResponse(url=request.url, status=response.status, body=json.dumps(json_data).encode('utf-8'), encoding='utf-8')

        return response

    def process_exception(self, request, exception, spider):
        # 处理请求异常
        if isinstance(exception, TimeoutError):
            spider.logger.warning(f"Request to {request.url} timed out")
            return HtmlResponse(url=request.url, status=408)

如何识别响应类型

process_response 方法中,可以通过检查响应头中的 Content-Type 字段来识别响应的数据类型。

# process_response 方法处理 JSON 数据

if response.headers.get('Content-Type') == b'application/json':
    json_data = json.loads(response.text)
    return HtmlResponse(url=request.url, status=response.status, body=json.dumps(json_data).encode('utf-8'), encoding='utf-8')
总结与拓展学习
Scrapy下载器中间件的总结

Scrapy 下载器中间件是 Scrapy 架构中的一个关键组件,用于在请求发送和响应接收之间拦截请求和响应,并对其进行预处理或后处理。通过下载器中间件,开发者可以灵活地实现各种功能,如添加请求头、修改请求参数、处理不同类型的响应等。中间件的使用可以提高 Scrapy 爬虫的灵活性和扩展性,使其能够应对更加复杂的数据抓取需求。

下载器中间件的主要功能

  • 添加请求头:可以添加如 User-AgentCookie 等信息。
  • 处理异常:可以捕获请求过程中可能出现的异常,并进行相应的处理。
  • 修改请求参数:可以在请求发送前修改请求中的参数。
  • 修改响应内容:可以在响应接收后修改响应的内容。

下载器中间件的调用流程

  1. 在请求发送前调用 process_request 方法。
  2. 在响应接收后调用 process_response 方法。
  3. 在请求异常时调用 process_exception 方法。
推荐的学习资源与进阶资料

为了深入了解 Scrapy 下载器中间件,可以参考以下学习资源:

  • 官方文档:Scrapy 官方文档提供了详细的下载器中间件教程和示例代码,是学习 Scrapy 的最佳资源。
  • 慕课网:慕课网上有很多 Scrapy 的课程,可以系统地学习 Scrapy 的各个方面。
  • Scrapy GitHub 仓库:Scrapy 的 GitHub 仓库包含了源码,开发者可以深入研究其内部实现。

示例代码

以下是一个简单的下载器中间件示例,演示了如何添加请求头和处理异常:

# myproject/middlewares.py

from scrapy import signals
from scrapy.http import HtmlResponse
from scrapy.exceptions import IgnoreRequest

class MyCustomDownloaderMiddleware:

    @classmethod
    def from_crawler(cls, crawler):
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

    def process_request(self, request, spider):
        # 添加自定义请求头
        request.headers['User-Agent'] = 'Custom User-Agent'

    def process_response(self, request, response, spider):
        # 检查响应的状态码
        if response.status == 404:
            return HtmlResponse(url='https://example.com/404', status=404)
        return response

    def process_exception(self, request, exception, spider):
        # 处理请求异常
        if isinstance(exception, TimeoutError):
            spider.logger.warning(f"Request to {request.url} timed out")
            return HtmlResponse(url=request.url, status=408)

进阶学习

  • 深入理解 Scrapy 架构:了解 Scrapy 的整体架构可以帮助更好地理解下载器中间件的作用和工作机制。
  • 实践案例:通过实际项目中的应用,不断积累经验,提高 Scrapy 技能。
  • 社区贡献:参与 Scrapy 社区的讨论和贡献代码,可以加深对 Scrapy 的理解和应用。
0人推荐
随时随地看视频
慕课网APP