文章

urllib.request 中代理的问题(不一定正确,若完全理解后再来改)

前言

python中我们时常因为开了代理,导致pip不了,或者爬虫request报错,最简单的方法就是关掉代理,但是如果有些网址不开代理上不去怎么办呢?这导致我不得不去深入了解一下。

解决方案

首先,先说结论,应该是因为Windows并不支持 https 代理,仅支持 http (网上还有说socket,这个先不管),所以使用 https 会导致目标网站的协议和代理服务器的协议不一致,那就把访问网站的协议从https改成 http 就行

1
HTTPS_PROXY=http://proxy_ip:proxy_port

注意,我这里原本在想可不可以直接用 http 访问网页,事实证明,有些可以,但有些出错,可能和对方网页服务器设置方式有关,猜测可能 有时候直接这样访问会自动又跳回 https 协议。

这里再引用一下知乎一位大佬的解释:

以前 urllib3 其实并不支持 https 代理,也就是说代理服务器的地址虽然大家配置的是 https,但是一直都是悄无声息地就按照 http 连接的,刚好代理服务器确实也只支持 http,所以皆大欢喜。

现在 urllib3 要支持 https 代理了,那么既然配置代理是 https 就尝试用 https 的方式去连接,但是由于代理服务器其实只支持 http,所以没法处理请求,ssl 握手阶段就出错了。

注意,这里的 https 是指代理服务器自己的,和我们要访问的目标网站无关。

具体配置

一、代码层面解决

1
2
3
4
5
6
7
url = 'https://github.com/'
proxies={
    'http': 'http://127.0.0.1:10809', 
    'https': 'http://127.0.0.1:10809'  # https -> http
}

r = requests.get(url, proxies=proxies)

还可以继续看一下系统代理服务器配置具体是什么

1
2
3
>>> from urllib.request import getproxies
>>> getproxies()
{'http': 'http://127.0.0.1:10809', 'https': 'https://127.0.0.1:10809', 'ftp': 'ftp://127.0.0.1:10809'}

二、一劳永逸

在 Windows 系统中,先从环境变量获取,如果没有则从注册表获取。

所以这里分两种解决方式,改环境变量,或者改系统配置

1. 环境变量

这里直接抄了

zhihu_env_var

结果:

1
2
>>> getproxies()
{'https': 'http://127.0.0.1:7890', 'http': 'http://127.0.0.1:7890'}

一旦设置了环境变量,程序就直接从环境变量获取,系统的配置也就失效了。

2.改系统配置

1
http=http://127.0.0.1:10809;https=http://127.0.0.1

from:
网络和Internet设置
to:
更改配置

1
2
>>> requests.get("https://www.google.com")
>>> <Response [200]>

其中最后的端口只对最后面的那个地址有效,分号前面的地址需要加上端口。

结尾

事实上,不是说一定要改成http,主要要看代理服务器支持的协议

Since the proxy server is http or https is nothing about the target url. So in Windows, when we config the proxy 127.0.0.1 and 7890,it should means:

1
2
3
4
5
# http proxy
proxies={
'http': 'http://127.0.0.1:7890',
'https': 'http://127.0.0.1:7890' 
}

If the proxy is https, we should config the address manually: https://127.0.0.1 ,and the result should be:

1
2
3
4
5
# https proxy
proxies={
'http': 'https://127.0.0.1:7890',
'https': 'https://127.0.0.1:7890' 
}

就是说,是https的代理服务器就得都改成https,是http的代理服务器就都得改成http,而我们的Windows就是http的,至于为什么不支持https,猜测可能和SSL证书有关?

参考链接

Python 遭遇 ProxyError 问题记录 - 知乎 (zhihu.com)

https://github.com/psf/requests/issues/5740

Pip 20.3+ break proxy connection · Issue #9216 · pypa/pip (github.com)

本文由作者按照 CC BY 4.0 进行授权