Python requests 怎么设置代理?实测教程和常见坑

用 Python requests 设置代理:proxies 字典、账号密码、SOCKS5、轮换 IP 和报错处理,以及系统代理覆盖 session.proxies、Session 复用连接两个坑。

教程发布于 2026年10月25日约 4 分钟

本页目录

结论先说: 用 Python 的 requests 走代理,就是传一个同时包含 "http" 和 "https" 两个键的 proxies 字典:requests.get(url, proxies={"http": proxy, "https": proxy}),其中 proxy 写成 http://USERNAME:PASSWORD@HOST:PORT。漏掉 "https" 键,所有 https:// 请求都会悄悄绕开代理。我们实测还发现两个容易踩的坑:系统代理设置会覆盖 session.proxies;Session 会复用同一条连接,轮换代理在这些请求之间没法换 IP。

安装

bash
pip install requests
pip install "requests[socks]"   # 只有用 SOCKS5 代理时才需要

通过代理发送请求

python
import requests

proxy = "http://USERNAME:PASSWORD@HOST:PORT"
proxies = {"http": proxy, "https": proxy}

response = requests.get("https://ipinfo.io/json", proxies=proxies, timeout=15)
response.raise_for_status()
print(response.json())

运行后会输出一个字典,包含出口 ip、city、country 和 org,也就是网站看到的信息。一定要设置 timeout,否则代理没有响应时,请求可能一直等下去。

为什么两个键都要写

两个键都写时,http://example.com 和 https://example.com 都走代理;只写 http 键时,http://example.com 走代理,https://example.com 直连,用的是你自己的 IP,而且不会报错
requests 按请求的网址选键。缺少键就不走代理,也不会报错。

字典里的键对应的是你请求的网址的协议,而不是代理的类型。我们测试时,用 proxies={"http": proxy} 请求 https:// 网址,页面正常返回,但测试代理的日志里什么都没有:请求直接从我们自己的 IP 发出去了。两个键都要写,指向同一个代理。

密码里有特殊字符

账号密码写在代理地址里,所以 @、:、/、# 这些字符必须转义,否则地址会在错误的位置被拆开。交给 Python 处理:

python
from urllib.parse import quote

import requests

username = "USERNAME"
password = quote("PASSWORD", safe="")  # encodes @ : / # and other special characters
proxy = f"http://{username}:{password}@HOST:PORT"

session = requests.Session()
session.proxies = {"http": proxy, "https": proxy}

for _ in range(3):
    print(session.get("https://ipinfo.io/ip", timeout=15).text.strip())

我们用密码 p@ss:word 测试,quote 把它转成了 p%40ss%3Aword。在照搬这种 Session 写法之前,请先看下面两节。

坑一:系统代理会覆盖 session.proxies

requests 用哪个代理,从上往下先找到的生效:请求里的 proxies= 参数一定生效;其次是环境变量和系统代理,即 HTTPS_PROXY 或 macOS、Windows 的代理设置,除非 trust_env 为 False;最后才是 session.proxies,上面都没有时才生效
请求里的 proxies= 参数总是生效;session.proxies 会输给环境变量和系统代理。

requests 还会读取环境里的代理:HTTPS_PROXY、HTTP_PROXY、NO_PROXY 环境变量,以及 macOS 和 Windows 的系统代理设置,而且它们的优先级高于 session.proxies。我们的测试电脑开着系统代理,上面的 Session 示例完全没有经过测试代理,也没有任何报错。把 HTTPS_PROXY 指向一个关闭的端口后,同一个 Session 直接报错;而在请求里直接传 proxies= 的写法仍然用的是正确的代理。

不管电脑怎么设置,都让 Session 走你的代理,有两种办法:

python
session.get(url, proxies=proxies, timeout=15)   # pass proxies= on every request
session.trust_env = False                       # or ignore environment and system proxies

trust_env = False 还会让 Session 不再读取 .netrc 里的账号和 REQUESTS_CA_BUNDLE 设置。如果你依赖这两项,就用第一种写法。

坑二:Session 复用一条连接,也就是一个 IP

通过代理发 3 次 HTTPS 请求:requests.get 三次建立 3 条隧道,最多 3 个 IP;Session.get 三次复用 1 条隧道,只有 1 个 IP;Session.get 三次并加上 Connection: close 请求头,建立 3 条隧道,最多 3 个 IP
按测试代理的日志计数:Session 的所有请求复用同一条隧道。

访问 https:// 网站时,requests 先让代理建一条隧道,再在隧道里发请求。Session 会保持并复用这条隧道:测试代理记录到,三次 session.get() 只建了一条隧道,三次普通的 requests.get() 建了三条。隧道里传的是加密数据,代理分不清哪里是一个请求,所以轮换代理只有在新连接建立时才能换 IP。

  • 想要每次请求换 IP(轮换路由):不用 Session,直接 requests.get();或者设置 session.headers["Connection"] = "close",让每次请求单独建隧道(我们测试时是三条隧道)。
  • 想让整个任务保持一个 IP(比如登录后再访问几个页面):用粘性路由。复用连接有帮助,但粘性会话即使连接重建也能保持同一个 IP。

