Python 移动代理配置指南
所有主流 Python HTTP 客户端都原生支持代理,所以无需安装任何东西,也无需学习厂商 SDK——接入只需一个环境变量和一个参数。真正值得做好的是底层细节:复用连接、正确重试,以及在长任务写入错误数据之前,先断言出口确实在您预期的位置。
问题所在:简单的部分很简单,稳定运行却不简单
让 requests 走代理只需一行代码,而大多数教程也就止步于此。问题会在之后出现:代码用的是 requests.get 而不是 Session,导致每次调用都新建 TCP 连接和 CONNECT 隧道,任务慢了好几倍;重试循环不遵守 Retry-After,反复冲击 429;异步客户端同时打开数百条隧道,被当作攻击。这些问题在报错堆栈里看起来都不像代理问题,这正是它们如此耗费时间的原因。
如果您遇到过以下情况
- 吞吐量远低于连接应有的水平,大部分时间花在建立连接上。
- 并发时出现 ProxyError 或 TunnelError,单线程运行时却消失。
- 添加代理后出现 SSL 错误,通常是因为试图对代理而不是目标网站做证书校验。
- 重试策略退避太少甚至不退避,429 越来越多。
- 任务跑完,数据却全是错误地区的,因为没有任何地方断言出口国家。
可运行的代码
只要 PROXY_URL 中填入真实的连接字符串,下面的每段代码都能直接运行。首次充值后,可在控制台复制您的连接字符串。
设置连接 URL
bashexport PROXY_URL="http://USERNAME-mbl-us-rot-auto10:[email protected]:7000"
# 同一个字符串同时适用于 http:// 和 https:// 目标——
# 客户端会自动为 TLS 建立 CONNECT 隧道。requests — 一个可以全天运行的 Session
pythonimport os
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
PROXY = os.environ["PROXY_URL"]
session = requests.Session()
session.proxies = {"http": PROXY, "https": PROXY}
retry = Retry(
total=4,
backoff_factor=1.5, # 1.5 秒、3 秒、6 秒、12 秒
status_forcelist=(429, 500, 502, 503, 504),
respect_retry_after_header=True,
allowed_methods=frozenset(["GET", "HEAD"]),
)
# pool_maxsize 让隧道保持存活,而不是每个请求都重新连接。
session.mount("https://", HTTPAdapter(max_retries=retry, pool_maxsize=20))
session.mount("http://", HTTPAdapter(max_retries=retry, pool_maxsize=20))
# 在任务写入任何数据之前,先断言出口位置。
who = session.get("https://ipinfo.io/json", timeout=30).json()
print(f"exit {who['ip']} — {who['country']} — {who['org']}")
r = session.get("https://example.com", timeout=30)
print(r.status_code, len(r.content))Session 加 pool_maxsize,就是"复用一条隧道"与"每个请求一条隧道"的区别。
httpx — 同一份配置,同步异步皆可
pythonimport os, asyncio, httpx
PROXY = os.environ["PROXY_URL"]
# 同步
with httpx.Client(proxy=PROXY, timeout=30.0) as client:
print(client.get("https://api.ipify.org?format=json").json())
# 异步——请限制并发数,否则每个任务都会打开一条隧道。
async def main(urls):
limits = httpx.Limits(max_connections=10, max_keepalive_connections=10)
sem = asyncio.Semaphore(10)
async with httpx.AsyncClient(proxy=PROXY, limits=limits, timeout=30.0) as c:
async def one(u):
async with sem:
r = await c.get(u)
return u, r.status_code, len(r.content)
return await asyncio.gather(*(one(u) for u in urls))
print(asyncio.run(main(["https://example.com"] * 5)))httpx 0.26 及以上版本使用 proxy=,旧版本使用 proxies=。请显式限制并发数——不受限的异步请求正是客户端被限流的原因。
aiohttp — 代理按请求设置
pythonimport os, asyncio, aiohttp
PROXY = os.environ["PROXY_URL"]
async def main():
conn = aiohttp.TCPConnector(limit=10)
async with aiohttp.ClientSession(connector=conn) as s:
# aiohttp 的 proxy= 要写在每次调用上,而不是会话上。
async with s.get("https://api.ipify.org?format=json", proxy=PROXY) as r:
print(r.status, await r.json())
asyncio.run(main())常见陷阱:在 ClientSession 上设置 proxy 不起作用——它必须设置在每个请求上。
无需改代码即可切换国家或会话
pythonimport os, re
def with_country(url: str, cc: str) -> str:
return re.sub(r"-(us|gb|de|fr|es|nl|pl|ch|pa|am|ge)-", f"-{cc}-", url, count=1)
def with_session(url: str, sid: str) -> str:
if "-sid-" in url:
return re.sub(r"-sid-[a-z0-9_]+", f"-sid-{sid}", url)
return url.replace("-rot-", f"-sid-{sid}-rot-")
base = os.environ["PROXY_URL"]
print(with_country(base, "de"))
print(with_session(with_country(base, "fr"), "worker07"))路由信息都在用户名字符串中,所以切换市场只是一次字符串替换,而不是重新部署。
应该使用哪种轮换模式
-rot-auto10对于常见的 Python 任务——爬取大量相互独立、无需保持会话的 URL——auto10 是合适的默认选择。您无需做任何编排,大约每 10 分钟就会获得一个新的运营商 IP,长任务因此分散到多个出口上。轮换期间请继续使用同一个 requests.Session:Session 是您的连接池和 cookie 容器,不受请求之间出口变化的影响。
何时选择其他模式
| 模式 | 适用场景 |
|---|---|
| sticky | 任何有状态的操作——已登录的账户、多步骤表单、购物车。为每个 worker 加上一个独立的 -sid-。 |
| auto30 | 长时间后台任务,较慢的 IP 更替已经足够,并希望隧道保持更久。 |
| none | 临时的一次性脚本,不在乎调用之间出口如何变化。 |
预计带宽用量
从 Python 发起普通 HTTP 请求是使用代理池最省钱的方式,因为您只下载请求的内容,别无其他。在这类任务中,1 GB 流量能用很久。
| 任务类型 | 单次用量 | 每 GB 可完成 |
|---|---|---|
| 抓取 HTML 页面(典型内容网站) | 100–400 KB | 约 2,600–10,000 页 |
| JSON API 响应 | 2–50 KB | 约 20,000–500,000 次调用 |
| 启用 gzip/brotli 压缩的 HTML | 30–120 KB | 约 8,700–35,000 页 |
| HEAD 请求 / 状态检查 | 约 1 KB | 几乎不消耗流量 |
请发送 Accept-Encoding: gzip, br——requests 和 httpx 默认会发送,但手写的请求头经常把它覆盖掉,而仅压缩一项通常就能减少 70% 的 HTML 传输量。只需要状态码或某个响应头时使用 HEAD;对大响应使用 stream=True 流式读取,拿到所需内容后即可中止,不必为剩余部分付费。以上为典型范围,请先对自己的目标计量,再做规划。
| 带宽 | 价格 | 每 GB | 节省 |
|---|---|---|---|
| 1 GB | $7 | $7 | — |
| 5 GB | $35 | $7 | — |
| 10 GB | $68 | $6.80 | 3% |
| 25 GB | $150 | $6 | 14% |
| 50 GB | $283 | $5.66 | 19% |
| 100 GB | $500 | $5 | 29% |
适用国家
修改代理用户名中的一个参数即可切换国家——无需重新部署,也无需第二个账户。 IP 池规模因国家而异,也取决于您使用的是移动(mbl) 还是住宅(peer)IP 池;实时可用情况请查看各国家页面和您的控制台。
仅限合法用途
ProxyMobile 仅用于在您拥有或已获授权测试的系统上进行测量、质量保证和研究。购买带宽并不代表您获得了访问第三方系统的许可。您仍需自行遵守每个目标网站的服务条款、robots 指令和速率限制,以及适用于您的数据保护法律。
本场景的使用要求
- 只从您拥有或已获授权采集的网站采集数据,并遵守其条款。
- 遵守 robots 指令、Retry-After 和任何公开的速率指引——重试策略不仅关乎稳定性,也是合规的一部分。
- 限制并发数。无论您的本意如何,对同一主机发起不受限的异步请求,与攻击无异。
- 没有合法依据时不得采集个人数据,也不要保留您本不需要采集的内容。
任何套餐均严禁
- 禁止撞库、盗用账户,或任何冒充他人进行身份验证的行为。
- 禁止绕过您未获授权测试的访问控制、付费墙或安全措施。
- 禁止收集您没有合法依据处理的个人数据。
- 禁止产生合理运营方会视为拒绝服务攻击的负载——请限速,遇到 429 时退避,并遵守 Retry-After。
用于欺诈、入侵或批量滥用的账户将被终止且不予退款,我们会配合有效的法律程序。完整条款请参阅我们的可接受使用政策。
Python常见问题
需要特殊的库或 SDK 吗?
不需要。requests、httpx、aiohttp、urllib3 和 Scrapy 都原生支持标准 HTTP 代理。您只需设置一个代理 URL,代码的其他部分保持不变。
为什么加了代理后爬虫变慢了?
几乎都是因为每次调用都新建了连接。整个任务请使用同一个 requests.Session(或同一个 httpx.Client),并调大 pool_maxsize,这样 CONNECT 隧道只需建立一次并被复用,而不是每个请求都建立一次。
如何通过代理访问 HTTPS?
同一个 URL 同时适用于两种协议——对于 TLS 目标,客户端会自动建立 CONNECT 隧道。请保持证书校验开启;它校验的是目标网站的证书,而不是代理,所以没有理由关闭它。
httpx 中 proxy= 和 proxies= 有什么区别?
httpx 0.26 及以上版本接受单个 proxy= 参数;更早的版本使用 proxies= 映射。如果遇到"unexpected keyword argument"错误,请核对已安装的版本与示例所针对的版本是否一致。
为什么在 aiohttp 的 ClientSession 上设置 proxy 不起作用?
因为 aiohttp 是按请求而不是按会话设置代理的。请像上面的示例那样,在每次 .get() 或 .post() 调用中传入 proxy=PROXY。
Python 爬虫应使用哪种轮换模式?
无状态地爬取大量独立 URL 时使用 auto10。一旦需要在请求之间保持登录、购物车或其他状态,就改用粘性会话,并为每个 worker 设置独立的 -sid-。
相关指南
用您自己的目标网站试一试
本页的带宽估算是如实给出的参考范围,而非承诺——唯一重要的数字,是您的目标网站实际消耗的流量。首次充值额外赠送 $2,足够您在确定预算之前,先实际计量一次运行。
