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

> Proxies résidentiels rotatifs en Python : requests avec retries, httpx synchrones et asynchrones, aiohttp, sessions sticky et comptage des octets. Code vérifié syntaxiquement.

- URL: https://crawlrelay.com/fr/docs/python/
- Dernière mise à jour: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

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.

**Réponse rapide — Comment utiliser des proxies résidentiels rotatifs en Python ?**

Passez `http://USERNAME:PASSWORD@gw.crawlrelay.com: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

| Attribut | Valeur |
| --- | --- |
| requests | `proxies={"http": url, "https": url}` ; retries via urllib3 `Retry` |
| httpx | `httpx.Client(proxy=url)` ; l’argument ancien `proxies=` a été supprimé dans httpx 0.28 |
| aiohttp | `session.get(url, proxy="http://USER:PASS@host:port")`, identifiants dans l’URL du proxy |
| SOCKS5 | `pip install "requests[socks]"` ou `httpx[socks]`, puis `socks5h://` sur le port 7778 |
| Nouvelle IP à chaque requête | Dé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 proxy | Toujours `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*

```python
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:%s@gw.crawlrelay.com: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](https://crawlrelay.com/fr/docs/errors/407-proxy-authentication-required/) 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*

```python
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:%s@gw.crawlrelay.com:%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*

```python
import httpx

PROXY = "http://USERNAME-country-gb:PASSWORD@gw.crawlrelay.com: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](https://crawlrelay.com/fr/docs/concurrency/) avant d’augmenter.

*httpx.AsyncClient avec un sémaphore par hôte*

```python
import asyncio
from collections import defaultdict
from urllib.parse import urlsplit

import httpx

PROXY = "http://USERNAME-country-us:PASSWORD@gw.crawlrelay.com: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*

```python
import asyncio

import aiohttp

# Credentials go in the proxy URL; percent-encode a password that contains @ or :
PROXY = "http://USERNAME-country-fr:PASSWORD@gw.crawlrelay.com: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*

```python
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:PASSWORD@gw.crawlrelay.com: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](https://crawlrelay.com/fr/guides/bandwidth-calculator/) 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](https://crawlrelay.com/fr/docs/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](https://crawlrelay.com/fr/docs/errors/ssl-wrong-version-number/).

### Comment utiliser des proxies SOCKS5 en Python ?

Installez le supplément (`pip install "requests[socks]"` ou `pip install "httpx[socks]"`), puis utilisez `socks5h://USERNAME:PASSWORD@gw.crawlrelay.com: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](https://crawlrelay.com/fr/docs/errors/read-timeout/).

### 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. [Voir les tarifs](https://crawlrelay.com/fr/pricing/) · [Guide Scrapy](https://crawlrelay.com/fr/docs/scrapy/)

---

CrawlRelay: CrawlRelay est un service de proxy résidentiel rotatif et proxy mobile 4G/5G pour développeurs, adapté aux scrapers et pipelines de données IA : trafic prépayé sans expiration à partir de 0,50 $US/GB, paiement en crypto, sans téléchargement d’ID. Sans KYC — pas d’ID, pas de selfie, pas de documents. Payer en crypto.