两种会话的区别见轮换会话和粘性会话的区别。

SOCKS5 代理

python
import requests

proxy = "socks5h://USERNAME:PASSWORD@HOST:PORT"
proxies = {"http": proxy, "https": proxy}

print(requests.get("https://ipinfo.io/ip", proxies=proxies, timeout=15).text.strip())

需要先安装 requests[socks]。用 socks5h://:我们的测试代理收到的是域名,DNS 查询在代理那一端完成;用 socks5:// 时,你的电脑会先自己查出地址。什么时候值得用 SOCKS5,见 SOCKS5和HTTP代理的区别。

处理代理报错

python
import requests

proxy = "http://USERNAME:PASSWORD@HOST:PORT"
proxies = {"http": proxy, "https": proxy}

try:
    response = requests.get("https://ipinfo.io/json", proxies=proxies, timeout=(5, 20))
    response.raise_for_status()
    print(response.json()["ip"])
except requests.exceptions.ProxyError as error:
    print("Proxy refused or unreachable:", error)
except requests.exceptions.ConnectTimeout:
    print("Timed out connecting to the proxy")
except requests.exceptions.HTTPError as error:
    print("The website answered with an error:", error.response.status_code)

timeout=(5, 20) 表示连接最多等 5 秒,读取最多等 20 秒。账号密码错误和代理连不上都会报 ProxyError,具体原因看后面的信息。密码错误时,我们得到的是:

text
ProxyError('Unable to connect to proxy', OSError('Tunnel connection failed: 407 Proxy Authentication Required'))

端口没有开放时:

text
ProxyError('Unable to connect to proxy', NewConnectionError("… Failed to establish a new connection: [Errno 61] Connection refused"))

407 是代理拒绝了你的账号密码,排查方法见代理服务器是什么里的连接信息说明和新手入门。

httpx 和 aiohttp

另外两个常用的 HTTP 库,同样用测试代理跑通了。

httpx 用一个 proxy 参数(httpx 0.28 已经去掉了旧的 proxies 参数):

python
import httpx

proxy = "http://USERNAME:PASSWORD@HOST:PORT"

with httpx.Client(proxy=proxy, timeout=15) as client:
    print(client.get("https://ipinfo.io/ip").text.strip())

aiohttp 在每个请求里传代理。aiohttp 3.14 中 proxy_auth 和 BasicAuth 会提示已弃用,改用请求头传账号密码:

python
import asyncio

import aiohttp

PROXY = "http://HOST:PORT"
PROXY_HEADERS = {"Proxy-Authorization": aiohttp.encode_basic_auth("USERNAME", "PASSWORD")}


async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get("https://ipinfo.io/ip", proxy=PROXY, proxy_headers=PROXY_HEADERS) as response:
            print((await response.text()).strip())


asyncio.run(main())

在 Python 中使用 NanoIP 代理

  • 动态住宅和移动代理: proxy = "https://USERNAME:PASSWORD@gateway.nanoip.net:1111",账号密码用 Dynamic Routes 里生成的路由。生成路由时选择轮换或粘性,并注意上面的坑二。大量页面的采集任务,见数据采集。
  • 静态 ISP 代理: http://USERNAME:PASSWORD@HOST:PORT,用这个 IP 自己的主机和端口;也可以用同一组账号写成 socks5h://。IP 在整个套餐期内不变。

NanoIP 只用用户名密码认证,脚本放在任何服务器或电脑上都能直接运行,不需要设置 IP 白名单。

常见问题

重定向也会走代理吗?

会。跳转后的请求使用和第一个请求相同的代理设置。

怎么让每个请求用不同的代理?

每次调用时传入不同的 proxies 字典。这也是最可靠的写法,因为请求里的参数优先级最高。

为什么设置了代理却没生效?

通常是这三种情况之一:漏了 "https" 键;session.proxies 被环境变量或系统代理覆盖;目标网站在 NO_PROXY 里。在请求里直接传 proxies=,这三种情况都能排除。

作者:NanoIP Team · Proxy infrastructure and customer support team

相关文章

教程

Playwright 怎么设置代理?Node.js 实测教程

在 Playwright 里给整个浏览器或每个上下文设置代理、填写账号密码、检查出口 IP,以及常见报错的处理方法。文中代码均已实际运行。

约 4 分钟

对比

独享IP和共享IP有什么区别?代理怎么选

独享代理IP在套餐期内只有你在用,共享IP同时有多个用户。本文用图讲清共享会带来哪些影响、各自适合什么场景,以及购买前要问服务商的五个问题。

约 5 分钟

配置遇到问题?

我们的技术支持团队可以和你一起检查代理配置。