Si votre script Python reçoit une erreur 403, un CAPTCHA ou un blocage par IP — cela signifie que le site cible vous a déjà remarqué. Connecter un proxy à la bibliothèque requests résout ce problème : vous changez d'adresse IP, contournez les restrictions géographiques et répartissez la charge entre plusieurs adresses. Dans ce guide, tout est abordé, de la connexion de base à la rotation avancée avec de vrais exemples de code.
Pourquoi utiliser des proxies dans les scripts Python
La plupart des sites et des API suivent les adresses IP des requêtes entrantes. Si une adresse effectue plus de 100 requêtes par minute — elle est bloquée. C'est une protection standard contre les bots, utilisée par Wildberries, Ozon, Avito, Google, Instagram et des centaines d'autres plateformes. Un proxy permet de diriger la requête via un serveur intermédiaire avec une autre adresse IP, vous rendant invisible pour les systèmes de protection.
Voici les principales tâches où les proxies en Python sont critiques :
- Scraping de marketplaces — collecte de prix depuis Wildberries, Ozon, Yandex.Market sans blocages par IP
- Surveillance des concurrents — requêtes régulières aux sites des concurrents toutes les 5 à 15 minutes
- Travail avec des API avec des limites — répartition des requêtes entre plusieurs IP pour ne pas dépasser le taux limite
- Tests géolocalisés — vérification de l'apparence d'un site depuis différents pays et régions
- Automatisation des formulaires et des inscriptions — création de comptes ou remplissage de formulaires depuis différentes IP
- Surveillance SEO — récupération des positions depuis différentes régions de Russie et d'autres pays
Sans proxy, même un parser bien écrit se heurtera à un blocage après quelques heures de fonctionnement. Avec une rotation IP correctement configurée, le même script fonctionne pendant des semaines sans interruption.
Configuration de base du proxy dans requests
La bibliothèque requests prend en charge les proxies nativement — aucun paquet supplémentaire n'est nécessaire. Le proxy est transmis via un dictionnaire proxies dans les paramètres de la requête.
L'exemple le plus simple — un proxy HTTP pour une seule requête :
import requests
proxies = {
"http": "http://123.45.67.89:8080",
"https": "http://123.45.67.89:8080",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
# {'origin': '123.45.67.89'}
Notez que dans le dictionnaire proxies, vous devez spécifier les deux clés — http et https. Si vous n'en spécifiez qu'une, les requêtes via le second protocole passeront directement sans proxy. C'est une erreur fréquente chez les débutants, qui entraîne une fuite de l'IP réelle.
Pour vous assurer que le proxy fonctionne, utilisez le service httpbin.org/ip — il retourne l'adresse IP depuis laquelle la requête a été faite. Si dans la réponse vous voyez l'IP du serveur proxy, et non la vôtre — tout est correctement configuré.
Proxies HTTP, HTTPS et SOCKS5 : différences et exemples de code
Les proxies existent en différents types, et chacun est adapté à ses propres tâches. Dans le contexte de Python requests, il est important de comprendre la différence entre les trois protocoles principaux :
| Type | Protocole dans l'URL | Vitesse | Support UDP | Meilleur scénario |
|---|---|---|---|---|
| HTTP | http:// |
Élevée | Non | Scraping de sites HTTP |
| HTTPS | https:// |
Élevée | Non | Scraping de sites sécurisés |
| SOCKS5 | socks5:// |
Moyenne | Oui | Anonymat complet, tous les protocoles |
Pour travailler avec SOCKS5 en Python, vous devez installer un paquet supplémentaire :
pip install requests[socks] # ou séparément : pip install PySocks
Après l'installation, la connexion SOCKS5 proxy ressemble à ceci :
import requests
# Proxy SOCKS5
proxies = {
"http": "socks5://123.45.67.89:1080",
"https": "socks5://123.45.67.89:1080",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
SOCKS5 est le protocole préféré pour les tâches où l'anonymat est important. Contrairement aux proxies HTTP, SOCKS5 n'ajoute pas d'en-têtes X-Forwarded-For, qui peuvent révéler votre IP réelle.
Proxy avec authentification par nom d'utilisateur et mot de passe
La plupart des services de proxy payants utilisent l'authentification par nom d'utilisateur et mot de passe. C'est une pratique standard — sans authentification, le proxy ne laissera tout simplement pas passer votre requête. Dans la bibliothèque requests, les données d'authentification sont transmises directement dans l'URL du proxy.
import requests # Format : protocole://nom_utilisateur:mot_de_passe@hôte:port proxy_url = "http://myuser:[email protected]:8080" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://httpbin.org/ip", proxies=proxies) print(response.status_code) print(response.json())
Si le mot de passe ou le nom d'utilisateur contient des caractères spéciaux (par exemple, @, #, %), ils doivent être URL-encodés. Pour cela, utilisez le module urllib.parse :
import requests
from urllib.parse import quote
username = "myuser"
password = "p@ss#word!" # Caractères spéciaux
# URL-encoder le nom d'utilisateur et le mot de passe
encoded_user = quote(username, safe="")
encoded_pass = quote(password, safe="")
proxy_url = f"http://{encoded_user}:{encoded_pass}@123.45.67.89:8080"
proxies = {
"http": proxy_url,
"https": proxy_url,
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
💡 Conseil de sécurité
Ne hardcodez jamais le nom d'utilisateur et le mot de passe directement dans le code du script. Utilisez des variables d'environnement ou un fichier .env avec la bibliothèque python-dotenv. Cela vous évitera une fuite accidentelle des informations d'identification lors de la publication du code sur GitHub.
Rotation des proxies : changement automatique d'IP pour le scraping
Un proxy — c'est toujours une adresse IP, qui peut être bloquée. La véritable protection contre les blocages est la rotation : chaque requête (ou toutes les N requêtes) part avec une nouvelle IP. Voici quelques approches pour mettre en œuvre la rotation.
Méthode 1 : Sélection aléatoire dans la liste
import requests
import random
# Liste de proxies
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
def get_random_proxy():
proxy = random.choice(proxy_list)
return {"http": proxy, "https": proxy}
# Scraping de 10 pages avec rotation IP
urls = [f"https://example.com/page/{i}" for i in range(1, 11)]
for url in urls:
proxies = get_random_proxy()
try:
response = requests.get(url, proxies=proxies, timeout=10)
print(f"URL : {url} | IP : {proxies['http'].split('@')[1]} | Statut : {response.status_code}")
except requests.RequestException as e:
print(f"Erreur : {e}")
Méthode 2 : Rotation cyclique avec itertools
import requests
import itertools
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
# Création d'une boucle infinie sur la liste de proxies
proxy_cycle = itertools.cycle(proxy_list)
def get_next_proxy():
proxy = next(proxy_cycle)
return {"http": proxy, "https": proxy}
# Chaque requête utilise le prochain proxy dans l'ordre
for i in range(9):
proxies = get_next_proxy()
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(f"Requête {i+1} : {response.json()['origin']}")
Pour des tâches industrielles avec des milliers de requêtes par heure, il est recommandé d'utiliser des proxies résidentiels avec rotation automatique intégrée — le fournisseur change lui-même l'IP pour chaque requête via un point d'accès unique, et vous n'avez pas besoin de gérer manuellement la liste des adresses.
Proxy via Session : connexions persistantes et cookies
Lorsque vous devez effectuer plusieurs requêtes dans le cadre d'une seule session (par exemple, se connecter puis faire des requêtes), utilisez l'objet requests.Session(). Il conserve les cookies, les en-têtes et les paramètres de proxy entre les requêtes — il n'est pas nécessaire de transmettre le proxy à chaque appel séparément.
import requests
# Création d'une session avec proxy
session = requests.Session()
session.proxies = {
"http": "http://user:[email protected]:8080",
"https": "http://user:[email protected]:8080",
}
# Ajout d'en-têtes pour imiter un navigateur
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept-Language": "fr-FR,fr;q=0.9",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
})
# Étape 1 : Authentification
login_data = {"username": "myuser", "password": "mypass"}
session.post("https://example.com/login", data=login_data)
# Étape 2 : Requêtes déjà avec des cookies et via proxy
response = session.get("https://example.com/dashboard")
print(response.status_code)
# Étape 3 : Fermeture de la session
session.close()
L'utilisation de Session est également plus efficace en termes de performance : la connexion TCP est réutilisée, au lieu d'être ouverte à nouveau pour chaque requête. Lors du scraping de plus de 1000 pages, cela offre un gain de vitesse significatif.
Gestion des erreurs, délais d'attente et répétitions automatiques
Les serveurs proxy peuvent être inaccessibles, répondre lentement ou renvoyer des erreurs de connexion. Un script fiable pour le scraping doit être capable de gérer ces situations et de passer automatiquement à un autre proxy en cas d'échec.
import requests
import random
import time
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
def fetch_with_retry(url, max_retries=3, timeout=10):
"""
Effectue une requête avec changement automatique de proxy en cas d'erreur.
Retourne un objet Response ou None si les tentatives sont épuisées.
"""
available_proxies = proxy_list.copy()
random.shuffle(available_proxies)
for attempt, proxy_url in enumerate(available_proxies[:max_retries], 1):
proxies = {"http": proxy_url, "https": proxy_url}
try:
response = requests.get(
url,
proxies=proxies,
timeout=timeout,
headers={"User-Agent": "Mozilla/5.0"}
)
response.raise_for_status() # Lève une exception pour les 4xx/5xx
print(f"✓ Succès à la tentative {attempt}")
return response
except requests.exceptions.ProxyError:
print(f"✗ Tentative {attempt} : proxy inaccessible — {proxy_url}")
except requests.exceptions.Timeout:
print(f"✗ Tentative {attempt} : délai d'attente — {proxy_url}")
except requests.exceptions.HTTPError as e:
print(f"✗ Tentative {attempt} : erreur HTTP {e.response.status_code}")
if e.response.status_code == 403:
print(" → Blocage reçu, essayons le prochain proxy...")
except requests.exceptions.RequestException as e:
print(f"✗ Tentative {attempt} : erreur générale — {e}")
time.sleep(1) # Pause entre les tentatives
print(f"✗ Toutes les {max_retries} tentatives épuisées pour {url}")
return None
# Utilisation
result = fetch_with_retry("https://httpbin.org/ip")
if result:
print(result.json())
Notez le raise_for_status() — cette méthode lève automatiquement une exception pour les statuts HTTP 4xx et 5xx. Sans cela, le script considérera même une réponse avec le code 403 (blocage) ou 429 (limite de requêtes dépassée) comme réussie.
Proxy via des variables d'environnement : stockage sécurisé des données
La bibliothèque requests lit automatiquement les variables d'environnement HTTP_PROXY et HTTPS_PROXY. Cela permet de ne pas stocker les informations d'identification dans le code et de passer facilement d'un proxy à l'autre sans modifier le script.
Définition des variables dans le terminal (Linux/macOS) :
export HTTP_PROXY="http://user:[email protected]:8080" export HTTPS_PROXY="http://user:[email protected]:8080" export NO_PROXY="localhost,127.0.0.1"
Ou via un fichier .env avec la bibliothèque python-dotenv :
# Fichier .env (ajoutez à .gitignore !) HTTP_PROXY=http://user:[email protected]:8080 HTTPS_PROXY=http://user:[email protected]:8080
# Script Python
from dotenv import load_dotenv
import requests
import os
load_dotenv() # Chargement des variables depuis .env
# requests utilise automatiquement HTTP_PROXY et HTTPS_PROXY
response = requests.get("https://httpbin.org/ip")
print(response.json())
# Ou explicitement depuis les variables d'environnement :
proxies = {
"http": os.getenv("HTTP_PROXY"),
"https": os.getenv("HTTPS_PROXY"),
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
⚠️ Important : variable NO_PROXY
La variable NO_PROXY permet d'exclure certaines adresses du proxy. Assurez-vous d'y ajouter localhost et 127.0.0.1, afin que les requêtes locales ne passent pas par le proxy.
Scénarios réels : scraping de marketplaces, travail avec des API et automatisation
Examinons trois scénarios pratiques auxquels les développeurs sont souvent confrontés.
Scénario 1 : Scraping des prix d'un marketplace
Lors de la surveillance des prix sur Wildberries ou Ozon, il est important d'imiter le comportement d'un utilisateur réel : transmettre les bons en-têtes de navigateur, ajouter des délais entre les requêtes et faire tourner les IP. Pour cette tâche, les proxies de data center conviennent bien — ils sont rapides et peu coûteux pour le traitement de grandes quantités de données.
import requests
import time
import random
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "fr-FR,fr;q=0.9",
"Referer": "https://www.wildberries.ru/",
}
PROXIES = [
{"http": "http://user:[email protected]:8080",
"https": "http://user:[email protected]:8080"},
{"http": "http://user:[email protected]:8080",
"https": "http://user:[email protected]:8080"},
]
def get_product_price(article_id: int) -> dict:
"""Obtient le prix d'un produit par son identifiant sur Wildberries."""
url = f"https://card.wb.ru/cards/v1/detail?appType=1&curr=rub&nm={article_id}"
proxies = random.choice(PROXIES)
try:
resp = requests.get(url, headers=HEADERS, proxies=proxies, timeout=15)
resp.raise_for_status()
data = resp.json()
product = data["data"]["products"][0]
return {
"id": product["id"],
"name": product["name"],
"price": product["salePriceU"] / 100, # prix en kopecks
}
except (requests.RequestException, KeyError, IndexError) as e:
return {"error": str(e)}
# Scraping de plusieurs articles avec délai
articles = [12345678, 87654321, 11223344]
for article in articles:
result = get_product_price(article)
print(result)
time.sleep(random.uniform(1.5, 3.0)) # Délai aléatoire de 1.5 à 3 sec
Scénario 2 : Travail avec une API via un proxy
Certaines API limitent le nombre de requêtes depuis une seule IP (limitation de taux). La répartition des requêtes entre plusieurs proxies permet de contourner cette limitation :
import requests
import itertools
from typing import Optional
class ProxyAPIClient:
"""Client pour travailler avec l'API via rotation de proxy."""
def __init__(self, api_key: str, proxy_list: list):
self.api_key = api_key
self.proxy_cycle = itertools.cycle(proxy_list)
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
})
def _get_proxy(self) -> dict:
proxy = next(self.proxy_cycle)
return {"http": proxy, "https": proxy}
def get(self, url: str, **kwargs) -> Optional[dict]:
proxies = self._get_proxy()
try:
resp = self.session.get(url, proxies=proxies, timeout=10, **kwargs)
resp.raise_for_status()
return resp.json()
except requests.RequestException as e:
print(f"Requête API échouée : {e}")
return None
# Utilisation
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
client = ProxyAPIClient(api_key="your_api_key", proxy_list=proxy_list)
data = client.get("https://api.example.com/products")
Scénario 3 : Tests géolocalisés
Les spécialistes du marketing et les experts SEO vérifient souvent comment un site apparaît depuis différentes régions. Avec des proxies des emplacements souhaités, ce processus peut être automatisé :
import requests
# Proxies de différentes régions
regional_proxies = {
"Moscou": "http://user:[email protected]:8080",
"Saint-Pétersbourg": "http://user:[email protected]:8080",
"Novossibirsk": "http://user:[email protected]:8080",
"États-Unis": "http://user:[email protected]:8080",
}
url = "https://example.com/prices"
for region, proxy_url in regional_proxies.items():
proxies = {"http": proxy_url, "https": proxy_url}
try:
resp = requests.get(url, proxies=proxies, timeout=15)
print(f"[{region}] Statut : {resp.status_code} | "
f"Taille : {len(resp.content)} octets")
except requests.RequestException as e:
print(f"[{region}] Erreur : {e}")
Quel type de proxy choisir pour votre tâche
Le choix du type de proxy influence directement le succès de votre projet. Un proxy de data center peu coûteux peut bien fonctionner pour certaines tâches et échouer complètement pour d'autres. Voici un guide pratique pour le choix :
| Tâche | Type de proxy | Pourquoi |
|---|---|---|
| Scraping de marketplaces (Wildberries, Ozon) | Résidentiels | Ont l'apparence d'utilisateurs normaux, sont moins souvent bloqués |
| Scraping de données ouvertes, d'actualités | Data centers | Rapides, peu coûteux, suffisamment anonymes |
| Travail avec l'API Facebook, Instagram | Mobiles | Les réseaux sociaux font davantage confiance aux IP mobiles |
| Tests géolocalisés | Résidentiels avec géotargeting | Géolocalisation précise, IP réelles de la région souhaitée |
| Scraping à fort volume (10k+ requêtes/heure) | Data centers (pool) | Vitesse et coût pour de grands volumes |
| Authentification et travail avec des comptes | Résidentiels ou mobiles | Moins de déclencheurs pour les systèmes anti-fraude |
Pour les tâches où la fiabilité maximale et le risque minimal de blocage lors du travail avec des sites sécurisés sont importants, les développeurs choisissent le plus souvent des proxies mobiles — ils utilisent des adresses IP de véritables opérateurs mobiles (MTS, Beeline, Megafon), qui sont très rarement ajoutées aux listes de blocage.
Liste de vérification des proxies avant utilisation
- ✅ Vérifiez l'IP via
httpbin.org/ip— votre adresse réelle est-elle visible ? - ✅ Vérifiez la vitesse — le temps de réponse ne doit pas dépasser 2-3 secondes
- ✅ Assurez-vous que le proxy n'est pas dans les listes de blocage via
blocklist.deouipqualityscore.com - ✅ Vérifiez la géolocalisation via
ipinfo.io— correspond-elle à la région attendue ? - ✅ Testez sur le site cible avec une seule requête avant de lancer le script complet
- ✅ Assurez-vous que le trafic HTTPS passe également par le proxy (les deux clés dans le dictionnaire)
Conclusion
Configurer un proxy dans Python requests n'est pas difficile, mais cela nécessite une attention aux détails. Les principaux principes à retenir : indiquez toujours les deux clés (http et https) dans le dictionnaire proxy, utilisez Session pour les scénarios multi-étapes, gérez toujours les erreurs et les délais d'attente, et stockez les informations d'identification dans des variables d'environnement, pas dans le code.
Pour le scraping industriel avec des milliers de requêtes par jour, une liste manuelle de proxies n'est pas suffisante — une rotation est nécessaire. Si vous scrapez des marketplaces sécurisées comme Wildberries ou Ozon, travaillez avec des réseaux sociaux ou testez la géolocalisation, nous vous recommandons d'essayer des proxies résidentiels — ils offrent un haut niveau de confiance de la part des systèmes anti-bots et prennent en charge la rotation automatique des IP via un point d'accès unique, ce qui simplifie considérablement le code de votre script.