// Python

Faire des requêtes HTTP en Python avec requests

Faire une requête HTTP en Python consiste à dialoguer avec un serveur web : récupérer une page, interroger une API, envoyer des données. La bibliothèque de référence pour cela est requests, réputée pour sa simplicité. Ce guide la couvre de façon exhaustive : requêtes GET et POST, passage de paramètres, headers, envoi et réception de JSON, authentification, gestion des erreurs et des timeouts, sessions persistantes, téléchargement de fichiers, et enfin l’alternative moderne httpx.

Installer requests

requests ne fait pas partie de la bibliothèque standard : on l’installe avec pip.

pip install requests

Une fois installée, on l’importe simplement :

import requests

La requête GET de base

Une requête GET récupère des données. requests.get(url) renvoie un objet Response riche d’informations.

import requests

reponse = requests.get("https://api.github.com")

print(reponse.status_code)   # 200 si tout va bien
print(reponse.headers["Content-Type"])   # application/json; ...
print(reponse.text[:80])     # les premiers caractères du corps

Les attributs les plus utiles de l’objet Response :

  • status_code : le code HTTP (200, 404, 500…)
  • text : le corps de la réponse sous forme de chaîne
  • content : le corps sous forme d’octets (pour les fichiers binaires)
  • headers : un dictionnaire des en-têtes de réponse
  • url : l’URL finale (après d’éventuelles redirections)

Passer des paramètres d’URL

Plutôt que de construire l’URL à la main avec des ? et des &, on passe un dictionnaire à l’argument params. requests s’occupe de l’encodage.

import requests

parametres = {"q": "python", "page": 2}
reponse = requests.get("https://httpbin.org/get", params=parametres)

print(reponse.url)
# https://httpbin.org/get?q=python&page=2

C’est plus sûr : les caractères spéciaux (espaces, accents) sont automatiquement échappés.

Recevoir du JSON

La plupart des API renvoient du JSON. La méthode .json() de l’objet Response le convertit directement en dictionnaire ou liste Python.

import requests

reponse = requests.get("https://api.github.com/users/python")
donnees = reponse.json()

print(donnees["name"])          # Python
print(donnees["public_repos"])  # nombre de dépôts publics

Inutile d’importer le module json : requests gère la conversion. Attention toutefois, .json() lève une exception si le corps n’est pas du JSON valide.

La requête POST

Une requête POST envoie des données au serveur, typiquement pour créer une ressource ou soumettre un formulaire.

Envoyer des données de formulaire

Avec l’argument data, les données sont envoyées au format formulaire (application/x-www-form-urlencoded).

import requests

formulaire = {"nom": "Léa", "email": "[email protected]"}
reponse = requests.post("https://httpbin.org/post", data=formulaire)

print(reponse.json()["form"])
# {'email': '[email protected]', 'nom': 'Léa'}

Envoyer du JSON

Pour envoyer un corps JSON (le cas le plus courant avec les API REST), on utilise l’argument json. requests sérialise le dictionnaire et ajoute le bon en-tête Content-Type.

import requests

charge = {"titre": "Mon article", "publie": True}
reponse = requests.post("https://httpbin.org/post", json=charge)

print(reponse.json()["json"])
# {'publie': True, 'titre': 'Mon article'}

Pour tout savoir sur la conception de ces API, consultez notre guide de l’API REST.

Personnaliser les headers

On passe un dictionnaire à l’argument headers, par exemple pour définir un User-Agent ou un jeton d’API.

import requests

entetes = {
    "User-Agent": "MonApp/1.0",
    "Accept": "application/json",
}
reponse = requests.get("https://httpbin.org/headers", headers=entetes)

print(reponse.json()["headers"]["User-Agent"])   # MonApp/1.0

L’authentification

requests prend en charge plusieurs schémas d’authentification.

Authentification basique

On passe un tuple (utilisateur, mot_de_passe) à l’argument auth.

import requests

reponse = requests.get(
    "https://httpbin.org/basic-auth/lea/secret",
    auth=("lea", "secret"),
)
print(reponse.status_code)   # 200

