2222def _ensure_known_request_class (request_dict : dict [str , Any ]) -> None :
2323 """Validate the optional `_class` entry before `request_from_dict` resolves it.
2424
25- `scrapy.utils.request.request_from_dict` resolves a `_class` entry via `load_object`, which
26- imports the dotted path it is given. To keep reconstruction self-contained — importing nothing
27- that the running spider has not already imported — we only accept a `_class` that is already
28- present in `sys.modules` and is a `scrapy.Request` subclass.
29-
30- A spider that reads its own requests always has its request classes imported by the time the
31- requests are reconstructed, so this does not restrict legitimate use.
25+ `request_from_dict` imports the `_class` dotted path via `load_object`. To avoid importing
26+ anything the running spider has not already imported, only a `_class` already present in
27+ `sys.modules` and subclassing `scrapy.Request` is accepted. A spider reading its own requests
28+ always has those classes imported by then, so legitimate use is unaffected.
3229 """
3330 class_path = request_dict .get ('_class' )
3431 if class_path is None :
@@ -66,12 +63,8 @@ def to_apify_request(scrapy_request: ScrapyRequest, spider: Spider) -> ApifyRequ
6663
6764 # Configuration to behave as similarly as possible to Scrapy's default RFPDupeFilter.
6865 #
69- # `payload` carries the request body, which is used both for platform processing and for
70- # computing the extended unique key. The body is also part of the serialized Scrapy request
71- # stored further below, where it is needed to faithfully reconstruct the request. Both copies
72- # originate from `scrapy_request.body` and are kept intentionally: dropping `payload` would
73- # change deduplication, and dropping the serialized copy would couple reconstruction to the
74- # Apify payload.
66+ # The body is stored twice on purpose: as `payload` (used for the extended unique key) and inside
67+ # the serialized Scrapy request below (used to reconstruct it). Both come from `scrapy_request.body`.
7568 request_kwargs : dict [str , Any ] = {
7669 'url' : scrapy_request .url ,
7770 'method' : scrapy_request .method ,
@@ -104,11 +97,9 @@ def to_apify_request(scrapy_request: ScrapyRequest, spider: Spider) -> ApifyRequ
10497
10598 request_kwargs ['user_data' ] = user_data if isinstance (user_data , dict ) else {}
10699
107- # Convert Scrapy's headers to HttpHeaders and store them on the apify_request. This is only
108- # the Apify-platform-level view of the headers; the authoritative copy, with exact bytes,
109- # travels inside the serialized scrapy_request below. `to_unicode_dict()` decodes as UTF-8
110- # and raises on non-UTF-8 header values, so it is guarded: a request with binary headers
111- # keeps them in the serialized payload instead of being dropped entirely.
100+ # Store an Apify-platform view of the headers. The authoritative copy with exact bytes
101+ # travels in the serialized scrapy_request below, so non-UTF-8 headers (which make
102+ # `to_unicode_dict()` raise) are tolerated rather than dropping the whole request.
112103 if isinstance (scrapy_request .headers , Headers ):
113104 try :
114105 headers = cast ('dict[str, str]' , dict (scrapy_request .headers .to_unicode_dict ()))
@@ -130,12 +121,9 @@ def to_apify_request(scrapy_request: ScrapyRequest, spider: Spider) -> ApifyRequ
130121 logger .warning (f'Conversion of Scrapy request { scrapy_request } to Apify request failed; { exc } ' )
131122 return None
132123
133- # Serialize the Scrapy request and store it (base64-encoded JSON) under 'scrapy_request' in the
134- # Apify request's user data. This is intentionally outside the broad except above so that a
135- # non-JSON-serializable value in `meta`/`cb_kwargs` is reported loudly rather than hidden as a
136- # generic warning. The failure is logged with a full traceback and the request is skipped (None
137- # is returned, honoring this function's contract) instead of crashing the whole crawl. See
138- # `_serialization` for the encoding details.
124+ # Serialize the Scrapy request as base64-encoded JSON under 'scrapy_request'. Kept outside the
125+ # broad except above so a non-JSON-serializable `meta`/`cb_kwargs` is logged with a traceback and
126+ # the request skipped (returning None per this function's contract), rather than crashing the crawl.
139127 try :
140128 scrapy_request_json = encode_to_json (scrapy_request_dict )
141129 except TypeError :
0 commit comments