实操教程2026年7月8日阅读约 5 分钟

用 Python 做代理轮换,大约十行

requests 和 httpx 的可用配置,外加两个让人误以为轮换坏了、实际上是连接池在作怪的坑。

轮换比多数教程写得要简单。用轮换入口的话,你不需要自己维护一个 IP 列表。你只连一个主机,服务商每条请求给你换一个 IP。

下面所有代码都用 HOST、PORT、USERNAME 和 PASSWORD 作占位符。ProxyPanda 没有一个可以从博客里直接抄走的统一网关地址:控制台在每个服务的页面上列出主机、端口和凭据,请从那里复制。

全部代码

import requests

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

for url in urls:
    r = requests.get(url, proxies=proxies, timeout=30)
    print(r.status_code, len(r.content))

就这些。住宅服务在控制台里设成随机 IP 时,每条请求都从不同的 IP 出去。

想亲眼看到轮换,就向一个回显 IP 的服务问几次:

for _ in range(3):
    print(requests.get("https://httpbin.org/ip", proxies=proxies, timeout=30).json())

三次调用,三个不同的 origin 地址。

让几条请求保持同一个 IP

有时候你需要 IP 别动:先登录,再访问登录之后的页面。在控制台里把住宅服务切换成粘性 IP,并设定会话保持多久。控制台会生成一个带会话 ID 的凭据,按显示的样子整串复制即可。

STICKY = "http://USERNAME:STICKY_PASSWORD@HOST:PORT"
sticky = {"http": STICKY, "https": STICKY}

同一个会话 ID,同一个出口 IP,在你选定的时长内,网络能保持多久就保持多久。想换新的,就在控制台里生成一个新会话。出口 IP 属于某户人家的宽带,那条连接断了,粘性会话也可能提前结束;所以把登录这一步写成可以重跑的。

两个坑

坑一:复用 Session 还指望它轮换。

s = requests.Session()
s.proxies = proxies

Session 会保持连接。对 HTTPS 目标来说,一个被复用的连接就是穿过代理的同一条隧道,所以出口 IP 也是同一个。轮换不会发生,因为你没建立新连接。如果你要每条请求换一个 IP,要么别用 session,要么发 Connection: close。

这是「你们的轮换坏了」这类反馈里最常见的一条,而且几乎每次都是这个原因。

坑二:没处理重试。

轮换意味着有些请求会落在目标站已经不喜欢的 IP 上。这很正常,而且正是轮换奏效的原因:你重试一次,就换到另一个。

from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter

retry = Retry(total=3, backoff_factor=0.4, status_forcelist=[429, 502, 503])
s = requests.Session()
s.mount("https://", HTTPAdapter(max_retries=retry))

在我们按 GB 计量的表上,网站发回来的 429 或 5xx 和其他响应一样,所以每次重试都要花掉那个响应的字节。它们通常很小,只有正常页面的零头,但不做退避的重试循环会收下很多个,所以 backoff_factor 要留着。超时、连接重置和我们自己网关的错误不计费。各家服务商把这条线划在不同的地方,记得问清楚你的服务商。哪些失败值得再试一次,重试不烧流量那篇讲得更细。

如果你更习惯 httpx 和异步

import asyncio
import httpx

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

async def main(urls):
    async with httpx.AsyncClient(proxy=PROXY, timeout=30) as client:
        return await asyncio.gather(*(client.get(u) for u in urls))

results = asyncio.run(main(["https://httpbin.org/ip"] * 3))

注意同一个注意事项:AsyncClient 也会复用连接池。要每次都拿新 IP,就每条请求建一个 client,或者有意识地用粘性会话,自己去切换会话。

轮换你自己的一组静态 IP

ISP 代理和数据中心代理的思路正好相反。你买下一组固定 IP,控制台逐个列出它们的端口和凭据,轮换由你自己来做。多写一行就够:

from itertools import cycle

pool = cycle([
    "http://USERNAME:PASSWORD@HOST_1:PORT_1",
    "http://USERNAME:PASSWORD@HOST_2:PORT_2",
    "http://USERNAME:PASSWORD@HOST_3:PORT_3",
])

for url in urls:
    proxy = next(pool)
    r = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)

该买哪个产品

如果目标站接受数据中心 IP,像这样循环使用几个,通常是分散请求最便宜的办法,而且你可以选它们所在的国家。如果遇到验证码或者空白页,再换住宅代理:它替你轮换,但按 GB 计费,也不能指定国家。别因为某篇教程这么说就一上来用住宅,多数任务并不需要它。当前价格见价格页。

哪一步卡住了?贴到 Discord 里,一分钟内就会有人看出问题。

还有后续问题?

去 Discord 问。答案还能帮到下一个读到这个帖子的人。

加入 Discorddiscord.gg/proxypanda
$5 起步去 Discord 问