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 · Scrapy 2.x · middleware · AutoThrottle

Middleware proxy Scrapy pour proxies résidentiels rotatifs

Un middleware de téléchargement de 30 lignes qui choisit le pays et la session par requête, plus les réglages pour garder un crawl respectueux : robots.txt, concurrence par domaine, AutoThrottle et retries.

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

Réponse rapide

Comment utiliser des proxies rotatifs avec Scrapy ?

Réglez request.meta["proxy"] à http://USERNAME:[email protected]:7777 dans un petit middleware de téléchargement qui tourne avant le HttpProxyMiddleware de Scrapy ; Scrapy envoie alors les identifiants pour vous. Avec des proxies résidentiels CrawlRelay, le pays et la session sticky vont dans le nom d’utilisateur, donc un seul middleware suffit. Le trafic coûte 0,50 $US/GB à partir de 50 GB. Payable en crypto, sans KYC.

Configuration Scrapy en un clin d'œil

Où placer le proxyrequest.meta["proxy"], défini par votre middleware
Ordre des middlewaresVotre middleware sous 750, la priorité du HttpProxyMiddleware intégré
IdentifiantsDans l’URL proxy ; Scrapy les transforme en un en-tête Proxy-Authorization
SOCKS5Pas avec le gestionnaire HTTP/1.1 par défaut ; Scrapy 2.17+ supporte SOCKS via HttpxDownloadHandler optionnel. Le port HTTP 7777 est le plus simple
RotationNouvelle sortie par nouvelle connexion ; un ID de session unique par requête donne normalement une IP différente
Respect des usagesROBOTSTXT_OBEY, CONCURRENT_REQUESTS_PER_DOMAIN, AUTOTHROTTLE_ENABLED
Retries429 et 5xx sont dans le RETRY_HTTP_CODES par défaut ; Retry-After n’est pas lu par défaut

Comment écrire le middleware proxy ?

Placez la classe dans middlewares.py. Elle construit le nom d’utilisateur à partir des réglages et de trois clés meta optionnelles : proxy_country, proxy_session et proxy_ttl. Une requête sans proxy_session tourne ; une requête avec proxy_session="new" obtient une nouvelle session, donc normalement une IP de sortie différente ; une requête avec un ID de session fixé partage une sortie avec les autres requêtes du même flux. Le middleware recalcule son proxy à chaque passage, retries inclus, et laisse intact un proxy défini par un autre code. Si CRAWLRELAY_USER manque, le crawl s’arrête au lancement au lieu de tourner depuis votre propre IP.

middlewares.py
import uuid
from urllib.parse import quote


class CrawlRelayProxyMiddleware:
    """Sets request.meta["proxy"]; Scrapy's HttpProxyMiddleware then sends the credentials."""

    def __init__(self, user, password, country):
        self.user, self.password, self.country = user, password, country

    @classmethod
    def from_crawler(cls, crawler):
        s = crawler.settings
        if not s.get("CRAWLRELAY_USER"):
            # Fail loudly: NotConfigured would only disable the middleware and crawl from your own IP
            raise ValueError("CRAWLRELAY_USER is not set: refusing to crawl without the proxy")
        return cls(s.get("CRAWLRELAY_USER"), s.get("CRAWLRELAY_PASSWORD", ""), s.get("CRAWLRELAY_COUNTRY", "us"))

    def process_request(self, request, spider=None):
        if "proxy" in request.meta and not request.meta.get("_crawlrelay"):
            return None  # a proxy set by other code: leave it alone
        parts = [self.user, "country", request.meta.get("proxy_country", self.country)]
        session = request.meta.get("proxy_session")
        if session == "new":  # a fresh session for this request (and for each of its retries)
            session = uuid.uuid4().hex[:12]
        if session:
            parts += ["session", session, "ttl", str(request.meta.get("proxy_ttl", 10))]
        username = "-".join(parts)
        request.meta["proxy"] = "http://%s:%[email protected]:7777" % (
            quote(username, safe=""), quote(self.password, safe=""))
        request.meta["_crawlrelay"] = True
        return None

Quels réglages Scrapy garantissent un crawl proxifié rapide et respectueux ?

Les proxies répartissent vos requêtes sur plusieurs IPs, mais la cible voit quand même votre taux total. Ces réglages maintiennent chaque domaine à un rythme soutenable et laissent AutoThrottle ralentir seul quand les réponses ralentissent. Gardez ROBOTSTXT_OBEY = True, déjà réglé par les nouveaux projets Scrapy.

settings.py (extrait)
import os

BOT_NAME = "myproject"
ROBOTSTXT_OBEY = True
USER_AGENT = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"

CRAWLRELAY_USER = os.environ.get("CRAWLRELAY_USER")  # e.g. the username from your dashboard
CRAWLRELAY_PASSWORD = os.environ.get("CRAWLRELAY_PASSWORD")
CRAWLRELAY_COUNTRY = "us"

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.CrawlRelayProxyMiddleware": 350,  # runs before HttpProxyMiddleware (750)
}

CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 4
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1.0
AUTOTHROTTLE_MAX_DELAY = 30.0
AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0

RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [429, 500, 502, 503, 504, 522, 524, 408]
DOWNLOAD_TIMEOUT = 45

Comment garder une IP fixe pour la pagination et la faire tourner pour les pages d'articles ?

Passez un proxy_session fixe dans les requêtes d'un flux qui dépend de la page précédente, et aucun dans les pages indépendantes. L’araignée ci-dessous fonctionne sur books.toscrape.com, un site sandbox publié pour s'entraîner au scraping.

