Sans KYC — pas d’ID, pas de selfie, pas de documents. Payer en crypto : BTC, USDT, USDC, ETH, SOL, LTC
Langue: Français
Commencez

Docs · Python 3.9+ · requests · httpx · aiohttp

Proxies résidentiels rotatifs en Python : requests, httpx et aiohttp

Modèles de production, pas du one-liner : retries respectant Retry-After, une nouvelle IP par requête ou une IP par flux, concurrence par hôte en asyncio, et le nombre d’octets réellement consommés par chaque page.

Dernière mise à jour: · 7 min de lecture

Réponse rapide

Comment utiliser des proxies résidentiels rotatifs en Python ?

Passez http://USERNAME:[email protected]:7777 comme proxy pour http et https dans requests, comme proxy= dans httpx 0.26+, ou comme proxy= par requête dans aiohttp. CrawlRelay proxies résidentiels tournent à chaque nouvelle connexion et restent stables avec -session-<id> dans le nom d’utilisateur. Leur coût est 1,50 $US pour 1 GB et 0,50 $US/GB à partir de 50 GB. Paiement en crypto, sans KYC.

Configuration Python en bref

requestsproxies={"http": url, "https": url} ; retries via urllib3 Retry
httpxhttpx.Client(proxy=url) ; l’argument ancien proxies= a été supprimé dans httpx 0.28
aiohttpsession.get(url, proxy="http://USER:PASS@host:port"), identifiants dans l’URL du proxy
SOCKS5pip install "requests[socks]" ou httpx[socks], puis socks5h:// sur le port 7778
Nouvelle IP à chaque requêteDésactiver keep-alive : Connection: close, max_keepalive_connections=0 ou force_close=True
Même IP pour un flux-session-<id>-ttl-<minutes> dans le nom d’utilisateur, jusqu’à 30 minutes
Schéma d’URL proxyToujours http:// pour le port HTTP, même pour les sites en HTTPS

Comment utiliser un proxy avec requests, avec des retries respectant Retry-After ?

Montez un HTTPAdapter avec urllib3's Retry sur un Session. Cela relance les erreurs de connexion et les statuts listés, attend avec une backoff exponentielle, et respecte l’en-tête Retry-After que les sites envoient avec 429 et 503. Fixez trust_env = False pour qu’une variable HTTPS_PROXY résiduelle ne puisse pas écraser votre 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)

Comment construire des URLs proxy ciblées en toute sécurité ?

Générez le nom d’utilisateur dans une fonction et validez-le. Une faute dans un code pays ou un caractère non encodé dans le mot de passe provoque un 407 ressemblant à un problème d’identifiants. L’outil ci-dessous respecte l’ordre documenté (USERNAME-country-{cc}-state-{state}-city-{city}-zip-{zip}-asn-{asn}-session-{id}-ttl-{minutes}), refuse les clés inconnues et encode en URL les deux identifiants.

Générateur de nom d’utilisateur et d’URL proxy
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"))

Comment obtenir une nouvelle IP à chaque requête avec httpx ?

Désactivez keep-alive. httpx met ses connexions en pool par défaut, et une connexion réutilisée conserve son IP de sortie. Avec max_keepalive_connections=0, chaque requête ouvre une nouvelle connexion au gateway et sort généralement par une IP résidentielle différente. num_bytes_downloaded donne le nombre d’octets reçus sur le fil, ce qui correspond le plus à la facturation du trafic.

httpx : client rotatif
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")

Comment scraper en concurrence avec asyncio sans surcharger un site ?

Limitez la concurrence par hôte, pas que globalement. Une limite globale à 50 laisse 50 requêtes cibler le même site si votre liste d’URLs est triée par domaine. Un sémaphore par nom d’hôte maintient chaque site à un niveau respectueux tout en gardant le crawl globalement rapide. Commencez à 2-4 par hôte et consultez le guide de concurence avant d’augmenter.

httpx.AsyncClient avec un sémaphore par hôte
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/"]))

Comment utiliser un proxy avec aiohttp ?

Passez le gateway en proxy= à chaque requête, avec les identifiants dans l’URL du proxy. Cela fonctionne sur toutes les versions 3.x de aiohttp sans avertissement ; BasicAuth et le paramètre proxy_auth= sont dépréciés depuis aiohttp 3.14 et seront retirés en 4.0. Un TCPConnector(force_close=True) ferme chaque connexion après la réponse, donc chaque requête obtient généralement une nouvelle sortie IP. Sans cela, aiohttp réutilise les connexions et vous gardez la même IP un moment.

