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 proxy | request.meta["proxy"], défini par votre middleware |
|---|---|
| Ordre des middlewares | Votre middleware sous 750, la priorité du HttpProxyMiddleware intégré |
| Identifiants | Dans l’URL proxy ; Scrapy les transforme en un en-tête Proxy-Authorization |
| SOCKS5 | Pas 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 |
| Rotation | Nouvelle sortie par nouvelle connexion ; un ID de session unique par requête donne normalement une IP différente |
| Respect des usages | ROBOTSTXT_OBEY, CONCURRENT_REQUESTS_PER_DOMAIN, AUTOTHROTTLE_ENABLED |
| Retries | 429 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.
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 NoneQuels 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.
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 = 45Comment 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.
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.
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 ?
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.