Использование Downloader Middleware при использовании фреймворка Scrapy

задняя часть Архитектура рептилия Scrapy

Downloader Middleware — это промежуточное ПО загрузки, которое представляет собой модуль обработки между запросом и ответом Scrapy. Давайте сначала посмотрим на его архитектуру, как показано на рисунке ниже.

Планировщик берет запрос из очереди и отправляет его загрузчику для выполнения загрузки, этот процесс будет обрабатываться промежуточным программным обеспечением загрузчика. Кроме того, когда загрузчик завершит загрузку запроса и вернет ответ пауку, он снова будет обработан промежуточным программным обеспечением загрузчика.

Другими словами, во всей архитектуре Downloader Middleware играет роль в следующих двух местах:

  • Перед тем, как планировщик отправит поставленный в очередь запрос Doanloader для загрузки, то есть мы можем изменить его до того, как запрос выполнит загрузку.

  • Прежде чем ответ, сгенерированный после загрузки, будет отправлен пауку, то есть мы можем изменить сгенерированный ответ до того, как он будет проанализирован пауком.

Функция Downloader Middleware очень мощная, и такие функции, как изменение User-Agent, обработка перенаправления, настройка прокси-сервера, повторная попытка при сбое, настройка файлов cookie и т. д., должны быть реализованы вместе с ним. Давайте взглянем на подробное использование промежуточного программного обеспечения Downloader.

1. Инструкция по применению

Следует отметить, что Scrapy на самом деле предоставил много промежуточного программного обеспечения загрузчика, такого как промежуточное программное обеспечение, отвечающее за повторную попытку при сбое, автоматическое перенаправление и другие функции.DOWNLOADER_MIDDLEWARES_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. Значение ключа представляет собой приоритет вызова. Приоритет — число. Чем меньше число, тем ближе к движку Scrapy , Чем больше номер, тем ближе к Downloader. , Downloader Middleware с меньшими номерами будет вызываться первым.

Если в проект необходимо добавить промежуточное программное обеспечение загрузчика, определенное вами,DOWNLOADER_MIDDLEWARES_BASEПеременные не могут быть изменены напрямую. Scrapy предоставляет еще одну переменную настройкиDOWNLOADER_MIDDLEWARES, мы можем напрямую изменить эту переменную, чтобы добавить наше собственное промежуточное ПО Downloader и отключитьDOWNLOADER_MIDDLEWARES_BASEПромежуточное программное обеспечение загрузчика, определенное внутри. Давайте подробно рассмотрим использование Downloader Middleware.

Основной метод

Встроенное ПО промежуточного слоя Downloader в Scrapy предоставляет базовые функции для Scrapy, но в реальных проектах нам часто приходится определять промежуточное ПО Downloader отдельно. Не волнуйтесь, процесс очень простой, нам нужно всего лишь реализовать несколько методов.

Каждое промежуточное ПО загрузчика определяет класс с одним или несколькими методами. Основные методы следующие.

  • process_request(request, spider).

  • process_response(request, response, spider).

  • process_exception(request, exception, spider).

Нам нужно реализовать хотя бы один метод для определения промежуточного программного обеспечения загрузчика. Давайте посмотрим на подробное использование этих трех методов.

1. process_request(request, spider)

Прежде чем запрос будет отправлен загрузчику движком Scrapy,process_request()Метод будет вызван, то есть до того, как Запрос будет отправлен из очереди и загружен и выполнен Загрузчиком, мы можем использоватьprocess_request()Метод обрабатывает запрос. Возвращаемое значение метода должно быть одним из следующих: None, Response object, Request object или throw.IgnoreRequestаномальный.

process_request()Параметры метода следующие.

  • request, является объектом запроса, то есть обрабатываемым запросом.

  • spider, является объектом Паука, то есть Пауком, соответствующим этому Запросу.

Различные типы возврата имеют разные эффекты. Ниже приводится сводка различных ситуаций возврата.

  • Когда возвращается None, Scrapy продолжит обработку запроса, а затем выполнит другие промежуточные программы Downloader.process_request()Метод не заканчивается, пока загрузчик не выполнит запрос и не получит ответ. Этот процесс на самом деле является процессом изменения запроса.Различные промежуточные программы загрузчика изменяют запрос по очереди в соответствии с установленным порядком приоритетов и, наконец, отправляют его загрузчику для выполнения.

  • При возврате в виде объекта Response промежуточное ПО загрузчика с более низким приоритетомprocess_request()иprocess_exception()метод больше не будет вызываться, каждый компонент Downloader Middlewareprocess_response()Вместо этого методы вызываются последовательно. После завершения вызова объект Response напрямую отправляется в Spider для обработки.

  • При возврате в качестве объекта запроса промежуточное ПО загрузчика с более низким приоритетомprocess_request()Метод перестает выполняться. Этот запрос будет возвращен в очередь планирования. Фактически, это совершенно новый запрос, ожидающий планирования. Если запланировано планировщиком, то все промежуточное ПО загрузчикаprocess_request()Методы будут повторно выполнены по порядку.

  • еслиIgnoreRequestвыдается исключение, все ПО промежуточного слоя загрузчикаprocess_exception()Методы выполняются последовательно. Если нет метода для обработки этого исключения, то запросerrorback()метод будет вызван обратно. Если исключение не было обработано, оно игнорируется.