aiohttp avec identifiants dans l’URL du proxy
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())

Comment garder une IP stable pour un flux multi-étapes ?

Générez un ID de session par flux et placez-le dans le nom d’utilisateur avec un TTL plus long que le flux. Keep-alive est conseillé ici : vous voulez la même sortie. Si le dispositif résidentiel derrière la session se déconnecte, l’IP peut changer avant expiration du TTL, donc rendez le flux capable de redémarrer depuis son dernier point de contrôle.

Session sticky avec 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")

Combien de trafic un scraper Python utilise-t-il par page ?

Mesurez-le sur 100 vraies pages avant d’acheter du volume. Enregistrez num_bytes_downloaded dans httpx, ou la taille du corps compressé dans votre middleware, et multipliez par le nombre de pages. Une requête HTML seule coûte généralement quelques dizaines de kilo-octets par page ; le guide bande passante donne les chiffres et le coût par 1 000 pages aux tarifs CrawlRelay.

Trois habitudes réduisent le trafic sans changer vos résultats :

  • Envoyez Accept-Encoding: gzip, br (httpx et requests compressent gzip par défaut) pour que les pages voyagent compressées.
  • Récupérez le point de terminaison JSON qu’une page charge, quand le site le documente ou l’autorise, au lieu du HTML complet.
  • Évitez les pages inchangées avec If-None-Match ou If-Modified-Since : un 304 Not Modified ne coûte que quelques centaines d’octets.

Python et proxies : questions

requests, httpx ou aiohttp : lequel devrais-je utiliser pour le scraping ?

Utilisez requests pour des scripts simples et de petits crawls. Utilisez httpx quand vous voulez la même API en mode synchrone et asynchrone, et HTTP/2 via httpx[http2]. Utilisez aiohttp pour une très haute concurrence en pur asyncio. Pour un crawl avec planification, déduplication et pipelines, utilisez Scrapy plutôt que d'écrire ces parties vous-même.

Pourquoi ai-je l'erreur SSL: WRONG_VERSION_NUMBER avec mon proxy en Python ?

Vous avez probablement écrit https:// devant l'adresse du proxy. Le port 7777 de la passerelle parle HTTP simple et ouvre un tunnel CONNECT pour les sites HTTPS, donc l'URL du proxy doit commencer par http:// dans les clés http et https. Voir la page d'erreur.

Comment utiliser des proxies SOCKS5 en Python ?

Installez le supplément (pip install "requests[socks]" ou pip install "httpx[socks]"), puis utilisez socks5h://USERNAME:[email protected]:7778. Le h envoie les noms d’hôte au proxy, donc la résolution DNS se fait à la sortie et vous obtenez les mêmes réponses régionales que les visiteurs locaux.

Pourquoi mon proxy rotatif renvoie-t-il deux fois la même IP ?

Votre client a réutilisé une connexion keep-alive, et la rotation se fait à chaque nouvelle connexion. Envoyez Connection: close dans requests, définissez max_keepalive_connections=0 dans httpx, ou utilisez force_close=True dans aiohttp. Vérifiez aussi que l'identifiant utilisateur ne contient pas de partie session-, ce qui conserve volontairement une IP.

Comment définir des délais d'attente pour les requêtes via proxy ?

Définissez-les explicitement : requests n'a pas de timeout par défaut. Un timeout de connexion d'environ 10 secondes et un timeout de lecture de 30 secondes conviennent pour des pages HTML ; les sorties mobiles et cibles lentes demandent 45 à 60 secondes. Recommencez une fois sur nouvelle connexion en cas de timeout plutôt que d'attendre plus longtemps. Voir timeouts de lecture.

Puis-je utiliser ces proxies dans Jupyter ou avec pandas ?

Oui. Tout ce qui utilise requests ou httpx en dessous prend les mêmes réglages de proxy. Pour pandas.read_html ou read_csv sur une URL, téléchargez d'abord le contenu avec une requête proxifiée, puis transmettez le texte ou les octets à pandas.

Faites fonctionner votre scraper Python via des IPs résidentielles

1,50 $US pour 1 GB, 0,50 $US/GB à partir de 50 GB.

À partir de 0,50 $US/GB · sans KYC

Commencez