Pourquoi la rotation de proxy est essentielle pour le scraping à grande échelle
Lorsque vous passez de centaines à des millions de requêtes, une seule IP proxy devient une responsabilité. Les sites Web suivent les modèles de requête par IP et vont activer ou interdire les adresses qui dépassent le comportement normal de navigation. Rotation proxy distribue vos demandes sur de nombreux IP de sorte qu'aucune adresse n'accumule suffisamment d'activité pour déclencher la détection.
La différence entre une approche de rotation naïve et une stratégie bien conçue peut signifier la différence entre un taux de réussite de 95 % et un taux de réussite de 40 %. Ce guide couvre les quatre principales stratégies de rotation, quand utiliser chacune, et comment les mettre en œuvre avec des exemples de code de travail.
Cet article fait partie de notre Guide complet des produits de scraping Web Groupe. Commencez là si vous avez besoin de concepts de proxy fondamental.
Stratégie 1: Rotation par demande
L'approche la plus simple: chaque requête obtient une nouvelle IP. Ceci est idéal pour la collecte de données apatrides où chaque demande est indépendante — recherche de prix, requêtes SERP, page de produits récupérés.
Quand utiliser
- Scraping de grands catalogues où chaque URL est indépendante
- Surveillance SERP sur de nombreux mots clés
- Toute tâche qui ne nécessite pas de cookies ou d'état de session
Mise en œuvre de Python
import requests
PROXY = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
def fetch_with_rotation(urls: list[str]) -> list[str]:
"""Each request automatically gets a fresh IP via the rotating gateway."""
results = []
session = requests.Session()
session.proxies = {"http": PROXY, "https": PROXY}
for url in urls:
try:
resp = session.get(url, timeout=30)
resp.raise_for_status()
results.append(resp.text)
except requests.RequestException as e:
print(f"Failed {url}: {e}")
results.append(None)
return results
# Each request through gate.proxyhat.com uses a different IP
pages = fetch_with_rotation([
"https://example.com/product/1",
"https://example.com/product/2",
"https://example.com/product/3",
])Mise en œuvre de Node.js
const HttpsProxyAgent = require('https-proxy-agent');
const fetch = require('node-fetch');
const agent = new HttpsProxyAgent('http://USERNAME:PASSWORD@gate.proxyhat.com:8080');
async function fetchWithRotation(urls) {
const results = [];
for (const url of urls) {
try {
const res = await fetch(url, { agent, timeout: 30000 });
results.push(await res.text());
} catch (err) {
console.error(`Failed ${url}: ${err.message}`);
results.push(null);
}
}
return results;
}Aller à la mise en œuvre
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"time"
)
func fetchWithRotation(urls []string) []string {
proxyURL, _ := url.Parse("http://USERNAME:PASSWORD@gate.proxyhat.com:8080")
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
Timeout: 30 * time.Second,
}
results := make([]string, len(urls))
for i, u := range urls {
resp, err := client.Get(u)
if err != nil {
fmt.Printf("Failed %s: %v\n", u, err)
continue
}
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
results[i] = string(body)
}
return results
}Stratégie 2 : Rotation dans le temps (Stratégies)
Certaines tâches de grattage nécessitent la même IP pour une série de requêtes connexes: navigation d'une liste paginée, navigation d'une commande en plusieurs étapes ou maintien d'une session connectée. Rotation chronométrée (ou sessions collantes) conserve la même IP attribuée pour une durée définie, généralement de 1 à 30 minutes.
Quand utiliser
- Paginé rampement (page 1, 2, 3... des résultats)
- Tâches nécessitant des cookies ou une persistance de session
- Simulation de modèles de navigation réalistes
Plan de mise en œuvre
Avec ProxyHat, les sessions collantes sont contrôlées via le paramètre session dans vos identifiants. Chaque ID de session unique maintient la même IP pour la durée configurée:
import requests
import uuid
def create_sticky_session(duration_label: str = "10m"):
"""Create a session that maintains the same IP."""
session_id = uuid.uuid4().hex[:8]
proxy = f"http://USERNAME-session-{session_id}:PASSWORD@gate.proxyhat.com:8080"
session = requests.Session()
session.proxies = {"http": proxy, "https": proxy}
return session
# All requests through this session use the same IP
session = create_sticky_session()
page1 = session.get("https://example.com/listings?page=1")
page2 = session.get("https://example.com/listings?page=2")
page3 = session.get("https://example.com/listings?page=3")Node.js Session Sticky
const HttpsProxyAgent = require('https-proxy-agent');
const fetch = require('node-fetch');
const crypto = require('crypto');
function createStickyAgent() {
const sessionId = crypto.randomBytes(4).toString('hex');
return new HttpsProxyAgent(
`http://USERNAME-session-${sessionId}:PASSWORD@gate.proxyhat.com:8080`
);
}
async function crawlPaginated(baseUrl, pages) {
const agent = createStickyAgent(); // Same IP for all pages
const results = [];
for (let page = 1; page <= pages; page++) {
const res = await fetch(`${baseUrl}?page=${page}`, { agent });
results.push(await res.text());
}
return results;
}Stratégie 3 : Rotation fondée sur l'échec
Au lieu de tourner sur chaque demande ou sur une minuterie, rotation fondée sur les défaillances continue à utiliser une IP jusqu'à ce qu'elle soit bloquée, puis change. Cela maximise la valeur de chaque IP en l'utilisant aussi longtemps qu'elle fonctionne.
Quand utiliser
- Objectifs comportant des seuils de blocage imprévisibles
- Grattage conscient du budget où vous voulez le maximum de requêtes par IP
- Long-courage rampe où certains IPs des dernières heures et d'autres minutes
Mise en œuvre avec l'échec automatique
import requests
import uuid
from time import sleep
class FailureBasedRotator:
"""Rotates proxy only when the current IP fails."""
BLOCK_SIGNALS = [403, 429, 503]
MAX_RETRIES = 3
def __init__(self):
self.session_id = None
self.requests_on_current_ip = 0
self._new_session()
def _new_session(self):
self.session_id = uuid.uuid4().hex[:8]
self.requests_on_current_ip = 0
proxy = f"http://USERNAME-session-{self.session_id}:PASSWORD@gate.proxyhat.com:8080"
self.session = requests.Session()
self.session.proxies = {"http": proxy, "https": proxy}
def fetch(self, url: str) -> str | None:
for attempt in range(self.MAX_RETRIES):
try:
resp = self.session.get(url, timeout=30)
if resp.status_code in self.BLOCK_SIGNALS:
print(f"Blocked (HTTP {resp.status_code}) after "
f"{self.requests_on_current_ip} requests. Rotating...")
self._new_session()
sleep(1)
continue
resp.raise_for_status()
self.requests_on_current_ip += 1
return resp.text
except requests.RequestException:
self._new_session()
sleep(1)
return None
# Usage
rotator = FailureBasedRotator()
for url in urls:
html = rotator.fetch(url)Aller Implémentation avec échec
package main
import (
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"net/http"
"net/url"
"time"
)
type FailureRotator struct {
client *http.Client
sessionID string
reqCount int
}
func NewFailureRotator() *FailureRotator {
r := &FailureRotator{}
r.rotate()
return r
}
func (r *FailureRotator) rotate() {
b := make([]byte, 4)
rand.Read(b)
r.sessionID = hex.EncodeToString(b)
r.reqCount = 0
proxyStr := fmt.Sprintf("http://USERNAME-session-%s:PASSWORD@gate.proxyhat.com:8080", r.sessionID)
proxyURL, _ := url.Parse(proxyStr)
r.client = &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
Timeout: 30 * time.Second,
}
}
func (r *FailureRotator) Fetch(target string) (string, error) {
for attempt := 0; attempt < 3; attempt++ {
resp, err := r.client.Get(target)
if err != nil {
r.rotate()
time.Sleep(time.Second)
continue
}
defer resp.Body.Close()
if resp.StatusCode == 403 || resp.StatusCode == 429 || resp.StatusCode == 503 {
fmt.Printf("Blocked after %d requests. Rotating...\n", r.reqCount)
r.rotate()
time.Sleep(time.Second)
continue
}
body, _ := io.ReadAll(resp.Body)
r.reqCount++
return string(body), nil
}
return "", fmt.Errorf("all retries exhausted for %s", target)
}Stratégie 4 : Rotation géodistribuée
Lors de la suppression de contenu localisé — résultats de la recherche, prix, disponibilité — vous avez besoin de PI de lieux géographiques spécifiques. Rotation géodistribuée assigne des PI de pays ou de villes cibles pour obtenir des données locales précises.
Quand utiliser
- Mise au rebut du SERP pour les classements de recherche locaux
- Contrôle des prix dans les régions
- Vérification de la disponibilité du contenu (contenu limité par des critères géographiques)
- Vérification sur des marchés spécifiques
Mise en œuvre avec ciblage par pays
import requests
from concurrent.futures import ThreadPoolExecutor
COUNTRIES = ["us", "gb", "de", "fr", "jp"]
def fetch_localized(url: str, country: str) -> dict:
"""Fetch URL through a proxy in the specified country."""
proxy = f"http://USERNAME-country-{country}:PASSWORD@gate.proxyhat.com:8080"
try:
resp = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)
return {"country": country, "status": resp.status_code, "body": resp.text}
except requests.RequestException as e:
return {"country": country, "status": 0, "error": str(e)}
def scrape_all_regions(url: str) -> list[dict]:
"""Fetch the same URL from multiple countries in parallel."""
with ThreadPoolExecutor(max_workers=len(COUNTRIES)) as executor:
futures = [executor.submit(fetch_localized, url, c) for c in COUNTRIES]
return [f.result() for f in futures]
# Get localized pricing from 5 countries simultaneously
results = scrape_all_regions("https://example.com/product/pricing")
for r in results:
print(f"{r['country'].upper()}: HTTP {r['status']}")Voir les options de ciblage disponibles sur le Emplacements de ProxyHat page.
La combinaison des stratégies : l'approche hybride
Dans la pratique, les projets de démolition à grande échelle combinent plusieurs stratégies. Voici un modèle qui utilise la rotation par demande pour la découverte, les séances collantes pour le rampage profond et le recul basé sur l'échec:
import requests
import uuid
from enum import Enum
class RotationMode(Enum):
PER_REQUEST = "per_request"
STICKY = "sticky"
FAILURE_BASED = "failure_based"
class HybridRotator:
def __init__(self, mode: RotationMode = RotationMode.PER_REQUEST):
self.mode = mode
self.session_id = None
self.failure_count = 0
self._init_session()
def _init_session(self):
if self.mode == RotationMode.PER_REQUEST:
proxy = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
else:
self.session_id = self.session_id or uuid.uuid4().hex[:8]
proxy = f"http://USERNAME-session-{self.session_id}:PASSWORD@gate.proxyhat.com:8080"
self.session = requests.Session()
self.session.proxies = {"http": proxy, "https": proxy}
def force_rotate(self):
"""Force a new IP regardless of mode."""
self.session_id = uuid.uuid4().hex[:8]
self.failure_count = 0
self._init_session()
def fetch(self, url: str) -> str | None:
try:
resp = self.session.get(url, timeout=30)
if resp.status_code in [403, 429, 503]:
self.failure_count += 1
if self.failure_count >= 2:
self.force_rotate()
return None
self.failure_count = 0
return resp.text
except requests.RequestException:
self.failure_count += 1
if self.failure_count >= 2:
self.force_rotate()
return None
# Discovery phase: rotate every request
discovery = HybridRotator(RotationMode.PER_REQUEST)
sitemap_urls = [discovery.fetch(url) for url in seed_urls]
# Deep crawl phase: sticky sessions per site section
crawler = HybridRotator(RotationMode.STICKY)
for section_url in section_urls:
pages = [crawler.fetch(f"{section_url}?page={i}") for i in range(1, 11)]
crawler.force_rotate() # New IP for next sectionComparaison de la stratégie de rotation
| Stratégie | Meilleur pour | Taux de réussite | Efficacité de la propriété intellectuelle | Complexité |
|---|---|---|---|---|
| Par demande | Collecte en vrac d ' apatrides | Élevé | Faible | Faible |
| Temps/Sticky | Tâches dépendantes de la session | Moyenne-haute | Moyenne | Faible |
| Échec | Objectifs à difficultés variables | Moyenne | Élevé | Moyenne |
| Géodistribué | Collecte de données localisées | Élevé | Moyenne | Moyenne |
| Hybride | Projets complexes en plusieurs phases | Plus haut | Élevé | Élevé |
Meilleures pratiques de rotation à l'échelle
- Respectez robots.txt. Rotation ne vous dispense pas d'être un bon citoyen. Vérifiez les règles et les directives d'honneur.
- Ajouter des retards réalistes. Même avec la rotation, éclater des centaines de demandes par seconde semble robotique. Ajouter 0,5-2 secondes de retard aléatoire entre les demandes.
- Surveiller les taux de réussite. Suivre les codes d'état HTTP par site cible. Une baisse en dessous de 90% signifie que votre rotation a besoin d'un réglage.
- Combiner avec la rotation de l'en-tête. La rotation des IP à elle seule ne suffit pas. Rotation des chaînes Utilisateur-Agent et autres en-têtes pour éviter détection fondée sur les empreintes digitales.
- Utilisez le recul sur les échecs. Quand une IP est bloquée, attendez avant de réessayer. Le recul exponentiel (1s, 2s, 4s, 8s) empêche de gaspiller des demandes sur des cibles temporairement hostiles.
Pour comprendre le nombre d'IP dont vous avez besoin pour soutenir votre stratégie de rotation, voir Combien de proxies avez-vous besoin pour le scraping?. Pour un aperçu complet de l'architecture de grattage, visitez notre Guide complet des produits de scraping Web.
Prêt à mettre en oeuvre ces stratégies? Regardez Python SDK, Numéro SDKou Allez au SDK pour l'intégration proxy prête à la production, ou explorer Plans de tarification ProxyHat pour commencer.
Foire aux questions
Quelle est la meilleure stratégie de rotation par procuration pour le grattage du web?
La rotation par demande est la plus sûre par défaut pour la plupart des tâches de grattage. Il assure que chaque demande utilise une IP différente, ce qui rend la détection des motifs beaucoup plus difficile. Pour les tâches nécessitant une persistance de session (pagination, flux de connexion), utilisez plutôt des sessions collantes.
À quelle vitesse devrais-je faire tourner les proxies ?
Pour la rotation par demande, chaque demande obtient automatiquement une nouvelle IP. Pour les séances collantes, 5-10 minutes est un bon défaut. La durée optimale dépend de la cible — les sites agressifs peuvent nécessiter des séances plus courtes (1-2 minutes), tandis que les sites clémentes tolèrent plus de 30 minutes.
Puis-je combiner différentes stratégies de rotation?
Oui, et vous devriez pour des projets complexes. Utilisez la rotation par demande pour la découverte et la collection d'URL, les sessions collantes pour le rampage profond et la rotation basée sur l'échec comme un recul lorsque les IP sont bloqués. L'approche hybride de ce guide montre comment.
ProxyHat gère-t-il la rotation automatiquement?
Oui. Chaque demande via la passerelle ProxyHat (gate.proxyhat.com:8080) reçoit automatiquement une IP différente de la piscine résidentielle. Pour les sessions collantes, ajoutez un paramètre de session à vos identifiants. Aucune gestion manuelle de la liste IP n'est nécessaire.






