bigywater.tech
Scrapy · 28

HttpProxyMiddleware:环境变量代理与 request.meta 里的 proxy

Scrapy 怎么用代理?环境变量和 meta 谁优先?代理 URL 里的用户名密码怎么发?https 怎么走?这篇用一个能记录原始请求的假代理,看代理实际收到的每一个字节。

绝对 URI
经代理的 http 请求的请求行写法(GET http://源站/路径 HTTP/1.1)
CONNECT :443
https 请求经代理时先发的请求,带 Proxy-Authorization
407
代理要求认证时的结果:HttpError,不重试

meta["proxy"]

实测 · 本机执行输出
  代理收到:GET http://127.0.0.1:9961/a HTTP/1.1
      Host=127.0.0.1:9961  Proxy-Authorization=(无)
  代理收到:GET http://127.0.0.1:9961/b HTTP/1.1
      Host=127.0.0.1:9961  Proxy-Authorization=Basic dXNlcjpwQHNzOnc= → 解码 'user:p@ss:w'
  源站直接收到的请求:[](经过代理的请求没有直连源站);回调拿到的正文:['<html>via-proxy</htm', '<html>via-proxy</htm']

请求的 meta["proxy"] 指向代理 URL;代理 URL 里的用户名密码被转成 Proxy-Authorization: Basic,先做百分号解码。

环境变量

实测 · 本机执行输出
  http_proxy=http://127.0.0.1:<代理>:代理收到 ['env', 'meta-override'](含 meta-override);源站直接收到 ['/meta-none'](meta-none 绕过了环境变量代理)
  只设 https_proxy:代理收到 ['meta-override'];源站直接收到 ['/env', '/meta-none'](http 请求不走 https_proxy;meta-override 仍走 meta 里指定的代理)
  http_proxy + no_proxy=127.0.0.1:代理收到 ['meta-override'];源站直接收到 ['/env', '/meta-none'](命中 no_proxy 的主机不走环境变量代理,meta 指定的仍走)
  只设大写 HTTP_PROXY:代理收到 ['env', 'meta-override'];源站直接收到 ['/meta-none']
  HTTPPROXY_ENABLED=0:代理收到 ['meta-override'];源站直接收到 ['/env', '/meta-none']

中间件初始化时读取 http_proxy、https_proxy、no_proxy(大小写均可),按请求协议选代理;meta["proxy"] 优先,设为 None 则这个请求直连。

关闭中间件

实测 · 本机执行输出
  代理收到:CONNECT secure.example.test:443 HTTP/1.1;Host=secure.example.test:443;Proxy-Authorization 解码='alice:s3cret';Proxy-Connection=(无)
  回调/errback:[{'who': 'errback', 'exc': 'scrapy.exceptions.DownloadFailedError', 'msg': '[<twisted.python.failure.Failure twisted.internet.error.ConnectionLost: Connecti', 't': 342.4}]

HTTPPROXY_ENABLED = 0 只让环境变量代理失效,请求 meta 里的 proxy 依然会被下载处理器使用。

https 与 407

实测 · 本机执行输出
  事件 [{'who': 'errback', 'exc': 'HttpError', 'status': 407}];代理收到 1 次;源站收到 []

https 先发 CONNECT 建立隧道;代理要求认证返回 407 时按普通的 4xx 处理。

分步演示

代理
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动。假代理是原始套接字服务器,记录请求行与头;普通请求回一个固定页面,CONNECT 回 200 后关闭连接,另有一个一律回 407 的模式;环境变量实验里用子进程环境显式增删 http_proxy 等变量(并清掉 pl 默认设置的 NO_PROXY)
    • 没有验证:SOCKS 代理(Scrapy 默认不支持);真实 HTTPS 站点经过真实 CONNECT 隧道的成功路径(本机没有可验证的证书链);代理失败后的重试与轮换;macOS 系统代理设置(getproxies 在没有环境变量时会读系统配置,这里的实验都先清掉了环境变量);重定向跨协议时代理凭据的处理(源码里有 _scheme_proxy / _auth_proxy 逻辑,没有测)
    京ICP备2026067502号-1京公网安备11010502063487号