Définition
Un script qui doit parler à un service web doit d'abord parler HTTP : ouvrir une connexion, construire une requête, poser les bons en-têtes, attendre une réponse, puis décoder ce qui revient. Chaque étape est un endroit où une erreur peut se glisser, surtout quand le résultat n'apparaît qu'après une quinzaine de lignes. requests existe pour effacer cette mécanique et vous rendre directement ce qui vous intéresse : la réponse du serveur.
Elle ne fait pas partie de la bibliothèque standard, il faut donc l'installer avec pip, qui la télécharge depuis PyPI.
python -m pip install requestsUne fois installée, un appel complet à un service web tient en trois lignes.
import requests
reponse = requests.get("https://api.exemple.com/articles", timeout=10)
print(reponse.status_code)
print(reponse.json())Derrière ces trois lignes, plusieurs décisions ont déjà été prises à votre place : l'encodage des paramètres dans l'adresse, le suivi des redirections, la conservation des cookies, la décompression du corps et le décodage du texte selon l'en-tête annoncé par le serveur. C'est ce travail invisible qui a fait de cette bibliothèque le moyen par défaut d'appeler une interface web.
Ce qu'elle remplace
Python sait déjà parler HTTP tout seul, avec le module urllib.request de la bibliothèque standard. Presque personne ne s'en sert pour du travail courant, alors autant regarder pourquoi dans le tableau qui suit.
| Besoin | Avec urllib.request | Avec requests |
|---|---|---|
| Ajouter des filtres à l'adresse | Les encoder à la main | Argument params |
| Envoyer du JSON | Sérialiser, encoder en octets, poser l'en-tête | Argument json |
| Lire le corps en texte | Lire des octets, puis les décoder | Attribut text |
| Conserver des cookies | Construire un opener et son gestionnaire | Objet Session |
| S'authentifier | Assembler l'en-tête soi-même | Argument auth |
Rien, dans la colonne du milieu, n'est réellement impossible : c'est juste une quinzaine de lignes là où une seule suffit, avec autant d'occasions de se tromper sur un encodage oublié. La documentation officielle de Python renvoie d'ailleurs vers cette bibliothèque pour les usages courants, ce qui reste rare pour un projet qui ne fait pas partie du langage.
Les deux pièges qui coûtent une soirée
Le premier surprend presque tout le monde une fois : une réponse 404 ou 500 ne lève aucune exception. On imagine souvent qu'un code d'erreur interrompt le programme, comme une division par zéro. Ce n'est pas le cas : pour la bibliothèque, le serveur a répondu, donc l'appel a réussi. Le script continue, et traite une page d'erreur comme une donnée valide, parfois jusqu'à un plantage sans rapport apparent, trois fonctions plus loin. La parade tient en une ligne, à poser juste après l'appel.
reponse = requests.get(url, timeout=10)
reponse.raise_for_status() # lève sur 4xx et 5xx
donnees = reponse.json()Sans l'argument timeout, il n'existe aucune limite d'attente. Un serveur qui accepte la connexion puis reste muet bloque le programme indéfiniment, sans message ni erreur, jusqu'à ce qu'on l'arrête à la main. Mettez un timeout sur chaque appel, y compris ceux qui visent votre propre serveur.
params, data, json : le trio qu'on intervertit
Trois arguments servent à envoyer quelque chose au serveur, et ils ne se valent pas : les confondre produit une requête que le serveur refuse, sans toujours dire pourquoi.
| Argument | Où part le contenu | Quand l'utiliser |
|---|---|---|
| params | Dans l'adresse, après le point d'interrogation | Filtres et pagination d'une lecture |
| data | Dans le corps, au format formulaire | Formulaire web classique |
| json | Dans le corps, sérialisé, en-tête posé | API moderne, le cas le plus fréquent |
Le symptôme se reconnaît vite : une réponse 400 ou 422 sur un appel qui semblait pourtant correct. Neuf fois sur dix, la cause est là, un dictionnaire parti dans data alors que le service attendait du JSON.
Quand elle ne sert à rien
Elle ne remplace pas un navigateur. Elle récupère exactement le document que le serveur renvoie, rien de plus : si la page construit une partie de son contenu en JavaScript, ce contenu n'existera pas dans la réponse. Chercher un texte bien visible à l'écran et introuvable dans ce que la bibliothèque a reçu est le rite de passage de tout premier projet d'extraction de données.
Elle bloque, ensuite. Chaque appel attend sa réponse avant de rendre la main, ce qui convient bien à un script qui interroge dix adresses et devient un goulot d'étranglement dès qu'il faut en interroger mille. Le travail en parallèle relève d'asyncio et d'un client conçu pour lui, comme httpx.
Enfin, c'est un client, pas un serveur. Elle appelle des interfaces web, elle n'en publie aucune : ce rôle-là revient à Flask, Django ou FastAPI.
Questions fréquentes
Faut-il utiliser une Session plutôt que des appels isolés ?
Dès qu'on interroge plusieurs fois le même hôte, oui. Une Session réutilise la connexion réseau au lieu de la rouvrir à chaque appel, et conserve les cookies et les en-têtes communs, ce qui se mesure sur une boucle de cent requêtes. Elle s'emploie comme un gestionnaire de contexte, dans un bloc with, afin qu'elle se referme proprement même si un appel échoue.
Pourquoi l'import échoue-t-il alors que l'installation a réussi ?
Parce que l'installation et l'exécution ne passent pas forcément par le même interpréteur. La commande a déposé la bibliothèque quelque part, le script la cherche ailleurs, et le résultat est une ModuleNotFoundError sur une bibliothèque pourtant bien présente sur la machine. Lancer python -m pip install après avoir activé l'environnement virtuel du projet écarte ce problème une fois pour toutes.
Faut-il lui préférer httpx aujourd'hui ?
Pas par principe. httpx reprend presque la même écriture tout en ajoutant le mode asynchrone et HTTP/2, ce qui compte pour un service qui lance des centaines d'appels simultanés. Sur un script, une tâche planifiée ou un projet qui appelle trois interfaces l'une après l'autre, la différence ne se mesure pas, et la bibliothèque la plus documentée des deux garde un avantage réel le jour où quelque chose casse.