Pourquoi une requête proxy a épuisé ses tentatives
Le problème
Un appel POST /api/proxy/ renvoie une erreur et aucune donnée. Le message est court et a toujours la même structure :
{
"error": "Download maxTry limit reached",
"total": 34.812,
"request": { "...": "..." }
}
Cette phrase se lit de la même façon, que chaque sortie ait été bloquée, que chaque sortie ait été inactive, ou que FourA ait récupéré la vraie page à presque chaque tentative et que vos propres règles validate l'aient rejetée. Ces trois cas nécessitent des correctifs opposés.
La réponse : attemptReport
Chaque appel Proxy ayant exécuté ses tentatives et échoué contient un objet attemptReport à côté de l'erreur, y compris lorsqu'il s'est terminé par "error": "Timeout". Un nom d'hôte non résolu échoue avant toute tentative, avec status: 0 et sans rapport. Il comptabilise ce que les tentatives ont réellement rencontré :
{
"error": "Download maxTry limit reached",
"attemptReport": {
"total": 25,
"noResponse": 0,
"defense": 0,
"contentRejected": 25,
"statusRejected": 0,
"other": 0,
"vendors": [],
"profilesTried": ["default"],
"summary": "25 attempt(s): 25 returned HTTP 200 with no defense present and were rejected only by your validate.data - the page was fetched, your content rule did not match it"
},
"total": 34.812
}
La chaîne error est délibérément inchangée, afin qu'un client effectuant une correspondance dessus continue de fonctionner. Lisez attemptReport.summary pour obtenir une réponse en une ligne, ou les compteurs si vous souhaitez effectuer des branchements logiques.
Champs
| Champ | Type | Ce qui est compté |
|---|---|---|
total |
integer | Tentatives effectuées |
noResponse |
integer | Le point de sortie n'a jamais répondu, ou le site n'a pas répondu à temps |
defense |
integer | Le site a répondu et un fournisseur de détection de bots a été identifié sur cette réponse |
contentRejected |
integer | HTTP 200, pas de détection de bots, rejeté uniquement par votre validate.data |
statusRejected |
integer | Le site a répondu, pas de détection de bots, rejeté par votre validate.status |
other |
integer | Réponse reçue, et aucun des cas ci-dessus |
vendors |
string[] | Tous les fournisseurs de détection de bots identifiés au cours de la tâche |
profilesTried |
string[] | Les profils de navigateur envoyés par la tâche, dans l'ordre de première utilisation. default signifie que la requête est partie exactement comme vous l'avez écrite. |
summary |
string | Une phrase générée à partir des compteurs. Convient pour les journaux ou pour un affichage utilisateur. |
Interprétation
contentRejected est élevé
Les pages sont bien arrivées. Votre règle validate.data ne correspondait pas à leur contenu.
C'est le cas que vous pouvez corriger vous-même, et c'est celui que tous les autres signaux masquent: les requêtes ressemblent à des échecs dans chaque métrique, alors que FourA délivrait du contenu réel tout le temps. Récupérez la page une fois via POST /api/single/ sans aucun validate, observez ce qui est réellement renvoyé, et réécrivez la règle en conséquence.
Une cause fréquente est une règle unique appliquée à un ensemble de pages qui ne sont pas toutes identiques. Un sélecteur présent sur les pages d'articles mais absent des pages vidéo échouera systématiquement sur une page vidéo, indéfiniment et à coût complet.
statusRejected est élevé
Le site a répondu et votre règle validate.status l'a rejeté. Si ces statuts sont 401, 403, 429 ou 503, le site refuse le client plutôt que d'indiquer que la page n'existe pas. Essayez:
- Un autre profil de navigateur (
browser,os,versionsur l'objetrequestinterne) exitCountriessi le contenu est restreint par zone géographiquePOST /api/browser/si le refus nécessite l'exécution de JavaScript pour être résolu
defense est élevé
Une détection de bots a été identifiée sur les réponses, et vendors indique laquelle. Consultez Contrôles de site pour savoir ce que FourA contourne aujourd'hui et ce qu'il se contente de signaler. Si le fournisseur n'est pas contourné sur cet endpoint, basculez l'appel vers POST /api/browser/ ou POST /api/auto/.
noResponse est élevé
Les points de sortie n'ont pas répondu du tout, aucune information n'a donc été obtenue sur la cible. Augmentez maxTries, augmentez timeout_ms, et vérifiez que l'URL est bien accessible depuis l'Internet public.
other est élevé
Le site a renvoyé un statut, aucune détection de bots n'a été identifiée, et la tentative a échoué pour une autre raison, comme une règle validate.headers. Une cible plus lente que votre timeout_ms est comptabilisée sous noResponse.
Rotation des profils de navigateur
Lorsqu'un site refuse le navigateur envoyé par FourA, Proxy cesse d'insister et essaie une autre famille issue du catalogue public de profils. Cela ne consomme aucune tentative supplémentaire: la rotation modifie ce qu'un retry envoie, mais ne change jamais le fait qu'il ait lieu ou non.
Proxy mémorise également, par site, la dernière famille ayant fonctionné après un refus du profil par défaut. Une requête ultérieure vers ce site peut démarrer directement avec cette famille dès sa première tentative, et FourA passe à la famille suivante dès que le site commence à refuser celle-ci également. Un navigateur mobile n'est mémorisé que pour les sites renvoyant du JSON.
profilesTried vous permet d'observer ce comportement. Une seule entrée signifie que chaque tentative a envoyé le même navigateur: default correspond à la requête telle qu'écrite, et un identifiant de catalogue correspond à la famille que FourA avait déjà mémorisée pour ce site. Plusieurs entrées indiquent que la rotation a eu lieu et que le site a refusé chacune d'elles, ce qui diffère du cas où aucune rotation n'a été effectuée.
Sur une réponse Proxy réussie, un champ profile apparaît uniquement lorsque la rotation a sélectionné un navigateur que vous n'aviez pas demandé:
{
"status": 200,
"data": "<!doctype html>...",
"proxy": "A1B2C3",
"profile": "...",
"total": 4.108
}
La valeur est un identifiant de catalogue issu de GET /api/profiles. Son absence signifie que la request a été envoyée exactement comme rédigée. Sa présence indique que le navigateur qui a fonctionné n'est pas celui que vous avez saisi, vous devez donc renvoyer cet identifiant via profile lors des appels suivants plutôt que de rejouer celui qui a échoué. Le dashboard Playground gère cela pour vous avec Carry.
Un paramètre explicite profile, browser, os ou version sur votre request n'est jamais écrasé. Il en va de même pour une request comportant votre propre header User-Agent ou Cookie, car une autorisation est liée à la signature qui l'a obtenue.
Lecture dans le code
import requests
API = "https://eu.api.foura.ai"
H = {"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"}
r = requests.post(f"{API}/api/proxy/", headers=H, json={
"maxTries": 5,
"request": {
"method": "GET",
"url": "https://example.com/product/42",
"validate": {"data": {"accept": ["Add to cart"]}},
},
}).json()
if "error" in r:
rep = r.get("attemptReport", {})
print(rep.get("summary", r["error"]))
if rep.get("contentRejected", 0) > rep.get("total", 0) / 2:
# The pages arrived. The validate rule is what threw them away.
raise SystemExit("validate.data did not match the real page")
if rep.get("defense", 0):
print("bot check met:", ", ".join(rep.get("vendors", [])))
Contenu associé
- Endpoints API : La référence complète des requêtes et réponses Proxy
- Vérifications de sites : Fournisseurs,
defenseet rejeu d'une autorisation - Résultats des requêtes : Comment une réponse rejetée est classifiée et facturée
- Problèmes courants : Autres problèmes et leurs solutions
- Choisir le bon endpoint : Quand Proxy n'est pas le bon outil