无需 KYC —— 无需身份证、自拍、文件证明。支持加密货币支付:BTC, USDT, USDC, ETH, SOL, LTC
语言: 简体中文
开始使用

文档 · Python 3.9+ · requests · httpx · aiohttp

Python中的住宅代理轮换:requests,httpx和aiohttp

生产环境模式,非单行代码:尊重Retry-After的重试,每次请求使用新的IP或一个流程用一个IP,asyncio中的每主机并发,以及每个页面实际消耗的流量字节。

最后更新: · 3 分钟阅读

快速回答

如何在Python中使用旋转住宅代理?

在requests中将http://USERNAME:[email protected]:7777作为http和https的代理,在httpx 0.26+中作为proxy=,在aiohttp中每次请求作为proxy=。CrawlRelay住宅代理每次新连接都会轮换,且用户名中带有-session-<id>保持会话。价格是US$1.50/1GB,50GB起价US$0.50/GB。支持加密货币支付,无需 KYC。

Python快速设置

requestsproxies={"http": url, "https": url};通过urllib3的Retry实现重试
httpxhttpx.Client(proxy=url);httpx 0.28删除了旧的proxies=参数
aiohttpsession.get(url, proxy="http://USER:PASS@host:port"),凭据写在代理URL中
SOCKS5pip install "requests[socks]"或httpx[socks],端口为7778的socks5h://
每次请求使用新 IP关闭keep-alive:Connection: close,max_keepalive_connections=0或force_close=True
保持一个流程使用同一IP用户名中写-session-<id>-ttl-<minutes>,最长支持 30 分钟持续时长
代理URL方案HTTP端口始终是http://,即使是HTTPS网站也一样

如何用requests配合Respect Retry-After的重试使用代理?

在Session上挂载带有urllib3Retry的HTTPAdapter。它会重试连接错误和你列出的状态码,采用指数退避,尊重网站在429和503返回的Retry-After头。设置trust_env = False,避免残留的HTTPS_PROXY变量覆盖代理设置。

requests + urllib3 Retry
import os

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

USER = os.environ.get("CRAWLRELAY_USER", "USERNAME-country-us")
PASSWORD = os.environ.get("CRAWLRELAY_PASSWORD", "PASSWORD")
PROXY = "http://%s:%[email protected]:7777" % (USER, PASSWORD)

retry = Retry(
    total=4,
    connect=2,
    backoff_factor=1.0,  # waits of 0 s, 2 s, 4 s, 8 s (Retry-After wins on 429/503)
    status_forcelist=(429, 502, 503, 504),
    allowed_methods=frozenset({"GET", "HEAD"}),
    respect_retry_after_header=True,
)

session = requests.Session()
session.trust_env = False  # ignore HTTP(S)_PROXY from the environment
session.mount("http://", HTTPAdapter(max_retries=retry))
session.mount("https://", HTTPAdapter(max_retries=retry))
session.proxies = {"http": PROXY, "https": PROXY}
session.headers["User-Agent"] = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"
session.headers["Connection"] = "close"  # new connection per request, so usually a new exit IP

r = session.get("https://api.ipify.org", timeout=(10, 30))
print(r.status_code, r.text)

如何安全生成定向代理URL?

在一个函数中生成用户名并校验。国家码拼写错误或密码中未编码字符会导致407,看上去像是凭据问题。下面的助手保持文档顺序(USERNAME-country-{cc}-state-{state}-city-{city}-zip-{zip}-asn-{asn}-session-{id}-ttl-{minutes}),拒绝未知键,并对凭据进行URL编码。

用户名生成器和代理URL
import re
from urllib.parse import quote

ORDER = ("country", "state", "city", "zip", "asn", "session", "ttl")
VALUE = re.compile(r"^[a-z0-9_]+$")


