首页 > 解决方案 > Scrapy 自定义 DownloadMiddleware 不尊重 DOWNLOAD_DELAY

问题描述

我目前正在研究我的 CustomDownloadMiddleware。这主要是由于对下载网页的控制有一定的需要。我的 CustomDownloadMiddleware 看起来如下

class MySeleniumDownloadMiddleware:
    """Scrapy middleware handling the requests using selenium"""

    def __init__(self, driver):
        self.driver = driver
        self.cookies = self.driver.get_cookies()

    @classmethod
    def from_crawler(cls, crawler):
        """Initialize the middleware with the crawler settings"""

        driver = init_chromium(crawler.settings.get('SELENIUM_HOSTNAME'))
        login(driver, crawler.settings.get('MY_CREDENTIAL'))

        middleware = cls(driver=driver)

        crawler.signals.connect(middleware.spider_closed, signals.spider_closed)

        return middleware


    def process_request(self, request, spider):
        """Process a request using the selenium driver if applicable"""

        try:
            self.driver.get(request.url)
        except WebDriverException:
            self.driver = init_chromium(spider.settings.get('SELENIUM_HOSTNAME'))
            recover_cookie_to_driver(self.driver, self.cookies)
            self.driver.get(request.url)

        body = str.encode(self.driver.page_source)

        # Expose the driver via the "meta" attribute
        request.meta.update({'driver': self.driver})

        return HtmlResponse(
            self.driver.current_url,
            body=body,
            encoding='utf-8',
            request=request
        )


    def spider_closed(self):
        """Shutdown the driver when spider is closed"""
        try:
            self.driver.quit()
        except WebDriverException:
            pass

在github打开一个issue后,发现问题是需要自定义下载中间件自己处理下载延迟,推荐使用这个文件中的slot 。但是,我没有找到下载中间件中使用的插槽的任何相关示例。

另外,值得一提的是,我的代码受到了scrapy-selenium的极大启发,并且发现了一个为下载延迟提供潜在解决方案的问题,但是当我使用time.sleep(second)解决方案时,管道和蜘蛛的parse功能没有同时处理,这我认为解决方案不可行。

是否有适当的方法在slot下载中间件中实现,以尊重下载延迟,并且不会妨碍管道和抓取同时处理的能力?如果是,那是什么?

标签: pythonscrapy

解决方案


我想我找到了这种行为的原因。
看起来您的请求未到达使用下载器相关设置(包括DOWNLOAD_DELAY)的下载器方法。
在一般情况下(根据scrapy achitecture)会发生这种情况:

  1. Downloader 中间件process_request修改请求并 - 向 Downloader 发送请求。

    下载器:将请求分配给从步骤 4 执行请求
    的下载器插槽。(计算下载器相关设置)。收到响应 -> 发送到方法(scrapy 架构方案的第 5 步)

    process_response

  2. 下载器中间件process_request- 修改响应并 - 将响应发送到engine.

你在你的情况下的行为会有所不同:

  1. 作为中间件方法的结果,下载器中间件process_requestHttpresponse对象发送到下载器(而不是请求)。下载器:不会执行请求 - 因为它已经收到上一步的响应。它将立即向方法发送响应。 5.(同上)下载器中间件- 修改响应并 - 将响应发送到.
    process_request



    process_response

    process_requestengine

此行为编码为:

  • 下载器中间件管理器download方法。
  • 下载器fetch方法(DownloaderMiddlewareManager.download 被调用的地方)

在目前的 scrapy 下载器实现中 -DOWNLOAD_DELAY以及其他一些与下载器相关的设置仅适用于非 selenium 请求。


推荐阅读