Lorsque l’on utilise le protocole HTTP, les requêtes qui échouent sont une réalité inévitable à laquelle il faut faire face. Dans le domaine du développement web, un statut 200 indique une bonne réponse. Cependant, nous n’obtenons pas toujours un 200, et ce guide vous aidera à comprendre comment gérer ces codes d’état autres que 200.
Selon Mozilla, les codes de statut peuvent être répartis dans les catégories suivantes :
- 100-199: Réponses informatives
- 200-299: Réponses positives
- 300-399: Messages de redirection
- 400-499: Messages d’erreur du client
- 500-599: Messages d’erreur du serveur
Que sont les codes d’état ?
Les codes d’erreur sont importants. Lors de la création de programmes côté client tels que les scanners de sites web, nous devons principalement nous concentrer sur les codes d’état de l’ordre de 400+ et 500+. Les codes dans les 400 couvrent généralement les erreurs côté client telles que les problèmes d’authentification, les limitations de débit, les dépassements de délai et la tristement célèbre erreur 404 : Fichier introuvable. Les codes de plus de 500 correspondent généralement à des problèmes de serveur.
Depuis des décennies, Mozilla documente les normes de développement web du W3C et de l’IETF. Vous trouverez ci-dessous une liste de codes d’erreur courants que vous pourriez rencontrer. Cette liste n’est pas exhaustive. Ces erreurs proviennent de la documentation officielle de Mozilla. En fonction de votre site cible, vos codes peuvent différer légèrement, mais la logique devrait rester la même.
Code de statut | Signification | Description |
---|---|---|
400 | Mauvaise demande | Vérifiez le format de votre demande |
401 | Non autorisé | Vérifiez votre clé API |
403 | Interdit | Vous ne pouvez pas accéder à ces données |
404 | Non trouvé | Le site ou le point de terminaison n’existe pas |
408 | Délai d’attente de la demande | La demande a expiré, réessayez |
429 | Trop de demandes | Ralentissez vos demandes |
500 | Erreur de serveur interne | Erreur générique du serveur, demande de réessai |
501 | Non mise en œuvre | Le serveur ne prend pas encore en charge cette fonction |
502 | Mauvaise passerelle | Échec de la réponse d’un serveur en amont |
503 | Service indisponible | Le serveur est temporairement hors service, réessayer plus tard |
504 | Délai d’attente de la passerelle | Le délai d’attente d’un serveur en amont a expiré |
Stratégies de réessai
Lors de la mise en œuvre d’un mécanisme de relance, vous pouvez utiliser des bibliothèques prédéfinies telles que HTTPAdapter et Tenacity. Selon le cas, vous pouvez même écrire votre propre logique de relance.
En règle générale, nous voulons une limite de tentatives et une stratégie pour revenir en arrière. Nous avons besoin d’une limite pour ne pas être pris dans une boucle infinie de tentatives. Nous devons nous retirer petit à petit afin de respecter le serveur hôte. Lorsque vos demandes arrivent trop vite, elles vous bloquent ou submergent le serveur.
- Limites de réessais: Vous devez fixer une limite. Après X tentatives, votre scraper abandonnera.
- Algorithme de backoff: Celui-ci est relativement simple. Il s’agit de commencer par un petit backoff et de l’augmenter à chaque nouvel essai. Nous voulons commencer par 0,3, puis passer à 0,6, 1,2 et ainsi de suite.
Nous voulons réessayer nos demandes jusqu’à une certaine limite. Après chaque échec, nous voulons attendre un peu plus longtemps.
Adaptateur HTTPA
Avec HTTPAdapter, nous devons configurer trois choses : total
, backoff_factor
, et status_forcelist
. allowed_methods
n’est pas vraiment une exigence, mais il rend notre code plus sûr en aidant à définir nos conditions de réessai. Dans le code ci-dessous, nous utilisons httpbin pour forcer automatiquement une erreur et déclencher notre logique de réessai.
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
#create a session
session = requests.Session()
#configure retry settings
retry = Retry(
total=3, #maximum retries
backoff_factor=0.3, #time between retries (exponential backoff)
status_forcelist=(429, 500, 502, 503, 504), #status codes to trigger a retry
allowed_methods={"GET", "POST"}
)
#mount the adapter with our custom settings
adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)
#actually make a request with our retry logic
try:
print("Making a request with retry logic...")
response = session.get("https://httpbin.org/status/500")
response.raise_for_status()
print("✅ Request successful:", response.status_code)
except requests.exceptions.RequestException as e:
print("❌ Request failed after retries:", e)
Une fois l’objet Session
créé, nous procédons comme suit :
- Créez un objet
Retry
et définissez les éléments suivants :total
: limite maximale pour la relance d’une demande.backoff_factor
: Temps d’attente entre les tentatives. Ce facteur s’ajuste de manière exponentielle au fur et à mesure que le nombre de tentatives augmente.status_forcelist
: Une liste de mauvais codes d’état. Tout code figurant dans cette liste déclenchera automatiquement une nouvelle tentative.
- Créer un objet
HTTPAdapter
avec notre variableretry
:adapter = HTTPAdapter(max_retries=retry)
. - Une fois l’
adaptateur
créé, nous le montons sur les méthodes HTTP et HTTPS à l’aide desession.mount().
Lorsque vous exécutez ce code, nos trois tentatives(total=3
) s’exécutent et vous obtenez le résultat suivant.
Making a request with retry logic...
❌ Request failed after retries: HTTPSConnectionPool(host='httpbin.org', port=443): Max retries exceeded with url: /status/500 (Caused by ResponseError('too many 500 error responses'))
Ténacité
Vous pouvez également utiliser Tenacity, une bibliothèque de relance open source populaire pour Python. Elle n’est pas limitée à HTTP, mais elle nous offre un moyen expressif et compréhensible d’implémenter les tentatives.
Vous devez d’abord l’installer.
pip install tenacity
Une fois installé, nous créons un décorateur et l’utilisons pour envelopper une fonction de requête. Avec notre décorateur @retry
, nous ajoutons les arguments stop
, wait
et retry
.
import requests
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type, RetryError
#define a retry strategy
@retry(
stop=stop_after_attempt(3), #retry up to 3 times
wait=wait_exponential(multiplier=0.3), #exponential backoff
retry=retry_if_exception_type(requests.exceptions.RequestException), #retry on request failures
)
def make_request():
print("Making a request with retry logic...")
response = requests.get("https://httpbin.org/status/500")
response.raise_for_status()
print("✅ Request successful:", response.status_code)
return response
# Attempt to make the request
try:
make_request()
except RetryError as e:
print("❌ Request failed after all retries:", e)
La logique et les paramètres sont très similaires à notre premier exemple avec HTTPAdapter.
stop=stop_after_attempt(3)
: Ceci indique àtenacity
d’abandonner après 3 tentatives infructueuses.wait=wait_exponential(multiplier=0.3)
utilise la même attente que précédemment. Il recule également de manière exponentielle, comme précédemment.retry=retry_if_exception_type(requests.exceptions.RequestException)
indique àtenacity
d’utiliser cette logique à chaque fois qu’uneRequestException
se produit.make_request()
effectue une requête vers notre point d’accès à l’erreur. Elle reçoit tous les traits du décorateur que nous avons créé au-dessus d’elle.
Lorsque vous exécutez ce code, vous obtenez un résultat similaire.
Making a request with retry logic...
Making a request with retry logic...
Making a request with retry logic...
❌ Request failed after all retries: RetryError[<Future at 0x75e762970760 state=finished raised HTTPError>]
Construisez votre propre mécanisme de réessai
Vous pouvez également créer votre propre mécanisme de relance. Lorsqu’il s’agit de code personnalisé, c’est souvent la meilleure approche. Avec une quantité de code relativement faible, nous pouvons obtenir le même effet que celui obtenu avec ces bibliothèques.
Dans le code ci-dessous, nous devons importer sleep
pour notre backoff exponentiel. Nous définissons à nouveau notre configuration : total
, backoff_factor
et bad_codes
. Nous utilisons ensuite une boucle while
pour maintenir notre logique de réessai. Tant que
nous avons encore des essais et que nous n’avons pas réussi, nous tentons la requête.
import requests
from time import sleep
#create a session
session = requests.Session()
#define our retry settings
total = 3
backoff_factor = 0.3
bad_codes = [429, 500, 502, 503, 504]
#try counter and success boolean
current_tries = 0
success = False
#attempt until we succeed or run out of tries
while current_tries < total and not success:
try:
print("Making a request with retry logic...")
response = session.get("https://httpbin.org/status/500")
if response.status_code in bad_codes:
raise requests.exceptions.HTTPError(f"Received {response.status_code}, triggering retry")
print("✅ Request successful:", response.status_code)
success = True
except requests.exceptions.RequestException as e:
print(f"❌ Request failed: {e}, retries left: {total-current_tries}")
sleep(backoff_factor)
backoff_factor = backoff_factor * 2
current_tries+=1
La logique réelle est ici gérée par une simple boucle while.
- Si
response.status_code
fait partie de notre liste debad_codes
, nous levons une exception. - Si une demande n’aboutit pas, nous
- Imprime un message d’erreur sur la console.
sleep(backoff_factor)
attend avant d’envoyer la demande suivante.backoff_factor = backoff_factor * 2
double notrebackoff_factor
pour le prochain essai.- Nous incrémentons
current_tries
pour ne pas rester indéfiniment dans la boucle.
Voici la sortie de notre logique de réessai personnalisée.
Making a request with retry logic...
❌ Request failed: Received 500, triggering retry, retries left: 3
Making a request with retry logic...
❌ Request failed: Received 500, triggering retry, retries left: 2
Making a request with retry logic...
❌ Request failed: Received 500, triggering retry, retries left: 1
Dépasser les blocages
Dans la nature, certains sites vont vous bloquer. La meilleure pratique consiste à toujours utiliser un proxy pour les requêtes Python. Avec un proxy, votre demande passe par une machine différente. Cela permet de protéger votre identité et d’éviter que votre adresse IP ne soit bloquée par votre site cible. Nous disposons même d’un guide détaillé sur la manière de contourner les blocages d’IP. Nos proxys résidentiels sont conçus pour vous permettre de surmonter ces difficultés.
Conclusion
Vous savez maintenant comment gérer les échecs des requêtes HTTP en Python. Que vous écriviez un scraper, un client API ou des outils d’automatisation, vous savez comment gérer ces problèmes. Pour éviter toutes sortes d’échecs de requêtes, nous avons développé des produits tels que l’API Web Unlocker et le Scraping Browser. Ces outils gèrent automatiquement les mesures anti-bots, les défis CAPTCHA et les blocages d’IP, garantissant ainsi un scrapping web efficace et sans faille, même pour les sites web les plus difficiles.
Inscrivez-vous maintenant et commencez votre essai gratuit dès aujourd’hui.