def build_username(base, **params):
    """build_username("USERNAME", country="us", city="new_york", session="job1", ttl=30)"""
    parts = [base]
    for key in ORDER:
        value = params.pop(key, None)
        if value is None:
            continue
        value = str(value).lower()
        if not VALUE.match(value):
            raise ValueError("bad value for %s: %r" % (key, value))
        if key == "ttl" and not 1 <= int(value) <= 30:
            raise ValueError("ttl must be 1 to 30 minutes")
        parts.append("%s-%s" % (key, value))
    if params:
        raise ValueError("unknown parameters: %s" % ", ".join(sorted(params)))
    return "-".join(parts)


def proxy_url(username, password, socks=False):
    scheme, port = ("socks5h", 7778) if socks else ("http", 7777)
    return "%s://%s:%[email protected]:%d" % (
        scheme, quote(username, safe=""), quote(password, safe=""), port)


print(proxy_url(build_username("USERNAME", country="us", state="ny", city="new_york", session="job1", ttl=20), "p@ss:word"))

如何用httpx每次请求获取新IP?

关闭keep-alive。httpx默认连接池复用,连接复用会保持相同出口IP。关闭后(max_keepalive_connections=0),每次请求都会新建连接,通常通过不同住宅IP出口。num_bytes_downloaded显示实际传输字节,这最接近流量计费。

httpx:旋转客户端
import httpx

PROXY = "http://USERNAME-country-gb:[email protected]:7777"

# No keep-alive: every request opens a new connection, so it usually gets a new exit IP
with httpx.Client(
    proxy=PROXY,
    timeout=httpx.Timeout(30.0, connect=10.0),
    limits=httpx.Limits(max_keepalive_connections=0),
    follow_redirects=True,
) as client:
    for _ in range(3):
        r = client.get("https://api.ipify.org")
        print(r.text, r.num_bytes_downloaded, "bytes on the wire")

如何用asyncio并发爬取而不过度请求单个站点?

每个主机限制并发,而非仅全局限制。全局限制50会导致50个请求都访问同一域名(当URL按域排序时)。对每主机使用信号量,保证每个网站访问量合理,同时整体爬取依然快速。初始限制2到4,提升前请阅读并发指南。

httpx.AsyncClient加每主机信号量
import asyncio
from collections import defaultdict
from urllib.parse import urlsplit

import httpx

PROXY = "http://USERNAME-country-us:[email protected]:7777"
PER_HOST = 4  # parallel requests per target host
host_slots = defaultdict(lambda: asyncio.Semaphore(PER_HOST))


async def fetch(client, url):
    async with host_slots[urlsplit(url).hostname]:
        try:
            r = await client.get(url)
            return url, r.status_code, r.num_bytes_downloaded
        except httpx.HTTPError as exc:
            return url, type(exc).__name__, 0


async def main(urls):
    limits = httpx.Limits(max_connections=50, max_keepalive_connections=0)
    timeout = httpx.Timeout(30.0, connect=10.0)
    async with httpx.AsyncClient(proxy=PROXY, limits=limits, timeout=timeout) as client:
        for result in await asyncio.gather(*(fetch(client, u) for u in urls)):
            print(*result)


asyncio.run(main(["https://example.com/", "https://example.org/", "https://example.net/"]))

如何使用aiohttp代理?

每次请求传递网关作为proxy=,凭据写入代理URL。此方法适用于所有aiohttp 3.x版本且无警告;BasicAuth和proxy_auth=参数自aiohttp 3.14起废弃,4.0将移除。TCPConnector(force_close=True)确保响应后关闭连接,每次请求通常获得新出口。否则aiohttp会复用连接,IP会保持一段时间。

aiohttp凭证写入代理URL
import asyncio

import aiohttp

# Credentials go in the proxy URL; percent-encode a password that contains @ or :
PROXY = "http://USERNAME-country-fr:[email protected]:7777"