2. process_response(request, response, spider)

После того, как загрузчик выполнит загрузку запроса, он получит соответствующий ответ. Движок Scrapy отправит ответ пауку для разбора. Перед отправкой мы все можем использоватьprocess_response()метод обработки ответа. Возвращаемое значение метода должно быть одним из объектов Request, Response или вызывать исключение IgnoreRequest.

process_response()Параметры метода следующие.

  • request, является объектом Запроса, то есть Запросом, соответствующим этому Отклику.

  • response, является объектом Response, то есть обработанным Response.

  • spider, является объектом Spider, то есть Spider, соответствующим этому Response.

Ниже приводится сводка различных ситуаций возврата.

  • При возврате в качестве объекта запроса промежуточное ПО загрузчика с более низким приоритетомprocess_response()Метод больше не будет вызываться. Объект запроса будет возвращен в очередь планирования для планирования, что эквивалентно совершенно новому запросу. Тогда запрос будетprocess_request()методы обрабатываются последовательно.

  • При возврате в виде объекта Response промежуточное ПО загрузчика с более низким приоритетомprocess_response()Метод будет продолжать вызываться и продолжать обрабатывать объект Response.

  • Если выдается исключение IgnoreRequest, запросerrorback()метод перезвонит. Если исключение не было обработано, оно игнорируется.

3. process_exception(request, exception, spider)

Когда загрузчик илиprocess_request()Когда метод выдает исключение, напримерIgnoreRequestаномальный,process_exception()метод будет вызван. Возвращаемое значение метода должно быть одним из None, объекта Response и объекта Request.

process_exception()Параметры метода следующие.

  • request, является объектом запроса, то есть запросом, вызвавшим исключение.

  • exception, является объектом Exception, то есть выброшенным исключением.

  • spdier, является объектом Spider, то есть Spider, соответствующим Запросу.

Ниже приведены различные возвращаемые значения.

  • Если возвращено значение «Нет», промежуточное ПО загрузчика с более низким приоритетомprocess_exception()Будет продолжать вызываться последовательно, пока все методы не будут отправлены.

  • При возврате в виде объекта Response промежуточное ПО загрузчика с более низким приоритетомprocess_exception()метод больше не вызывается постоянно, каждое промежуточное ПО Downloaderprocess_response()Вместо этого методы вызываются последовательно.

  • При возврате в качестве объекта запроса промежуточное ПО загрузчика с более низким приоритетомprocess_exception()Он больше не будет вызываться, а объект запроса будет возвращен в очередь планирования для планирования, что эквивалентно совершенно новому запросу. Тогда запрос будетprocess_request()методы обрабатываются последовательно.

Приведенное выше содержание представляет собой подробную логику использования этих трех методов. Прежде чем использовать их, у вас должно быть четкое представление об обработке возвращаемых значений этих трех методов. При настройке промежуточного программного обеспечения загрузчика вы также должны обратить внимание на тип возвращаемого значения каждого метода.

Давайте рассмотрим реальный случай, чтобы углубить наше понимание использования Downloader Middleware.

3. Боевой проект

Создайте новый проект с помощью следующей команды:

scrapy startproject scrapydownloadertest

Создайте сцепное проект с именем ScrapyDownLoadRtest. Введите проект, создайте новый паук, команда выглядит следующим образом:

scrapy genspider httpbin httpbin.org

Создается новый Spider с именем httpbin, исходный код которого выглядит следующим образом:

import scrapy
class HttpbinSpider(scrapy.Spider):
    name = 'httpbin'
    allowed_domains = ['httpbin.org']
    start_urls = ['http://httpbin.org/']

    def parse(self, response):
        pass

Далее мы модифицируемstart_urlsза:[http://httpbin.org/](http://httpbin.org/). будет затемparse()метод добавляет строку вывода журнала, которая будетresponseПеременнаяtextСвойства выводятся, чтобы мы могли видеть информацию о запросе, отправленную Scrapy.

Измените содержимое Spider следующим образом:

import scrapy

class HttpbinSpider(scrapy.Spider):
    name = 'httpbin'
    allowed_domains = ['httpbin.org']
    start_urls = ['http://httpbin.org/get']

    def parse(self, response):
        self.logger.debug(response.text)

Затем запустите паука и выполните следующую команду:

scrapy crawl httpbin

Результат выполнения Scrapy содержит информацию о запросе, отправленную Scrapy, следующим образом:

{
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 
    "Accept-Encoding": "gzip,deflate,br", 
    "Accept-Language": "en", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "Scrapy/1.4.0 (+http://scrapy.org)"
  }, 
  "origin": "60.207.237.85", 
  "url": "http://httpbin.org/get"
}