spiders/catalog.py
import scrapy


class CatalogSpider(scrapy.Spider):
    name = "catalog"
    start_urls = ["https://books.toscrape.com/"]

    def parse(self, response):
        for href in response.css("article.product_pod h3 a::attr(href)").getall():
            # Product pages are independent: rotating exits
            yield response.follow(href, callback=self.parse_item)
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            # The listing flow keeps one sticky exit for 30 minutes
            yield response.follow(next_page, callback=self.parse,
                                  meta={"proxy_session": "listing1", "proxy_ttl": 30})

    def parse_item(self, response):
        yield {
            "title": response.css("div.product_main h1::text").get(),
            "price": response.css("p.price_color::text").get(),
            "url": response.url,
        }

Est-ce que Scrapy réutilise les connexions, et cela empêche-t-il la rotation ?

Oui, Scrapy garde les connexions ouvertes et les réutilise par proxy et hôte cible, donc les requêtes consécutives sur une connexion partagent une même sortie. Pour la plupart des crawls, cela fonctionne : Scrapy ouvre jusqu’à CONCURRENT_REQUESTS_PER_DOMAIN connexions par site, et chaque nouvelle connexion obtient généralement une IP résidentielle différente.

Quand une requête doit sortir via une nouvelle IP, par exemple une tentative après un 502 dû à une sortie défaillante, renvoyez-la avec response.request.replace(meta={**response.meta, "proxy_session": "new"}, dont_filter=True). Le middleware crée un nouveau nom d'utilisateur de session, un nom d'utilisateur différent signifie un pool de connexions différent, donc Scrapy ouvre une nouvelle connexion et la passerelle attribue normalement une sortie différente.

Comment respecter Retry-After sur les réponses 429 avec Scrapy ?

Le système intégré de RetryMiddleware de Scrapy retente les 429 mais n’attend pas le délai indiqué par Retry-After. AutoThrottle ralentit le domaine progressivement, ce qui est généralement suffisant. Pour les API qui envoient de longues valeurs Retry-After, ajoutez un petit middleware qui lit l’en-tête et planifie la nouvelle tentative avec délai, ou réduisez CONCURRENT_REQUESTS_PER_DOMAIN pour ce domaine. La page 429 explique ce que signifie cet en-tête.

Comment utiliser un proxy avec scrapy-playwright ?

Pour les pages nécessitant JavaScript, scrapy-playwright transmet les options de lancement de Playwright via un réglage. Donnez les identifiants comme champs séparés : Chromium ignore les identifiants placés dans l’URL du serveur. Utilisez un contexte par session stable si vous rendez des flux déconnectés qui s’étendent sur plusieurs pages.

settings.py pour scrapy-playwright
import os

DOWNLOAD_HANDLERS = {
    "http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
    "https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
}
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"

PLAYWRIGHT_LAUNCH_OPTIONS = {
    "proxy": {
        "server": "http://gw.crawlrelay.com:7777",
        "username": os.environ.get("CRAWLRELAY_USER", "USERNAME") + "-country-us",
        "password": os.environ.get("CRAWLRELAY_PASSWORD", "PASSWORD"),
    },
}

Scrapy et proxies : questions

Ai-je besoin de scrapy-rotating-proxies ou d’une liste de proxies ?

Non. Ces paquets font tourner une liste d’adresses proxy individuelles et bannissent celles qui échouent. Avec une passerelle résidentielle, la rotation se fait derrière une seule adresse : chaque nouvelle connexion obtient généralement une IP résidentielle différente. Un seul middleware qui définit request.meta["proxy"] suffit.

Pourquoi Scrapy affiche-t-il TunnelError avec mon proxy ?

TunnelError: Could not open CONNECT tunnel signifie que le proxy a refusé ou échoué la requête CONNECT pour un site HTTPS. Le statut dans le message indique pourquoi : 407 correspond aux identifiants, 502 ou 504 au problème de sortie ou de cible. Consultez les pages 407 et 502.

Combien de requêtes simultanées par domaine devrais-je utiliser ?

Commencez avec 2 à 4 par domaine et laissez AutoThrottle ajuster. Augmentez ce nombre seulement si la part de réponses 429, 403 et 5xx reste stable, et surveillez les temps de réponse du site. Les grands sites tolèrent plus que les petits ; un petit site peut nécessiter un délai entre les requêtes et une seule connexion.

Dois-je désactiver ROBOTSTXT_OBEY pour obtenir plus de données ?

Non. robots.txt est la manière dont un site indique quels chemins les clients automatisés peuvent récupérer, et la norme RFC 9309 a standardisé ce format en 2022. L’ignorer augmente vos risques légaux et éthiques et viole notre Acceptable Use Policy lorsqu’il est utilisé pour accéder à du contenu que le site a exclu.

Combien de trafic utilise un crawl Scrapy ?

Scrapy télécharge uniquement le HTML, donc les pages coûtent généralement quelques dizaines de kilo-octets chacune, compressées. Vérifiez la valeur downloader/response_bytes dans les statistiques que Scrapy affiche en fin de crawl et divisez-la par le nombre de réponses. Le guide sur la bande passante transforme cela en un coût.

Intégrez des IP résidentielles dans votre projet Scrapy

5,94 $US pour 10 GB pour tester, 0,50 $US/GB depuis 50 GB.

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

Commencez