Gestion des erreurs
Crawler à grande échelle, c'est accepter que des erreurs arrivent. Anticipez-les dès le départ et vous passerez votre temps à livrer des fonctionnalités, pas à surveiller des tentatives.
Trois classes d'erreurs
Chaque erreur Crawlbase appartient à l'une de ces trois catégories, et chacune appelle une réponse différente.
429, 500, 503, 522, 599. Toujours réessayer avec backoff.404, 410, 451. Ne réessayez pas - la page n'existe vraiment pas ou n'est pas accessible. Marquez l'URL comme échouée et passez à la suite.401, 402, 403, 422. Réessayer ne servira à rien - corrigez la requête, le token ou le compte.Modèle de tentatives en production
Le modèle qui tient la charge : backoff exponentiel avec jitter complet, plafonnement des tentatives et destination de lettres mortes pour les échecs terminaux.
import time, random, logging
from crawlbase import CrawlingAPI
api = CrawlingAPI({'token': 'YOUR_TOKEN'})
log = logging.getLogger('crawler')
TRANSIENT = {429, 500, 503, 522, 599}
TERMINAL = {401, 402, 403, 404, 410, 422, 451}
def crawl(url, max_attempts=5, base=0.5, cap=30):
for attempt in range(max_attempts):
res = api.get(url)
status = res['status_code']
if status == 200 and res['pc_status'] == 200:
return res
if status in TERMINAL or res['pc_status'] in TERMINAL:
log.warning(f'Terminal error {status}/{res['pc_status']} for {url}')
raise PermanentFailure(url, status)
# Transient - sleep with full jitter, then retry
wait = min(cap, base * (2 ** attempt))
wait = random.uniform(0, wait)
log.info(f'Attempt {attempt+1} got {status}; sleeping {wait:.2f}s')
time.sleep(wait)
raise RuntimeError(f'Exhausted retries for {url}')
class PermanentFailure(Exception): passconst { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: process.env.CRAWLBASE_TOKEN });
const TRANSIENT = new Set([429, 500, 503, 522, 599]);
const TERMINAL = new Set([401, 402, 403, 404, 410, 422, 451]);
async function crawl(url, { maxAttempts = 5, base = 500, cap = 30000 } = {}) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const res = await api.get(url);
const status = res.statusCode;
if (status === 200 && res.pcStatus === 200) return res;
if (TERMINAL.has(status) || TERMINAL.has(res.pcStatus)) {
throw new Error(`Permanent failure \${status} for \${url}`);
}
const wait = Math.random() * Math.min(cap, base * 2 ** attempt);
await new Promise(r => setTimeout(r, wait));
}
throw new Error(`Exhausted retries for \${url}`);
}File de lettres mortes
Quand les tentatives sont épuisées, ne jetez pas l'URL en silence. Envoyez-la quelque part où un humain peut la consulter.
- Pour les utilisateurs du Crawler API : les échecs sont automatiquement réessayés jusqu'au nombre que vous avez configuré, puis livrés à votre webhook avec les métadonnées de l'échec. Aucune DLQ à construire.
- Pour les utilisateurs en API directe : en cas d'échec terminal, écrivez l'URL + le statut + le dernier corps de réponse dans une file ou une table séparée. Examinez chaque semaine.
Plafonnez les tentatives à environ 5. Une URL qui échoue 5 fois de suite échouera presque certainement 50 fois. Gardez les cycles pour le nouveau travail.
Quoi surveiller
Les quatre signaux que tout système utilisant Crawlbase devrait tracer :
| Signal | D'où il provient | Alerter quand |
|---|---|---|
| Taux de succès | pc_status == 200 / total | < 95 % pendant 10 min |
| Latence P95 | durée de la requête | > 15 s en continu |
| Taux de 429 | histogramme des statuts HTTP | > 5 % de façon soutenue - réduisez la concurrence |
| Distribution du nombre de tentatives | votre boucle de tentatives | P95 > 2 - quelque chose se dégrade en amont |
Étiquetez chaque métrique avec le domaine cible pour repérer quand un seul site empoisonne vos chiffres globaux.
Rendre les tentatives sûres
Les requêtes Crawlbase sont intrinsèquement idempotentes : un GET sur la même URL avec le même token renvoie le même type de résultat à chaque fois. Vous pouvez réessayer librement sans vous soucier d'effets secondaires en double.
Deux remarques :
- Async + store : si vous avez utilisé
&async=true&store=true, chaque tentative consomme un crédit et crée un nouveaurid. Dédupliquez de votre côté si nécessaire. - Webhooks : les webhooks du Crawler API peuvent être livrés plus d'une fois en cas d'échec. Rendez votre gestionnaire de webhook idempotent sur
rid.