Давайте посмотрим на заголовки.User-Agent, используемый запросом, отправленным Scrapy, — это Scrapy/1.4.0 (+http://scrapy.org), который фактически устанавливается встроенным в Scrapy UserAgentMiddleware.Исходный код `UserAgentMiddleware` выглядит следующим образом:

from scrapy import signals

class UserAgentMiddleware(object):
    def __init__(self, user_agent='Scrapy'):
        self.user_agent = user_agent

    @classmethod
    def from_crawler(cls, crawler):
        o = cls(crawler.settings['USER_AGENT'])
        crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
        return o

    def spider_opened(self, spider):
        self.user_agent = getattr(spider, 'user_agent', self.user_agent)

    def process_request(self, request, spider):
        if self.user_agent:
            request.headers.setdefault(b'User-Agent', self.user_agent)

существуетfrom_crawler()Метод, сначала попробуйте получитьsettingsвUSER_AGENT, затем поставьтеUSER_AGENTПерейти к__init__()Метод инициализирован, и его параметрыuser_agent. если не прошелUSER_AGENTПо умолчанию для параметра задана строка Scrapy. В нашем новом проекте нет настроекUSER_AGENT, так вотuser_agentПеременные Scrapy. Далее, вprocess_request()метод,user-agentпеременная установлена ​​вheadersСвойство переменной, которое успешно устанавливает User-Agent. Таким образом, User-Agent проходит через промежуточное ПО Downloader.process_request()набор методов.

Есть два способа изменить User-Agent запроса: один — изменитьsettingsвнутриUSER_AGENTПеременная; второй через Downloader Middlewareprocess_request()метод модификации.

Первый способ очень простой, нам нужно только добавить строчку в settings.pyUSER_AGENTможно определить как:

USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'

Обычно рекомендуется использовать этот метод для установки. Но если вы хотите установить более гибкие настройки, такие как установка случайногоUser-Agent, то вам нужно использовать Downloader Middleware. Итак, далее мы используем Downloader Middleware для реализации случайной настройки User-Agent.

Добавьте внутренний файл middlewares.py.RandomUserAgentMiddlewareкласс следующим образом:

import random

class RandomUserAgentMiddleware():
    def __init__(self):
        self.user_agents = [
            'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)',
            'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2',
            'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1'
        ]

    def process_request(self, request, spider):
        request.headers['User-Agent'] = random.choice(self.user_agents)

Мы начинаем с класса__init__()В методе определены три разных пользовательских агента, представленных в виде списка. Далее реализованоprocess_request()метод, который принимает один параметрrequest, мы напрямую модифицируемrequestможно использовать свойства. Здесь мы прямо задаемrequestПеременнаяheadersUser-Agent атрибута, содержимое настройки — это случайно выбранный User-Agent, такой Downloader Middleware пишется.

Однако, чтобы сделать его эффективным, нам нужно снова вызвать Downloader Middleware. В settings.py поместитеDOWNLOADER_MIDDLEWARESРаскомментируйте и установите следующее:

DOWNLOADER_MIDDLEWARES = {
   'scrapydownloadertest.middlewares.RandomUserAgentMiddleware': 543,
}

Затем мы повторно запускаем Spider и видим, что User-Agent был успешно изменен на случайный User-Agent, определенный в списке:

{
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 
    "Accept-Encoding": "gzip,deflate,br", 
    "Accept-Language": "en", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)"
  }, 
  "origin": "60.207.237.85", 
  "url": "http://httpbin.org/get"
}

Путем внедрения Downloader Middleware и использованияprocess_request()Метод успешно устанавливает случайный User-Agent.

Кроме того, Downloader Middleware также имеетprocess_response()метод. После того, как загрузчик загрузит запрос, он получит ответ, а затем механизм Scrapy отправит ответ обратно в паук для обработки. Но прежде чем ответ будет отправлен пауку, мы также можем использоватьprocess_response()Метод обрабатывает ответ. Например, измените код состояния ответа здесь, вRandomUserAgentMiddlewareДобавьте следующий код:

def process_response(self, request, response, spider):
    response.status = 201
    return response

мы будемresponseПеременнаяstatusАтрибут изменяется на 201, а затемresponseВернитесь, измененный ответ будет отправлен пауку.

Затем мы выводим измененный код состояния в Spider, вparse()Добавьте в метод следующий оператор вывода:

self.logger.debug('Status Code: ' + str(response.status))

После повторного запуска консоль выводит следующее:

[httpbin] DEBUG: Status Code: 201

Можно обнаружить, что код состояния Response был успешно изменен.

Поэтому, если вы хотите постобработать ответ, вы можете воспользоваться помощьюprocess_response()метод.

Еще одинprocess_exception()метод, который используется для обработки исключений. Мы можем вызвать этот метод, если нам нужна обработка исключений. Однако частота использования этого метода относительно невелика, и здесь не приводится пример.

В-четвертых, код этого раздела

Исходный код этого раздела: https://github.com/Python3WebSpider/ScrapyDownloaderTest.

V. Заключение

В этом разделе объясняется основное использование ПО промежуточного слоя Downloader. Этот компонент очень важен и является ядром обработки исключений и обработки, препятствующей восхождению. Позже мы применим этот компонент в реальном бою для работы с прокси, куками и другим контентом.