bigywater.tech
Scrapy · 44

下载处理器:http、https、file 的分派

请求最终是谁发出去的?file://、data: 是怎么处理的?能不能自己加一个协议?把 HTTP 处理器换成 aiohttp 会有什么不同?这篇从 DOWNLOAD_HANDLERS 讲起,把几种处理器的行为实测一遍。

6 个
DOWNLOAD_HANDLERS_BASE 里的协议:data、file、ftp、http、https、s3
48 → 4
换成 AiohttpDownloadHandler 后,每域名并发 4 的服务器峰值(默认处理器是 48)
None
把某协议的处理器设成它就禁用该协议

协议映射

实测 · 本机执行输出
  {'data': 'scrapy.core.downloader.handlers.datauri.DataURIDownloadHandler',
   'file': 'scrapy.core.downloader.handlers.file.FileDownloadHandler',
   'ftp': 'scrapy.core.downloader.handlers.ftp.FTPDownloadHandler',
   'http': 'scrapy.core.downloader.handlers.http11.HTTP11DownloadHandler',
   'https': 'scrapy.core.downloader.handlers.http11.HTTP11DownloadHandler',
   's3': 'scrapy.core.downloader.handlers.s3.S3DownloadHandler'}

每个 URL 协议对应一个处理器类;DOWNLOAD_HANDLERS 覆盖或追加基础映射,值为 None 禁用。

本地协议

实测 · 本机执行输出
  …hello.html                  HtmlResponse  status=200 38 字节 头={} 文本='<html><title>local f'
  …blob.bin                    Response      status=200 256 字节 头={} 文本=None
  …missing.html                错误 FileNotFoundError: [Errno 2] No such file or directory: '/Users/w/Des
  …plain;charset=utf-8;base64  TextResponse  status=200 23 字节 头={} 文本='数据 URI 里的中文'
  …data:,hello%20world         TextResponse  status=200 11 字节 头={} 文本='hello world'
  …h1%3E                       HtmlResponse  status=200 10 字节 头={} 文本='<h1>x</h1>'
  …s44b                        错误 IsADirectoryError: [Errno 21] Is a directory: '/Users/w/Desktop/zen/t

file:// 直接读本地文件,data: 解析 URI,都不走网络和下载槽;响应类按扩展名或 MIME 判定,file 响应没有头。

自定义协议

实测 · 本机执行输出
  {'e': 'handler 被调用', 'url': 'myproto://some/resource'}
  {'who': 'callback', 'url': 'myproto://some/resource', 'status': 200, 'text': 'hello from myproto: some/resource'}
  {'who': 'errback', 'url': 'ftp://example.com/x', 'exc': "NotSupported: Unsupported URL scheme 'ftp': no handler available for that scheme"}

继承 BaseDownloadHandler 并实现 async download_request(request) 返回 Response 即可注册新协议。

换 HTTP 处理器

实测 · 本机执行输出
  默认 HTTP11DownloadHandler:48 个各 0.2 s 的请求、每域名并发 4 → 服务器并发峰值 48,总耗时 0.27 s;response.ip_address=127.0.0.1,protocol=HTTP/1.1,certificate=None
  AiohttpDownloadHandler:48 个各 0.2 s 的请求、每域名并发 4 → 服务器并发峰值 4,总耗时 2.52 s;response.ip_address=127.0.0.1,protocol=HTTP/1.1,certificate=None
      警告:WARNING: AiohttpDownloadHandler is experimental an
  aiohttp 处理器 + meta bindaddress=("127.0.0.1", 0):[{'who': 'callback'}];日志 ["ERROR: The 'bindaddress' request meta key is not supported by AiohttpDownloadHandler and will be ignored."]
  aiohttp 处理器 + meta proxy="http://127.0.0.1:1":[{'who': 'errback', 'exc': 'DownloadConnectionRefusedError', 'msg': 'Cannot connect to host 127.0.0.1:1 ssl:<ssl.SSLContext object at 0x…> [C'}];日志 []

AiohttpDownloadHandler(实验性)自带连接数限制,所以每域名并发限制生效;不支持 bindaddress,会记 ERROR 并忽略。

分步演示

下载处理器
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Scrapy 2.19.0 / Twisted 26.4.0 / lxml 6.1.3 / parsel 1.12.1(临时虚拟环境);所有请求只发给本机自己起的测试服务器(127.0.0.1,端口 9960 以上,不访问任何真实网站);服务器可以按实验需要制造延迟、重定向、压缩、断连等行为,并记录收到的原始请求;每个实验在临时目录里新建 Scrapy 项目或单文件 spider,命令输出来自真实的 scrapy 进程;计时用 time.perf_counter,数字每次运行会有小幅抖动。file:// 与 data: 用临时文件和内联 URI;自定义处理器是一个返回 TextResponse 的最小实现;处理器对比用本机服务器每个请求延迟 0.2 s,统计服务器并发峰值
    • 没有验证:_httpx 处理器(需要另装 httpx / httpcore2,没有装);FTP、S3 处理器(没有可用的服务器);HTTP/2 处理器(见下一篇);处理器之间的响应头、cookie、压缩、重定向行为差异的全面对比(只比了并发限制、ip_address、protocol);处理器的 lazy 属性与 close() 的行为
    京ICP备2026067502号-1京公网安备11010502063487号