V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Koril
V2EX  ›  问与答

关于 Clash、curl、 Python pip/requests/httpx 代理的一些困惑

  •  
  •   Koril · 41 天前 · 1020 次点击
    这是一个创建于 41 天前的主题,其中的信息可能已经有所发展或是发生改变。

    各位大佬,有个关于代理的困惑望解答。

    问题描述

    当我打开 Clash 后,curl/pip/requests/httpx 的工作结果让有些困惑。

    环境

    1. 系统:Windows 10/11

    2. Python:3.8.0

    curl

    通过 curl 请求 baidu 和 google:

    curl 'https://www.baidu.com'
    

    请求 baidu ,开不开 Clash 都可以正常访问,符合预期。

    curl 'https://www.google.com'
    

    请求 google ,不开 Clash 肯定访问不了,符合预期

    开了 Clash 后,需要指定 curl 的 proxy 参数,可以访问,符合预期

    curl -x 'http://localhost:7890' 'https://www.google.com' 
    

    我猜 curl 默认应该是直接请求谷歌服务器,只有指定了 proxy 参数后,curl 才走代理请求。

    pip

    接下来 pip 的行为开始让我感到困惑。

    为了避免每次 pip install 都要使用 -i 参数指定清华源,我在 pip 全局配置设置了清华源。

    这就开始出现了一种诡异的事情:我开着 Clash ,在 google 上搜索着问题,然后用 pip 拉包,结果 pip 报错了。

    然后我把 Clash 系统代理关闭,google 是搜不了了,但是 pip 成功的从清华源那里拉到了包。

    这样频繁的开关很麻烦,我就搜索解决方法,网上的解决方案是:

    Clash 设置 -> 系统代理 -> 绕过域/网络 -> 加上一行 pypi.tuna.tsinghua.edu.cn

    我通过这篇文章了解到了绕过的目的: https://clashyun.com/195.html

    第一个问题:我的理解是清华源禁止了境外的访问请求,请问这样理解对么?

    requests 和 httpx

    当开着 Clash 系统代理时,使用这样两个库访问 baidu 就很奇怪。

    curl 可以正常访问百度。

    但是 requests 就没法访问百度:

    response_0 = requests.get('https://www.baidu.com')
    print(response_0.status_code)
    

    爆出以下错误:

    HTTPSConnectionPool(host='www.baidu.com', port=443): Max retries exceeded with url: / (Caused by ProxyError('Unable to connect to proxy', FileNotFoundError(2, 'No such file or directory')))
    

    同样的 httpx:

    httpx 'https://www.baidu.com'
    

    也会报错:

    ConnectError: [Errno 0] Error
    

    网上的解决方案是,设置 proxy 参数:

    1. 走代理
    proxies_1 = {
      'http': 'http://127.0.0.1:7890',
      'https': 'http://127.0.0.1:7890',
    }
    response_1 = requests.get('https://www.baidu.com', proxies=proxies_1)
    print(response_1.status_code)
    
    1. 不走代理
    proxies_2 = {
      'http': None,
      'https': None,
    }
    response_2 = requests.get('https://www.baidu.com', proxies=proxies_2)
    print(response_2.status_code)
    

    httpx 指定了 proxy 参数后可以访问 baidu

    httpx 'https://www.baidu.com' --proxy 'http://localhost:7890'
    

    第二个问题:为什么开了 Clash 后,requests 和 httpx 都必须显示的指定 proxy 才可以请求 baidu ?


    我对 Clash 、代理、requests 这些了解的很浅,希望能给些思路,如果有参考的文章也很感谢,想弄弄明白。

    14 条回复    2024-09-27 21:44:40 +08:00
    proxytoworld
        1
    proxytoworld  
       41 天前
    很疑惑为什么会有这么抽象的问题

    你浏览器设置代理指向 clash ,clash 不要启动系统代理,pip 读取的是系统代理。
    proxytoworld
        2
    proxytoworld  
       41 天前   ❤️ 1
    pip 走代理会有什么域名解析类还是证书错误,反正 pip 使用的时候不要设置系统代理
    proxytoworld
        3
    proxytoworld  
       41 天前
    ➜ ~ export https_proxy=127.0.0.1:10809
    ➜ ~ export http_proxy=127.0.0.1:10809
    ➜ ~ curl https://www.google.com
    <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
    <TITLE>302 Moved</TITLE></HEAD><BODY>
    <H1>302 Moved</H1>
    The document has moved
    <A HREF="https://www.google.com.hk/url?sa=p&amp;hl=zh-CN&amp;pref=hkredirect&amp;pval=yes&amp;q=https://www.google.com.hk/&amp;ust=1727422428210314&amp;usg=AOvVaw2nstpFlwc2FIx4X1WAqdF-">here</A>.
    </BODY></HTML>
    ➜ ~ python3
    Python 3.8.10 (default, Nov 26 2021, 20:14:08)
    [GCC 9.3.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import requests
    >>> r=requests.get('https://www.baidu.com')
    >>> r.status_code
    200
    >>>
    Koril
        4
    Koril  
    OP
       41 天前
    @proxytoworld hhh 抽象么,可能因为我是个初学者,提的问题比较奇怪。
    SenLief
        5
    SenLief  
       41 天前
    不要用系统代理,直接 tun
    proxytoworld
        6
    proxytoworld  
       41 天前
    你都会写代码了,浏览器弄个 proxyswitchomega 切换代理不就行了
    body007
        7
    body007  
       41 天前
    这就是我讨厌全局或系统代理的原因。我只用 socks5 或 http 代理服务器,那些程序要走代理特殊配置,这样其他软件铁定不走代理,清楚明了。
    pweng286
        8
    pweng286  
       41 天前
    虽然我看不懂但是问题描述的很有条理.
    我有时候想提问都不知道怎么表达哈哈哈哈
    Abbeyok
        9
    Abbeyok  
       41 天前 via iPhone
    python 进行网络请求就是有这个问题,所以能不开系统代理就不开,直接开 tun 模式
    Koril
        10
    Koril  
    OP
       41 天前
    感谢大家的回复,晚上回家翻了下源码,这里我自问自答下:

    关于第二个问题:为什么 Windows 开了 Clash 的系统代理,使用 requests 如果不显示的设置 proxies 这个参数(无论是方法传参,还是设置环境变量)就无法请求的问题。

    requests 的 sessions 模块的 merge_environment_settings() 方法调用了 Python 自带的 urllib 库中的 request 模块的 getproxies() 方法。

    似乎顺序是这样的:方法传参 > 环境变量 > 注册表

    如果方法没传参,环境变量也没有设置 http/https_proxy 的话,代码走到以下 elif 块中,去读 Windows 的注册表:

    ```
    elif os.name == 'nt':
    def getproxies_registry():
    """Return a dictionary of scheme -> proxy server URL mappings.

    Win32 uses the registry to store proxies.

    """
    # 省略部分代码
    try:
    # 查询 win 注册表
    internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
    r'Software\Microsoft\Windows\CurrentVersion\Internet Settings')
    proxyEnable = winreg.QueryValueEx(internetSettings,
    'ProxyEnable')[0]
    if proxyEnable:
    # Returned as Unicode but problems if not converted to ASCII
    proxyServer = str(winreg.QueryValueEx(internetSettings,
    'ProxyServer')[0])
    if '=' in proxyServer:
    # Per-protocol settings
    for p in proxyServer.split(';'):
    protocol, address = p.split('=', 1)
    # See if address has a type:// prefix
    if not re.match('^([^/:]+)://', address):
    address = '%s://%s' % (protocol, address)
    proxies[protocol] = address
    else:
    # Use one setting for all protocols
    if proxyServer[:5] == 'http:':
    proxies['http'] = proxyServer
    else:
    proxies['http'] = 'http://%s' % proxyServer
    proxies['https'] = 'https://%s' % proxyServer
    proxies['ftp'] = 'ftp://%s' % proxyServer
    internetSettings.Close()


    return proxies
    ```
    Koril
        11
    Koril  
    OP
       41 天前
    然后就读取到了 ProxyServer 127.0.0.1:7890 这个键值对,然后在末尾的 else 块中,擅自加上了 https ,最后返回的 proxy 变成了:
    {
    'ftp': 'ftp://127.0.0.1:7890',
    'http': 'http://127.0.0.1:7890',
    'https': 'https://127.0.0.1:7890'
    }
    而 Clash 的代理是 http 代理,所以第三个键值对 https: https://127.0.0.1:7890 会引发 ProxyError 异常,显示无法连接到该代理,正确的键值对应该是 https: http://127.0.0.1:7890 。

    这是我目前的一点点理解。
    Koril
        12
    Koril  
    OP
       41 天前
    @proxytoworld 好的
    Koril
        13
    Koril  
    OP
       41 天前
    @Abbeyok ok ,我去了解下什么是 tun 模式
    Koril
        14
    Koril  
    OP
       41 天前
    补充:Clash 打开系统代理,注册表的 ProxyEnable 变成 1 ,反之为 0 ,urllib.request 的 getproxies_registry() 就是拿这个变量来判断的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1110 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 18:58 · PVG 02:58 · LAX 10:58 · JFK 13:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.