async def main():
    # force_close=True: one connection per request, so each request usually gets a new exit IP
    connector = aiohttp.TCPConnector(limit=20, force_close=True)
    timeout = aiohttp.ClientTimeout(total=60, sock_connect=10)
    async with aiohttp.ClientSession(connector=connector, timeout=timeout) as session:
        for _ in range(3):
            async with session.get("https://api.ipify.org", proxy=PROXY) as resp:
                print(resp.status, await resp.text())


asyncio.run(main())

如何保持多步骤流程使用同一个IP?

每个流程生成一个会话ID,写入用户名,TTL应长于流程时间。此处keep-alive可用,目的是保持同一出口IP。若会话对应住宅设备掉线,TTL到期前IP可能变化,保证流程可从上次检查点重启。

requests中的保持会话
import uuid

import requests

session_id = uuid.uuid4().hex[:10]  # one ID per flow, lowercase letters and digits
user = "USERNAME-country-us-state-ca-session-%s-ttl-20" % session_id
proxy = "http://%s:[email protected]:7777" % user

with requests.Session() as s:
    s.proxies = {"http": proxy, "https": proxy}
    first = s.get("https://api.ipify.org", timeout=(10, 30)).text
    second = s.get("https://api.ipify.org", timeout=(10, 30)).text
    print(first, second, "same exit" if first == second else "exit changed")

Python爬虫每页流量多少?

购买大流量前,测量100个真实页面流量。在httpx中记录num_bytes_downloaded,或在中间件统计压缩内容长度,乘以页面数。HTML页面通常几十KB;带宽指南给出成本和1,000页价格,基于CrawlRelay定价。

三种习惯大幅降低流量,效果不变:

  • 发送Accept-Encoding: gzip, br(httpx和requests默认支持gzip),页面传输压缩。
  • 获取页面加载的JSON接口(前提是网站支持或开放此行为),而非完整HTML。
  • 使用If-None-Match或If-Modified-Since跳过未变更页面:304 Not Modified只需几百字节。

Python 和代理:常见问题

requests、httpx 还是 aiohttp:我该用哪个来爬虫?

简单脚本和小规模爬取用 requests。想同时支持同步和异步代码、并通过 httpx[http2] 使用 HTTP/2 就用 httpx。纯 asyncio 中高并发用 aiohttp。需要调度、去重和管道的爬虫,建议用 Scrapy,别自己写这些功能。

为什么在 Python 里用代理时会遇到 SSL: WRONG_VERSION_NUMBER 错误?

大概率是你在代理地址前写了 https://。网关端口 7777 是纯 HTTP,用来建立 HTTPS 的 CONNECT 隧道,所以代理 URL 在 http 和 https 键里都必须以 http:// 开头。详见 错误页面。

如何在 Python 中使用 SOCKS5 代理?

安装额外组件(pip install "requests[socks]" 或 pip install "httpx[socks]"),然后使用 socks5h://USERNAME:[email protected]:7778。h 会把主机名发送给代理,DNS 解析在出口进行,可以获得跟本地用户一样的区域结果。

为什么我的轮换代理会返回相同的 IP?

你的客户端重复使用了“keep-alive”连接,而轮换是针对新连接才执行的。在 requests 里发送 Connection: close,httpx 里设置 max_keepalive_connections=0,aiohttp 中用 force_close=True。还要检查用户名是否含有 session- 部分,那是故意保留一个 IP 的。

如何设置代理请求的超时?

要显式设置:requests 默认无超时。连接超时一般设为 10 秒,读取超时 30 秒适合 HTML 页;移动出口或慢速目标建议 45 到 60 秒。超时后最好重试一次,且新建连接,别一直等。参考读取超时。

我可以在 Jupyter 或 pandas 中使用这些代理吗?

可以。只要用到 requests 或 httpx 的工具都能用相同的代理设置。对于 pandas.read_html 或 read_csv 形式的 URL,先用带代理的 requests 下载内容,再把文本或字节传给 pandas。

通过住宅代理运行你的 Python 爬虫

US$1.50,1 GB起,US$0.50/GB从50 GB起。

从 US$0.50/GB 开始 · 无需 KYC

开始使用