Source code for scrapy.spidermiddlewares.metacopy

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from scrapy.spidermiddlewares.base import BaseSpiderMiddleware

if TYPE_CHECKING:
    from scrapy.crawler import Crawler
    from scrapy.http import Request, Response


logger = logging.getLogger(__name__)


[docs] class MetaCopyDetectionMiddleware(BaseSpiderMiddleware): """Warn when a spider yields a request with internal meta keys that should not be copied from response.meta. Each warning is emitted at most once per crawl. """ _INTERNAL_KEYS: frozenset[str] = frozenset( { "_auth_proxy", "_dont_cache", "_scheme_proxy", "download_latency", "redirect_reasons", "redirect_times", "redirect_ttl", "redirect_urls", "retry_times", } ) def __init__(self, crawler: Crawler) -> None: super().__init__(crawler) skip = frozenset(crawler.settings.getlist("META_COPY_WARN_SKIP_KEYS", [])) self._keys: frozenset[str] = self._INTERNAL_KEYS - skip self._warned: bool = False def get_processed_request( self, request: Request, response: Response | None ) -> Request | None: if response is None: return request if not self._warned: found = self._keys & request.meta.keys() if found: spider_name = type(self.crawler.spider).__name__ logger.warning( f"{spider_name} yielded a request containing internal " f"meta keys that were likely copied from response.meta " f"and should not be forwarded to new requests: " f"{sorted(found)}. See the MetaCopyDetectionMiddleware " f"documentation for more information. Source response: " f"{response}, target request: {request}", extra={"spider": self.crawler.spider}, ) self._warned = True return request