Jeton Bearer (API modernes)

Le plus fréquent aujourd’hui : un jeton placé dans l’en-tête Authorization.

import requests

jeton = "abc123xyz"
entetes = {"Authorization": f"Bearer {jeton}"}
reponse = requests.get("https://httpbin.org/bearer", headers=entetes)

print(reponse.json())   # {'authenticated': True, 'token': 'abc123xyz'}

Gérer les erreurs et les timeouts

Une requête peut échouer : serveur injoignable, délai dépassé, code d’erreur HTTP. Un code robuste anticipe ces cas.

Toujours définir un timeout

Sans timeout, une requête peut se bloquer indéfiniment. Définissez-en toujours un.

import requests

try:
    reponse = requests.get("https://httpbin.org/delay/10", timeout=3)
except requests.exceptions.Timeout:
    print("La requête a dépassé le délai de 3 secondes")

Vérifier le code de statut

raise_for_status() lève une exception si le code indique une erreur (4xx ou 5xx).

import requests

try:
    reponse = requests.get("https://httpbin.org/status/404", timeout=5)
    reponse.raise_for_status()
    donnees = reponse.json()
except requests.exceptions.HTTPError as err:
    print("Erreur HTTP :", err)
except requests.exceptions.RequestException as err:
    print("Erreur réseau :", err)

RequestException est la classe mère de toutes les exceptions de requests : l’attraper couvre timeouts, erreurs DNS, problèmes de connexion, etc.

Les sessions persistantes

Un objet Session réutilise la connexion TCP et conserve les cookies et en-têtes entre les requêtes. C’est plus rapide et plus pratique dès qu’on enchaîne plusieurs appels au même serveur.

import requests

with requests.Session() as session:
    session.headers.update({"Authorization": "Bearer abc123"})

    # Les deux requêtes partagent en-têtes et cookies
    r1 = session.get("https://httpbin.org/cookies/set/session/actif")
    r2 = session.get("https://httpbin.org/cookies")

    print(r2.json())   # {'cookies': {'session': 'actif'}}

Le bloc with garantit la fermeture propre de la session à la fin.

Télécharger un fichier

Pour un fichier volumineux, on active le mode stream=True et on écrit le contenu par blocs afin de ne pas tout charger en mémoire.

import requests

url = "https://httpbin.org/image/png"

with requests.get(url, stream=True, timeout=10) as reponse:
    reponse.raise_for_status()
    with open("image.png", "wb") as fichier:
        for bloc in reponse.iter_content(chunk_size=8192):
            fichier.write(bloc)

print("Téléchargement terminé")

iter_content renvoie les données par morceaux de 8 Ko, ce qui permet de télécharger des fichiers de plusieurs gigaoctets sans saturer la RAM.

L’alternative moderne : httpx

httpx est une bibliothèque plus récente, dont l’API est presque identique à celle de requests, mais qui ajoute le support de HTTP/2 et surtout de l’asynchrone (async/await).

pip install httpx

En mode synchrone, la transition est immédiate :

import httpx

reponse = httpx.get("https://api.github.com", timeout=5)
print(reponse.status_code)
print(reponse.json()["current_user_url"])

Et en mode asynchrone, pour lancer de nombreuses requêtes en parallèle :

import asyncio
import httpx

async def recuperer(url):
    async with httpx.AsyncClient() as client:
        reponse = await client.get(url, timeout=5)
        return reponse.status_code

print(asyncio.run(recuperer("https://api.github.com")))

Retenez la règle simple : requests reste parfait pour du code synchrone classique ; passez à httpx si vous avez besoin d’asynchrone ou de HTTP/2.

Conclusion

Avec requests, interroger le web tient en quelques lignes : get et post pour l’essentiel, params et json pour les données, headers et auth pour l’authentification. N’oubliez jamais les deux réflexes de robustesse : un timeout sur chaque requête et un raise_for_status() pour détecter les erreurs. Pour des besoins d’asynchrone ou de performance, httpx prend le relais avec une API familière.

À lire ensuite