Cours Python · Module 4: Données, API et bases de données
HTTP - le protocole d'échange de données
Dans cette leçon14
Content de te revoir ! Ici Darwin.
Dans la leçon précédente, tu as découvert les formats de données - comment stocker l'information (JSON, CSV, XML, YAML). Il est maintenant temps de parler de HTTP - le protocole qui te permet de transmettre ces données sur Internet !
Analogie safari : Les formats de données sont la langue dans laquelle tu consignes tes observations. HTTP est le système de messagerie - la façon d'envoyer ces observations entre les stations de recherche des différentes régions d'Afrique !
Qu'est-ce que HTTP ?
HTTP (HyperText Transfer Protocol) est un protocole de communication - un ensemble de règles selon lesquelles les ordinateurs échangent des données sur Internet.
Comment ça marche :
- Le client (par ex. un navigateur, une appli Python) envoie une requête
- Le serveur (par ex. un site web, une API) traite la requête
- Le serveur renvoie une réponse avec des données ou une information d'erreur
1[CLIENT] ---(REQUÊTE HTTP)---> [SERVEUR]
2 | |
3 | Traitement
4 | |
5 | <---(RÉPONSE HTTP)--- [SERVEUR]Exemple safari : Tu envoies une requête à la base de données centrale de Nairobi : « Donne-moi les données sur les lions du Serengeti ». Le serveur répond : « Voici 120 lions au format JSON » !
Les méthodes HTTP - les types de requêtes
HTTP dispose de différentes méthodes (parfois appelées « verbes ») - elles précisent CE que tu veux faire :
| Méthode | Objectif | Analogie safari |
|---|---|---|
| GET | Récupérer des données | « Montre-moi la liste des espèces » |
| POST | Créer une nouvelle ressource | « Ajoute une nouvelle observation d'éléphant » |
| PUT | Remplacer une ressource entière | « Remplace tout le profil du lion par de nouvelles données » |
| PATCH | Mettre à jour une partie d'une ressource | « Change uniquement la population des gorilles » |
| DELETE | Supprimer une ressource | « Supprime une observation erronée » |
Les plus importantes : GET et POST - utilisées dans 90 % des cas !
GET - récupérer des données
1GET /api/species/lion HTTP/1.1
2Host: safari-api.comCaractéristiques :
- Ne modifie pas les données sur le serveur
- Paramètres dans l'URL (
?name=lion&habitat=savanna) - Peut être mise en favori
- Peut être appelée plusieurs fois sans danger
POST - créer / envoyer des données
1POST /api/observations HTTP/1.1
2Host: safari-api.com
3Content-Type: application/json
4
5{
6 "species": "Panthera leo",
7 "count": 12,
8 "location": "Serengeti"
9}Caractéristiques :
- Envoie les données dans le corps de la requête, pas dans l'URL
- Peut modifier les données sur le serveur
- N'est pas enregistrée dans l'historique du navigateur
- Peut créer de nouvelles ressources
Les codes de statut HTTP - les réponses du serveur
Le serveur répond toujours avec un code de statut - un nombre indiquant si l'opération a réussi :
2xx - Succès
- 200 OK - requête réussie, voici les données
- 201 Created - la ressource a été créée
- 204 No Content - succès, mais aucune donnée à renvoyer
3xx - Redirection
- 301 Moved Permanently - ressource déplacée définitivement
- 302 Found - redirection temporaire
4xx - Erreur client
- 400 Bad Request - requête mal formée (par ex. mauvais JSON)
- 401 Unauthorized - authentification requise
- 403 Forbidden - pas d'accès
- 404 Not Found - la ressource n'existe pas
- 429 Too Many Requests - trop de requêtes
5xx - Erreur serveur
- 500 Internal Server Error - erreur du serveur
- 502 Bad Gateway - problème du serveur proxy
- 503 Service Unavailable - serveur indisponible
Analogie safari : Le code 200 = « J'ai trouvé le lion ! », 404 = « Il n'y a pas de lion ici », 500 = « Ma radio est tombée en panne »
Python requests - la bibliothèque HTTP
requests est la bibliothèque Python la plus populaire pour HTTP - simple, élégante, puissante !
Installation
1pip install requestsUtilisation de base - requête GET
1import requests
2
3# Récupérer des données
4response = requests.get("https://api.github.com/users/octocat")
5
6# Code de statut
7print(response.status_code) # 200
8
9# Données au format JSON
10data = response.json()
11print(data["login"]) # "octocat"
12print(data["name"]) # "The Octocat"
13
14# Données sous forme de texte
15print(response.text) # JSON brut sous forme de chaîne
16
17# En-têtes de la réponse
18print(response.headers["Content-Type"]) # "application/json"Vérifier le succès
1import requests
2
3response = requests.get("https://api.github.com/users/nonexistent")
4
5# Méthode 1 : vérifier le code de statut
6if response.status_code == 200:
7 print("Succès !")
8elif response.status_code == 404:
9 print("Introuvable")
10
11# Méthode 2 : utiliser response.ok (True pour 2xx)
12if response.ok:
13 data = response.json()
14 print(data)
15else:
16 print(f"Erreur : {response.status_code}")
17
18# Méthode 3 : lever une exception en cas d'erreur
19try:
20 response.raise_for_status() # Lève HTTPError pour 4xx/5xx
21 data = response.json()
22except requests.HTTPError as e:
23 print(f"Erreur HTTP : {e}")Les paramètres d'URL - les query parameters
Les requêtes GET utilisent souvent des paramètres dans l'URL :
1import requests
2
3# Manuellement dans l'URL
4response = requests.get("https://api.example.com/species?habitat=savanna&endangered=true")
5
6# Mieux : dictionnaire params (encodage automatique)
7params = {
8 "habitat": "savanna",
9 "endangered": "true",
10 "min_population": 100
11}
12
13response = requests.get("https://api.example.com/species", params=params)
14# URL : https://api.example.com/species?habitat=savanna&endangered=true&min_population=100
15
16print(response.url) # Voir l'URL généréeLes en-têtes
Les en-têtes sont les métadonnées de la requête - des informations sur le client, le format attendu, l'autorisation :
1import requests
2
3# En-têtes personnalisés
4headers = {
5 "User-Agent": "Safari-Data-Collector/1.0",
6 "Accept": "application/json",
7 "Authorization": "Bearer YOUR_API_KEY_HERE"
8}
9
10response = requests.get("https://api.example.com/species", headers=headers)
11
12# Vérifier les en-têtes de la réponse
13print(response.headers["Content-Type"])
14print(response.headers["Date"])En-têtes courants :
User-Agent- identification du clientAccept- format de réponse attenduContent-Type- format des données envoyéesAuthorization- token / clé d'API
Requête POST - envoyer des données
Charge utile JSON
1import requests
2
3# Données à envoyer
4new_observation = {
5 "species": "Panthera leo",
6 "location": "Serengeti",
7 "count": 12,
8 "date": "2024-01-15",
9 "observer": "Darwin"
10}
11
12# POST avec JSON
13response = requests.post(
14 "https://api.safari.com/observations",
15 json=new_observation # Automatiquement : Content-Type: application/json
16)
17
18if response.status_code == 201: # Created
19 created_data = response.json()
20 print(f"ID de l'observation créée : {created_data['id']}")
21else:
22 print(f"Erreur : {response.status_code}")Données de formulaire (application/x-www-form-urlencoded)
1import requests
2
3# Données de formulaire
4form_data = {
5 "species": "Loxodonta africana",
6 "count": "35"
7}
8
9response = requests.post(
10 "https://api.safari.com/quick-report",
11 data=form_data # Content-Type: application/x-www-form-urlencoded
12)PUT, PATCH, DELETE
1import requests
2
3# PUT - remplacer la ressource entière
4updated_species = {
5 "scientific_name": "Panthera leo",
6 "population": 125, # Mis à jour
7 "habitat": "savanna"
8}
9
10response = requests.put(
11 "https://api.safari.com/species/lion",
12 json=updated_species
13)
14
15# PATCH - changer seulement une partie
16partial_update = {
17 "population": 125 # Seulement la population
18}
19
20response = requests.patch(
21 "https://api.safari.com/species/lion",
22 json=partial_update
23)
24
25# DELETE - supprimer la ressource
26response = requests.delete("https://api.safari.com/observations/12345")
27
28if response.status_code == 204: # No Content
29 print("Supprimé avec succès")Gestion des erreurs
1import requests
2from requests.exceptions import RequestException, Timeout, ConnectionError
3
4def fetch_species_data(species_id: str):
5 """Récupère les données d'une espèce avec gestion des erreurs"""
6 try:
7 response = requests.get(
8 f"https://api.safari.com/species/{species_id}",
9 timeout=5 # Délai d'attente de 5 secondes
10 )
11
12 # Vérifier le statut
13 response.raise_for_status()
14
15 # Traiter les données
16 return response.json()
17
18 except Timeout:
19 print("Délai dépassé - le serveur n'a pas répondu à temps")
20 return None
21
22 except ConnectionError:
23 print("Erreur de connexion - vérifie ta connexion Internet")
24 return None
25
26 except requests.HTTPError as e:
27 print(f"Erreur HTTP {response.status_code} : {e}")
28 return None
29
30 except RequestException as e:
31 print(f"Erreur de requête : {e}")
32 return None
33
34# Utilisation
35data = fetch_species_data("lion")
36if data:
37 print(f"Espèce : {data['name']}")Exemple safari - client d'API pour la gestion des observations
1import requests
2from typing import List, Dict, Optional
3from datetime import datetime
4
5class SafariAPIClient:
6 """Client d'API pour le système d'observations Safari"""
7
8 def __init__(self, base_url: str, api_key: str):
9 self.base_url = base_url.rstrip("/")
10 self.api_key = api_key
11 self.session = requests.Session() # Connexion réutilisable
12
13 # En-têtes par défaut pour toutes les requêtes
14 self.session.headers.update({
15 "User-Agent": "Safari-Data-Collector/2.0",
16 "Authorization": f"Bearer {self.api_key}",
17 "Accept": "application/json"
18 })
19
20 def get_species(self, species_id: str) -> Optional[Dict]:
21 """Récupérer les données d'une espèce"""
22 try:
23 response = self.session.get(
24 f"{self.base_url}/species/{species_id}",
25 timeout=10
26 )
27 response.raise_for_status()
28 return response.json()
29
30 except requests.RequestException as e:
31 print(f"Erreur lors de la récupération de l'espèce : {e}")
32 return None
33
34 def search_species(self, habitat: Optional[str] = None,
35 endangered: Optional[bool] = None,
36 min_population: Optional[int] = None) -> List[Dict]:
37 """Rechercher des espèces avec des filtres"""
38 params = {}
39 if habitat:
40 params["habitat"] = habitat
41 if endangered is not None:
42 params["endangered"] = str(endangered).lower()
43 if min_population:
44 params["min_population"] = min_population
45
46 try:
47 response = self.session.get(
48 f"{self.base_url}/species",
49 params=params,
50 timeout=10
51 )
52 response.raise_for_status()
53 return response.json()
54
55 except requests.RequestException as e:
56 print(f"Erreur de recherche : {e}")
57 return []
58
59 def create_observation(self, species: str, location: str,
60 count: int, notes: str = "") -> Optional[Dict]:
61 """Créer une nouvelle observation"""
62 observation_data = {
63 "species": species,
64 "location": location,
65 "count": count,
66 "date": datetime.now().isoformat(),
67 "notes": notes
68 }
69
70 try:
71 response = self.session.post(
72 f"{self.base_url}/observations",
73 json=observation_data,
74 timeout=10
75 )
76 response.raise_for_status()
77
78 if response.status_code == 201:
79 created = response.json()
80 print(f"ID de l'observation créée : {created.get('id')}")
81 return created
82 else:
83 return response.json()
84
85 except requests.RequestException as e:
86 print(f"Erreur lors de la création de l'observation : {e}")
87 return None
88
89 def update_species_population(self, species_id: str,
90 new_population: int) -> bool:
91 """Mettre à jour la population d'une espèce (PATCH)"""
92 try:
93 response = self.session.patch(
94 f"{self.base_url}/species/{species_id}",
95 json={"population": new_population},
96 timeout=10
97 )
98 response.raise_for_status()
99 print(f"Population mise à jour {species_id} -> {new_population}")
100 return True
101
102 except requests.RequestException as e:
103 print(f"Erreur de mise à jour : {e}")
104 return False
105
106 def delete_observation(self, observation_id: str) -> bool:
107 """Supprimer une observation"""
108 try:
109 response = self.session.delete(
110 f"{self.base_url}/observations/{observation_id}",
111 timeout=10
112 )
113 response.raise_for_status()
114
115 if response.status_code == 204:
116 print(f"Observation supprimée {observation_id}")
117 return True
118
119 return False
120
121 except requests.RequestException as e:
122 print(f"Erreur de suppression : {e}")
123 return False
124
125 def get_statistics(self) -> Optional[Dict]:
126 """Récupérer les statistiques (avec des paramètres de requête)"""
127 params = {
128 "include_endangered": "true",
129 "group_by": "habitat"
130 }
131
132 try:
133 response = self.session.get(
134 f"{self.base_url}/statistics",
135 params=params,
136 timeout=15
137 )
138 response.raise_for_status()
139 return response.json()
140
141 except requests.RequestException as e:
142 print(f"Erreur lors de la récupération des statistiques : {e}")
143 return None
144
145 def close(self):
146 """Fermer la session"""
147 self.session.close()
148
149
150# === DÉMONSTRATION ===
151
152print("=== CLIENT D'API SAFARI ===\n")
153
154# Initialiser le client
155client = SafariAPIClient(
156 base_url="https://api.safari-data.org/v1",
157 api_key="demo_key_12345"
158)
159
160# GET - récupérer une espèce
161print("1. Récupération des données du lion...")
162lion_data = client.get_species("lion")
163if lion_data:
164 print(f" Espèce : {lion_data.get('common_name')}")
165 print(f" Population : {lion_data.get('population')}")
166
167# GET avec paramètres - recherche
168print("\n2. Recherche des espèces menacées dans la savane...")
169endangered = client.search_species(habitat="savanna", endangered=True)
170print(f" {len(endangered)} espèces trouvées")
171
172# POST - créer une observation
173print("\n3. Création d'une nouvelle observation...")
174new_obs = client.create_observation(
175 species="Panthera leo",
176 location="Serengeti North",
177 count=8,
178 notes="Groupe avec 2 lionceaux"
179)
180
181# PATCH - mise à jour
182print("\n4. Mise à jour de la population...")
183client.update_species_population("lion", 125)
184
185# DELETE - suppression
186print("\n5. Suppression d'une observation erronée...")
187client.delete_observation("obs_12345")
188
189# GET avec statistiques
190print("\n6. Récupération des statistiques...")
191stats = client.get_statistics()
192if stats:
193 print(f" Nombre total d'espèces : {stats.get('total_species')}")
194
195# Fermer la session
196client.close()
197print("\nSession fermée")Session - efficacité pour de multiples requêtes
Si tu effectues de nombreuses requêtes vers la même API, utilise Session - elle réutilise la connexion TCP :
1import requests
2
3# SANS Session - chaque requête = nouvelle connexion
4for i in range(100):
5 response = requests.get("https://api.example.com/data")
6
7# AVEC Session - une seule connexion, plus rapide !
8session = requests.Session()
9session.headers.update({"Authorization": "Bearer token123"})
10
11for i in range(100):
12 response = session.get("https://api.example.com/data")
13
14session.close()Timeouts - ne pas attendre indéfiniment
Utilise toujours un timeout pour qu'une requête ne reste pas bloquée indéfiniment :
1import requests
2
3# Timeout de 5 secondes
4response = requests.get("https://slow-api.com/data", timeout=5)
5
6# Timeouts distincts : (connect_timeout, read_timeout)
7response = requests.get("https://api.com/data", timeout=(3, 10))
8# 3s pour se connecter, 10s pour lire les donnéesRésumé
Dans cette leçon, tu as appris :
- Ce qu'est HTTP et comment ça fonctionne (client -> serveur -> réponse)
- Les méthodes HTTP : GET, POST, PUT, PATCH, DELETE
- Les codes de statut : 200 OK, 404 Not Found, 500 Server Error
- La bibliothèque
requests:requests.get(),requests.post() - Les paramètres d'URL (
params=), les en-têtes (headers=) - L'envoi de JSON (
json=) et de données de formulaire (data=) - La gestion des erreurs :
response.raise_for_status(), try/except - Session pour l'efficacité
- Les timeouts pour la sécurité
Point de contrôle
Avant de continuer :
- Tu comprends la différence entre GET et POST
- Tu sais envoyer une requête GET avec des paramètres
- Tu sais envoyer un POST avec du JSON
- Tu sais gérer les erreurs HTTP
- Tu comprends les codes de statut (2xx, 4xx, 5xx)
Analogie safari : HTTP est un système de messagerie entre stations de recherche. GET = « envoie-moi des données », POST = « enregistre ces nouvelles observations », les codes de statut = les accusés de réception !
Dans la prochaine leçon, Darwin te montrera les API REST - les standards d'architecture d'API qui te permettent de construire des systèmes professionnels d'échange de données !
Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Quelle méthode HTTP sert à récupérer des données ?
2. Comment effectue-t-on une requête GET avec la bibliothèque requests ?
Voici 2 des 4 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Tri horizontal
Remets les éléments dans le bon ordre :
- Clique dans l'ordre
Clique les éléments dans le bon ordre :
- Éditeur de code
Récupère les données depuis l'URL 'https://api.example.com/animals' avec requests
- Éditeur de code
Effectue une requête GET et analyse la réponse en JSON
- Clique dans l'ordre
Clique dans l'ordre : response = requests.get(url)