Retour au blog

Configuration des proxies dans le framework Scrapy : guide complet avec exemples de code

Guide complet sur l'intégration de proxies dans Scrapy : de la configuration de base aux méthodes avancées de rotation des adresses IP avec des exemples de code fonctionnel.

📅14 février 2026
```html

Scrapy est l'un des frameworks Python les plus puissants pour le web scraping, mais sans une configuration correcte du proxy, vos scrapers seront bloqués après quelques minutes de fonctionnement. Dans ce guide, je vais vous montrer toutes les façons d'intégrer un proxy dans Scrapy : de la configuration la plus simple aux méthodes avancées de rotation des adresses IP avec gestion automatique des erreurs.

Le matériel est basé sur une expérience réelle de scraping de grandes plateformes de commerce électronique et de sites protégés. Vous obtiendrez des exemples de code prêts à l'emploi que vous pourrez immédiatement utiliser dans vos projets.

Pourquoi Scrapy sans proxy reçoit des blocages

Les sites modernes utilisent une protection multi-niveaux contre le scraping. Même si vous avez configuré l'User-Agent et des délais entre les requêtes, votre adresse IP révèle l'automatisation par plusieurs signes :

  • Fréquence des requêtes : une IP effectue 100+ requêtes par minute — un signe évident de bot
  • Modèles de comportement : navigation séquentielle des pages sans transitions aléatoires
  • Absence de JavaScript : Scrapy n'exécute pas de JS, ce qui est facilement détectable
  • Géolocalisation : accès depuis un centre de données au lieu d'un réseau domestique

Le résultat — un ban par IP pendant plusieurs heures ou jours. Les marketplaces (Amazon, Wildberries, Ozon), les réseaux sociaux et les sites avec Cloudflare utilisent une protection particulièrement agressive. Les proxies résolvent ce problème en distribuant les requêtes entre de nombreuses adresses IP.

Important : Même avec des proxies, il est nécessaire de respecter les limites de taux. Vitesse recommandée : 1-3 requêtes par seconde par IP. Pour un scraping à haute vitesse, utilisez un pool de 50+ proxies avec rotation.

Configuration de base du proxy dans Scrapy

La manière la plus simple consiste à spécifier le proxy directement dans les paramètres de l'araignée. Cette méthode convient pour les tests ou le scraping de petits volumes de données avec un seul serveur proxy.

Méthode 1 : Via meta dans la requête

import scrapy

class MySpider(scrapy.Spider):
    name = 'example'
    start_urls = ['https://example.com']
    
    def start_requests(self):
        proxy = 'http://username:password@proxy.example.com:8080'
        
        for url in self.start_urls:
            yield scrapy.Request(
                url=url,
                callback=self.parse,
                meta={'proxy': proxy}
            )
    
    def parse(self, response):
        # Votre logique de scraping
        self.log(f'Scraped {response.url} via {response.meta["proxy"]}')

Le format du proxy dépend du protocole et de la méthode d'authentification :

  • http://proxy.example.com:8080 — sans authentification
  • http://user:pass@proxy.example.com:8080 — avec login/mot de passe
  • socks5://user:pass@proxy.example.com:1080 — proxy SOCKS5

Méthode 2 : Configuration globale dans settings.py

# settings.py

# Proxy HTTP pour toutes les requêtes
HTTPPROXY_ENABLED = True
HTTPPROXY_AUTH_ENCODING = 'utf-8'

# Configuration via des variables d'environnement
HTTP_PROXY = 'http://username:password@proxy.example.com:8080'
HTTPS_PROXY = 'http://username:password@proxy.example.com:8080'

Cette méthode est pratique pour des tests rapides, mais ne convient pas pour la production : pas de rotation d'IP, si le proxy tombe, tout le scraper s'arrête, impossible d'utiliser différents proxies pour différents sites.

Création d'un middleware Proxy personnalisé

Pour le scraping en production, un middleware personnalisé est nécessaire pour gérer le pool de proxies, traiter les erreurs et changer automatiquement d'IP. Voici une implémentation de base :

# middlewares.py

import random
from scrapy import signals
from scrapy.exceptions import NotConfigured

class RandomProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
    
    @classmethod
    def from_crawler(cls, crawler):
        # Chargement de la liste des proxies depuis les paramètres
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        
        if not proxy_list:
            raise NotConfigured('PROXY_LIST not configured')
        
        return cls(proxy_list)
    
    def process_request(self, request, spider):
        # Choisir un proxy aléatoire dans le pool
        proxy = random.choice(self.proxy_list)
        request.meta['proxy'] = proxy
        
        spider.logger.info(f'Using proxy: {proxy}')
    
    def process_exception(self, request, exception, spider):
        # En cas d'erreur, essayer un autre proxy
        proxy = random.choice(self.proxy_list)
        request.meta['proxy'] = proxy
        
        spider.logger.warning(
            f'Proxy error, switching to: {proxy}'
        )
        
        return request

Maintenant, configurons l'utilisation du middleware dans settings.py :

# settings.py

# Liste des proxies (peut être chargée depuis un fichier ou une API)
PROXY_LIST = [
    'http://user1:pass1@proxy1.example.com:8080',
    'http://user2:pass2@proxy2.example.com:8080',
    'http://user3:pass3@proxy3.example.com:8080',
    # ... ajoutez 50+ proxies pour une rotation efficace
]

# Activation du middleware
DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.RandomProxyMiddleware': 350,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 400,
}

# Tentatives répétées en cas d'erreurs
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 429]

Rotation des proxies : trois méthodes fonctionnelles

Le choix aléatoire des proxies (comme dans l'exemple ci-dessus) est la méthode la plus simple, mais pas la plus efficace. Examinons trois stratégies de rotation pour différents scénarios.

Méthode 1 : Round-robin (rotation séquentielle)

Les proxies sont choisis en cercle. Convient pour une répartition uniforme de la charge :

class RoundRobinProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
        self.current_index = 0
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        return cls(proxy_list)
    
    def process_request(self, request, spider):
        # Prendre le prochain proxy en cercle
        proxy = self.proxy_list[self.current_index]
        self.current_index = (self.current_index + 1) % len(self.proxy_list)
        
        request.meta['proxy'] = proxy

Méthode 2 : Rotation intelligente avec blacklist

Suivre les proxies problématiques et les exclure temporairement de la rotation :

import time
from collections import defaultdict

class SmartProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
        self.proxy_errors = defaultdict(int)
        self.blacklist = set()
        self.blacklist_timeout = 300  # 5 minutes
        self.blacklist_time = {}
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        return cls(proxy_list)
    
    def get_working_proxies(self):
        # Retirer de la blacklist les proxies dont le délai a expiré
        current_time = time.time()
        expired = [
            proxy for proxy, ban_time in self.blacklist_time.items()
            if current_time - ban_time > self.blacklist_timeout
        ]
        
        for proxy in expired:
            self.blacklist.discard(proxy)
            self.proxy_errors[proxy] = 0
        
        # Retourner les proxies fonctionnels
        return [p for p in self.proxy_list if p not in self.blacklist]
    
    def process_request(self, request, spider):
        working_proxies = self.get_working_proxies()
        
        if not working_proxies:
            spider.logger.error('All proxies are blacklisted!')
            return
        
        proxy = random.choice(working_proxies)
        request.meta['proxy'] = proxy
    
    def process_response(self, request, response, spider):
        # Si nous avons reçu un blocage — ajouter à la blacklist
        if response.status in [403, 429, 503]:
            proxy = request.meta.get('proxy')
            self.proxy_errors[proxy] += 1
            
            if self.proxy_errors[proxy] >= 3:
                self.blacklist.add(proxy)
                self.blacklist_time[proxy] = time.time()
                spider.logger.warning(
                    f'Proxy {proxy} blacklisted for {self.blacklist_timeout}s'
                )
        
        return response

Méthode 3 : Rotation via l'API du fournisseur

De nombreux fournisseurs de proxies (y compris les proxies résidentiels) proposent un point de terminaison rotatif — une URL qui change automatiquement d'IP à chaque requête :

# settings.py

# Point de terminaison unique avec rotation automatique
ROTATING_PROXY = 'http://username:password@rotating.proxy.com:8080'

# Middleware simple
class RotatingProxyMiddleware:
    def __init__(self, proxy):
        self.proxy = proxy
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy = crawler.settings.get('ROTATING_PROXY')
        return cls(proxy)
    
    def process_request(self, request, spider):
        # Une seule URL, mais chaque requête utilise une nouvelle IP
        request.meta['proxy'] = self.proxy

C'est la méthode la plus pratique pour la production : pas besoin de gérer un pool de proxies, le fournisseur s'occupe de la qualité des IP et remplace les problématiques. Cela fonctionne particulièrement bien avec les proxies résidentiels, où le pool d'IP peut atteindre des millions d'adresses.

Authentification : login/mot de passe vs liste blanche IP

Les fournisseurs de proxies proposent deux méthodes d'authentification. Le choix influence la vitesse de connexion et la facilité de configuration.

Authentification User:Pass

Le login et le mot de passe sont transmis dans l'URL du proxy. Scrapy les convertit automatiquement en en-tête HTTP Proxy-Authorization :

proxy = 'http://username:password@proxy.example.com:8080'
request.meta['proxy'] = proxy

# Scrapy ajoutera automatiquement l'en-tête :
# Proxy-Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

Avantages : fonctionne depuis n'importe quelle IP, facile à changer les proxies dans le code.
Inconvénients : léger overhead sur chaque requête (~50-100ms), informations d'identification en clair dans le code.

Authentification par liste blanche IP

Vous ajoutez l'IP de votre serveur à la liste blanche chez le fournisseur, aucune authentification n'est requise :

proxy = 'http://proxy.example.com:8080'  # sans login/mot de passe
request.meta['proxy'] = proxy

Avantages : plus rapide de 50-100ms, plus sûr (pas de credentials dans le code).
Inconvénients : fonctionne uniquement depuis certaines IP, besoin de mettre à jour la liste blanche lors du changement de serveur.

Recommandation pour la production :

Utilisez la liste blanche IP pour le scraping depuis des serveurs dédiés (AWS, Google Cloud, Hetzner). Pour le développement et les tests depuis une machine locale — utilisez l'authentification user:pass.

Gestion des erreurs et changement automatique d'IP

Même avec des proxies de qualité, il y aura des erreurs : des délais d'attente, des refus de connexion, des blocages. Une bonne gestion des erreurs est cruciale pour le bon fonctionnement du scraper.

Gestion des statuts HTTP

class ProxyMiddleware:
    def process_response(self, request, response, spider):
        # Codes pour lesquels il faut changer de proxy et réessayer
        ban_codes = [403, 407, 429, 503]
        
        if response.status in ban_codes:
            proxy = request.meta.get('proxy')
            spider.logger.warning(
                f'Got {response.status} from {proxy}, retrying...'
            )
            
            # Marquer pour réessayer avec un nouveau proxy
            request.meta['dont_retry'] = False
            request.meta['proxy'] = self.get_new_proxy()
            
            return request
        
        return response

Gestion des exceptions réseau

from twisted.internet.error import TimeoutError, ConnectionRefusedError
from scrapy.exceptions import IgnoreRequest

class ProxyMiddleware:
    def process_exception(self, request, exception, spider):
        # Erreurs de connexion au proxy
        proxy_errors = (
            TimeoutError,
            ConnectionRefusedError,
            ConnectionLost,
        )
        
        if isinstance(exception, proxy_errors):
            proxy = request.meta.get('proxy')
            spider.logger.error(
                f'Proxy {proxy} connection failed: {exception}'
            )
            
            # Changer de proxy et essayer à nouveau
            request.meta['proxy'] = self.get_new_proxy()
            return request
        
        # Pour les autres erreurs, utiliser le traitement standard
        return None

Détection des blocages par le contenu

Certains sites renvoient un HTTP 200, mais affichent un captcha ou une page de blocage :

class ProxyMiddleware:
    def process_response(self, request, response, spider):
        # Indicateurs de blocage dans le contenu
        ban_indicators = [
            'captcha',
            'access denied',
            'blocked',
            'unusual traffic',
            'robot check',
        ]
        
        body_text = response.text.lower()
        
        if any(indicator in body_text for indicator in ban_indicators):
            spider.logger.warning(
                f'Ban page detected from {request.meta.get("proxy")}'
            )
            
            # Changer de proxy et réessayer
            request.meta['proxy'] = self.get_new_proxy()
            return request
        
        return response

Quel type de proxy choisir pour Scrapy

Le choix du type de proxy dépend du site cible, du budget et de la vitesse de scraping requise. Voici une comparaison des principales options :

Type de proxy Vitesse Coût Quand utiliser
Proxy de centre de données Élevée (50-200ms) Faible ($1-3/IP) Sites simples sans protection, API, outils internes
Proxies résidentiels Moyenne (300-800ms) Moyenne ($5-15/GB) E-commerce, réseaux sociaux, sites avec Cloudflare, géotargeting
Proxies mobiles Faible (500-1500ms) Élevée ($50-150/IP) Applications mobiles, Instagram, TikTok, protection maximale

Recommandations pour le choix

Pour le scraping des marketplaces (Amazon, Wildberries, Ozon, AliExpress) — uniquement des proxies résidentiels. Ces sites bloquent agressivement les centres de données. Une rotation et un géotargeting sont nécessaires (par exemple, des IP russes pour Wildberries).

Pour le scraping de sites d'actualités, blogs, forums — des proxies de centre de données conviennent. La protection est minimale, la vitesse et le coût du trafic sont importants.

Pour le scraping de sites avec Cloudflare — des proxies résidentiels sont obligatoires. Les centres de données sont détectés presque instantanément par Cloudflare. Ajoutez à Scrapy la bibliothèque cloudscraper pour contourner les défis JS.

Pour le scraping de Google Search, outils SEO — des proxies résidentiels avec géotargeting. Google affiche des résultats différents selon les pays et les villes.

Conseil : Commencez avec un pool de 10 proxies résidentiels pour les tests. Si vous recevez des blocages — augmentez le pool à 50-100 IP. Pour un scraping à haute vitesse (1000+ requêtes/minute), utilisez un point de terminaison rotatif avec un pool de 10,000+ IP.

Techniques avancées : sessions et IP collantes

Lors du scraping de certains sites, il est nécessaire de conserver une IP tout au long de la session (authentification, panier d'achats, formulaires multi-étapes). Voici comment réaliser des sessions collantes dans Scrapy.

IP collante pour un domaine

from urllib.parse import urlparse

class StickyProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
        # Dictionnaire : domaine -> proxy
        self.domain_proxy_map = {}
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        return cls(proxy_list)
    
    def process_request(self, request, spider):
        # Extraire le domaine de l'URL
        domain = urlparse(request.url).netloc
        
        # Si ce domaine a déjà un proxy — l'utiliser
        if domain in self.domain_proxy_map:
            proxy = self.domain_proxy_map[domain]
        else:
            # Sinon, choisir un nouveau et mémoriser
            proxy = random.choice(self.proxy_list)
            self.domain_proxy_map[domain] = proxy
            spider.logger.info(f'Assigned {proxy} to {domain}')
        
        request.meta['proxy'] = proxy

IP collante avec délai de session

Une variante plus avancée : le proxy est lié au domaine pendant une certaine période (par exemple, 10 minutes), puis change :

import time
from urllib.parse import urlparse

class SessionProxyMiddleware:
    def __init__(self, proxy_list, session_timeout=600):
        self.proxy_list = proxy_list
        self.session_timeout = session_timeout  # 10 minutes
        # Dictionnaire : domaine -> (proxy, temps de création)
        self.sessions = {}
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        timeout = crawler.settings.getint('PROXY_SESSION_TIMEOUT', 600)
        return cls(proxy_list, timeout)
    
    def get_proxy_for_domain(self, domain):
        current_time = time.time()
        
        # Vérifier s'il y a une session active
        if domain in self.sessions:
            proxy, created_at = self.sessions[domain]
            
            # Si la session n'a pas expiré — utiliser le même proxy
            if current_time - created_at < self.session_timeout:
                return proxy
        
        # Créer une nouvelle session avec un nouveau proxy
        new_proxy = random.choice(self.proxy_list)
        self.sessions[domain] = (new_proxy, current_time)
        
        return new_proxy
    
    def process_request(self, request, spider):
        domain = urlparse(request.url).netloc
        proxy = self.get_proxy_for_domain(domain)
        request.meta['proxy'] = proxy

Intégration avec le middleware des cookies

Pour des sessions complètes, il est nécessaire de synchroniser les proxies et les cookies. Scrapy stocke les cookies séparément pour chaque domaine, mais lors du changement de proxy, il faut nettoyer les cookies :

# settings.py

# Activer le middleware des cookies
COOKIES_ENABLED = True
COOKIES_DEBUG = False

# Middleware pour synchroniser proxies et cookies
class ProxyCookieMiddleware:
    def process_request(self, request, spider):
        # Obtenir le proxy actuel
        current_proxy = request.meta.get('proxy')
        
        # Si le proxy a changé — nettoyer les cookies
        previous_proxy = request.meta.get('previous_proxy')
        
        if previous_proxy and previous_proxy != current_proxy:
            # Nettoyer les cookies pour ce domaine
            jar = spider.crawler.engine.downloader.middleware.middlewares[0].jars
            domain = urlparse(request.url).netloc
            
            if domain in jar:
                jar[domain].clear()
                spider.logger.info(f'Cleared cookies for {domain}')
        
        request.meta['previous_proxy'] = current_proxy

Conclusion

Une configuration correcte des proxies dans Scrapy est la base d'un scraping stable sans blocages. Nous avons examiné tous les aspects clés : de l'intégration de base aux techniques avancées de rotation et de gestion des sessions.

Principales conclusions :

  • Pour la production, utilisez un middleware personnalisé avec une rotation intelligente et une blacklist des IP problématiques
  • Traitez tous les types d'erreurs : statuts HTTP, exceptions réseau, blocages par contenu
  • Choisissez le type de proxy en fonction de la tâche : centres de données pour des sites simples, résidentiels pour des sites protégés
  • Pour les sites avec authentification, utilisez des sessions collantes avec liaison du proxy au domaine
  • Commencez avec un pool de 10-50 proxies, évoluez avec l'augmentation de la charge

Si vous prévoyez de scraper des sites protégés (marketplaces, réseaux sociaux, sites avec Cloudflare), je recommande d'utiliser des proxies résidentiels — ils offrent une anonymité maximale et un risque minimal de blocages. Pour un scraping à haute vitesse, choisissez des fournisseurs avec un point de terminaison rotatif et un pool de 10,000 adresses IP.

Tous les exemples de code de cet article ont été testés sur Scrapy 2.x et sont prêts à être utilisés en production. Adaptez-les à vos besoins et évoluez au fur et à mesure de la croissance de votre projet.

```