Configurer Scrapy : utiliser un proxy différent par requête avec meta proxy
Ce guide explique comment connecter les proxies HTTP de Socks5Proxy avec meta proxy dans Scrapy, faire tourner une liste récupérée via l'API à chaque requête avec un middleware de téléchargement, et régler la vitesse selon le site cible.
Comment Scrapy gère-t-il les proxies ?
Scrapy active HttpProxyMiddleware par défaut. Ajoutez l'adresse proxy dans meta['proxy'] : la requête utilisera ce proxy, avec l'identifiant/mot de passe converti en en-tête Proxy-Authorization. Les URLs HTTPS passent par un tunnel CONNECT.
Le gestionnaire de téléchargement par défaut de Scrapy prend en charge HTTP. Pour SOCKS5 (nécessitant un gestionnaire spécifique), utilisez plutôt le port HTTP de la même IP - le site cible verra la même IP.
L'exemple utilise l'IP 198.51.100.23, port HTTP 8000, identifiant spx_user et mot de passe spx_pass.
pip install scrapy
scrapy startproject shop
cd shopComment ajouter un proxy à une requête ?
Ajoutez proxy à meta lors de la création de Request dans start_requests. Utilisez la même méthode pour ajouter proxy lors de la demande de la page suivante dans le callback. Pour maintenir la même session, passez la valeur proxy de meta de la réponse précédente pour conserver la même IP.
import scrapy
PROXY = "http://spx_user:spx_pass@198.51.100.23:8000"
class IpCheckSpider(scrapy.Spider):
name = "ip_check"
def start_requests(self):
yield scrapy.Request("https://api.ipify.org?format=json", meta={"proxy": PROXY})
def parse(self, response):
self.logger.info("seen as %s", response.json()["ip"])Comment faire tourner les IP à chaque requête ?
Créez un middleware de téléchargement pour insérer le proxy suivant dans chaque requête, permettant ainsi de faire tourner les proxies sans modifier le code du spider. Le middleware ci-dessous récupère la liste via l'API au démarrage et conserve le proxy dans meta s'il est déjà présent, permettant une utilisation combinée de sessions fixes et de rotation.
Placez la clé API dans settings.py ou lisez-la depuis les variables d'environnement. Enregistrez-la avec un nombre inférieur à celui de HttpProxyMiddleware (750) pour que le middleware de base traite l'adresse insérée par le middleware.
import itertools
import requests
class RotatingProxyMiddleware:
def __init__(self, key):
text = requests.get(
"https://socks5proxy.io/api/v1/proxies/download",
params={"format": "user:pass@ip:port", "protocol": "http"},
headers={"Authorization": "Bearer " + key},
timeout=30,
).text
self.pool = itertools.cycle(["http://" + l.strip() for l in text.splitlines() if l.strip()])
@classmethod
def from_crawler(cls, crawler):
return cls(crawler.settings.get("SPX_API_KEY"))
def process_request(self, request, spider):
if "proxy" not in request.meta: # keep a proxy that was set on purpose (sticky sessions)
request.meta["proxy"] = next(self.pool)SPX_API_KEY = "YOUR_API_KEY"
DOWNLOADER_MIDDLEWARES = {
"shop.middlewares.RotatingProxyMiddleware": 740,
}
CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 16
DOWNLOAD_DELAY = 0.25
AUTOTHROTTLE_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [429, 500, 502, 503, 504, 522, 524, 408]
DOWNLOAD_TIMEOUT = 30Comment ajuster la vitesse et les nouvelles tentatives ?
CONCURRENT_REQUESTS_PER_DOMAIN est le nombre de requêtes simultanées pouvant être envoyées au même domaine. Plus vous avez d'IP de proxy, plus vous pouvez augmenter cette valeur sans surcharger une seule IP. Par exemple, avec 500 IP et 50 requêtes simultanées, chaque IP sera utilisée en moyenne une fois toutes les 10 requêtes.
Avec AUTOTHROTTLE activé, l'intervalle entre les requêtes est ajusté automatiquement en fonction de la vitesse de réponse. Si les réponses 429 augmentent, utilisez RETRY_HTTP_CODES pour réessayer, mais vous pouvez aussi supprimer le proxy dans le meta pour que le middleware utilise le proxy suivant lors de la nouvelle tentative.
Une étape de vérification dans votre pipeline pour confirmer que les résultats collectés sont des pages réelles et non des pages de confirmation améliorera la qualité des données. Si des éléments requis sont manquants, relancez la requête avec une autre IP.
- Ajustez le nombre de requêtes simultanées par domaine en fonction du nombre d'IP
- AUTOTHROTTLE pour ajuster l'intervalle en fonction de la vitesse de réponse
- 429 et 5xx : Réessayez avec une autre IP
- Filtrer les écrans de vérification par inspection du contenu
Qu'est-ce qui change avec une connexion par autorisation IP ?
Si vous enregistrez l'IPv4 du serveur exécutant Scrapy dans le tableau de bord, vous pouvez utiliser http://198.51.100.23:8000 sans nom d'utilisateur ni mot de passe dans meta proxy. Vous pouvez également obtenir la liste au format ip:port. Si vous exécutez des spiders sur plusieurs serveurs, enregistrez toutes les IP des serveurs. Vous pouvez enregistrer jusqu'à 10 IP.
Comment regrouper les requêtes de session sous une même IP ?
Pour les flux nécessitant une continuité des cookies, comme la connexion ou le panier, utilisez la même IP. Ajoutez directement le proxy meta à la première requête, puis passez la valeur proxy de response.meta dans le callback pour la requête suivante, afin que le middleware de rotation ne la change pas. Utilisez également la clé meta cookiejar de Scrapy pour séparer les cookies par session.
En associant un proxy et un jeu de cookies par session, vous pouvez exécuter plusieurs sessions simultanément sans interférences.
import scrapy
PROXIES = [
"http://spx_user:spx_pass@198.51.100.23:8000",
"http://spx_user:spx_pass@198.51.100.24:8000",
]
class SessionSpider(scrapy.Spider):
name = "sessions"
def start_requests(self):
for i, proxy in enumerate(PROXIES):
yield scrapy.Request("https://example.com/", meta={"proxy": proxy, "cookiejar": i}, dont_filter=True)
def parse(self, response):
# follow-up request keeps the same proxy and the same cookie jar
yield response.follow("/account", meta={"proxy": response.meta["proxy"], "cookiejar": response.meta["cookiejar"]}, callback=self.parse_account)
def parse_account(self, response):
self.logger.info("status %s via %s", response.status, response.meta["proxy"])Comment mettre temporairement de côté un proxy en échec ?
Si vous recevez une réponse 403 ou 429 dans le middleware process_response, vous pouvez mettre le proxy en pause pendant quelques minutes et renvoyer la requête via un autre proxy. Copiez la requête, supprimez le proxy dans meta, activez dont_filter, puis retournez-la pour que le prochain proxy prenne le relais.
Gérez simplement la liste des proxies en pause et leur heure de réutilisation avec un dictionnaire. Évitez de réutiliser la même IP immédiatement pour laisser le temps aux sites cibles de lever leurs restrictions.
Comment optimiser la bande passante avec Scrapy ?
Scrapy ne télécharge que le HTML (bien moins de bande passante qu'un navigateur). Le middleware HttpCompressionMiddleware (activé par défaut) réduit encore le trafic. Ainsi, les plans partagés permettent de collecter beaucoup de pages.
Activez HTTPCACHE_ENABLED en développement pour éviter de répéter les requêtes. En prod, gardez le filtre de doublons et utilisez dont_filter si nécessaire.
Il est aussi important de limiter la portée des liens suivis pour éviter de s'éparpiller sur des sous-pages inutiles. Une portée de collecte bien définie réduit le temps, la bande passante et la charge sur le site cible.
Comment résoudre les problèmes fréquents ?
Si vous recevez une réponse 407, vérifiez l'ID et le mot de passe dans l'URL. Si le mot de passe contient des caractères spéciaux, encodez-les en pourcentage. TunnelError signifie que le proxy n'a pas reçu de requête CONNECT, vérifiez que vous utilisez bien le port HTTP.
Si toutes les requêtes utilisent la même IP, cela peut être dû à l'ordre du middleware HttpProxyMiddleware ou à l'utilisation directe de meta proxy dans le spider. Vérifiez l'ordre dans settings.py et le code du spider.