11 — Météo via API
Niveau requis : 9 (HTTP, requests, JSON, gestion d'erreurs réseau) / Difficulté : 4/5 / Durée estimée : 3–4 h
Objectif
Un outil météo en ligne de commande : on lui donne un nom de ville, il affiche la météo actuelle et les prévisions des prochains jours, en interrogeant une vraie API publique — Open-Meteo, gratuite et sans clé d'inscription.
python weather.py Nice
Meteo a Nice (43.70, 7.27)
Actuellement : 27.4 degC, vent 11 km/h
sam. 05/07 : 24-29 degC, ensoleille
dim. 06/07 : 23-28 degC, peu nuageux
lun. 07/07 : 24-27 degC, averses
Premier programme qui dépend du monde extérieur : le réseau peut être lent, coupé, l'API peut répondre n'importe quoi, la ville peut ne pas exister. La moitié du travail de ce projet, c'est gérer ce qui échoue. C'est ça, la programmation réseau réelle.
Notions utilisées
requests.get(), paramètres d'URL (params=),response.json(),response.raise_for_status(),timeout=— niveau 9- Exceptions réseau :
requests.exceptions.RequestExceptionet ses sous-classes (ConnectionError,Timeout,HTTPError) — niveaux 5 et 9 - Navigation dans un JSON imbriqué (dicts dans listes dans dicts) — niveaux 3 et 9
sys.argvpour la ville en argument — niveau 5- Fonctions et séparation appel réseau / affichage — niveau 4
- Installation d'un paquet :
pip install requests(premier paquet externe du parcours) — niveau 9
Cahier des charges
- Fichier
weather.py, appelépython weather.py <ville>(les noms à espaces fonctionnent :python weather.py "Le Havre"). - Deux appels d'API Open-Meteo :
- Géocodage :
https://geocoding-api.open-meteo.com/v1/search(name=<ville>,count=1,language=fr) → latitude/longitude. - Prévisions :
https://api.open-meteo.com/v1/forecast(latitude,longitude,current=temperature_2m,wind_speed_10m,weather_code,daily=temperature_2m_min,temperature_2m_max,weather_code,timezone=auto).
- Géocodage :
- Affichage : météo actuelle (température, vent) + 3 jours de prévisions (min-max + description en français du
weather_code, via un dict de correspondance — les codes sont documentés sur le site d'Open-Meteo ; couvre au moins les codes courants, avec un libellé par défaut sinon). - Tout appel réseau a un
timeout(par ex. 10 s). Sans exception. - Cas gérés SANS traceback, avec message clair et code de sortie non nul : ville introuvable, pas de connexion, timeout, réponse HTTP en erreur, JSON sans les champs attendus.
- Structure :
geocode(city),fetch_forecast(lat, lon),format_report(...),main(). Les fonctions réseau retournent des données, l'affichage est à part.
Étapes de construction
- Explore l'API sans écrire de programme : dans le REPL,
import requestspuis appelle l'URL de géocodage avecparams={"name": "Nice", "count": 1}. Regarder.status_code, puisr.json(). Note le chemin vers lat/lon (data["results"][0]["latitude"]). Cette exploration AVANT de coder est la méthode normale face à toute API. - Écris
geocode(city)qui retourne(lat, lon, name)— sans gestion d'erreurs pour l'instant — et unmain()minimal qui l'appelle avecsys.argv[1]et affiche le résultat. Programme qui tourne. - Même démarche pour les prévisions : explore la réponse de
/v1/forecastdans le REPL, puis écrisfetch_forecast(lat, lon)et affiche brut la partiecurrent. Tu as une météo qui marche (par beau temps réseau). - Écris l'affichage propre :
format_report(), dictWEATHER_CODESpour les descriptions, boucle sur les listes parallèles dedaily(dates, min, max, codes — elles vont ensemble par indice :zip()est ton ami). - Blindage 1 — l'utilisateur : ville manquante en argument, ville introuvable (
resultsabsent ou vide dans la réponse de géocodage). Messages clairs,sys.exit(1). Teste avecXyzzyville. - Blindage 2 — le réseau :
timeout=10sur chaque appel,raise_for_status(), et untry/except requests.exceptions.RequestExceptionau niveau demain(). Teste réellement : coupe le Wi-Fi et lance ; metstimeout=0.001temporairement pour provoquer un vraiTimeout. Un except jamais déclenché sous tes yeux est un except non testé. - Finition : en-tête avec nom de ville et coordonnées, arrondis, alignement des colonnes des prévisions. Déroule le scénario complet sur 3 villes dont une avec espaces et une inexistante.
Extensions possibles
- Option
--days 7pour choisir le nombre de jours (paramètreforecast_daysde l'API). - Cache local : sauvegarder la dernière réponse par ville dans un JSON avec horodatage, et l'utiliser si elle a moins de 15 minutes (économise l'API, et affiche quelque chose hors ligne — en le signalant honnêtement).
- Comparer deux villes côte à côte (
python weather.py Nice Paris). - Emoji météo par code (attention à l'encodage du terminal Windows — c'est une extension, pas une exigence).
Erreurs fréquentes sur ce projet
- Pas de
timeout: sans lui, un réseau qui « rame » gèle ton programme pour toujours, sans erreur. C'est le bug le plus sournois de ce projet parce qu'il est invisible tant que tout va bien. KeyError: 'results'sur une ville inconnue : Open-Meteo ne renvoie pas une erreur HTTP mais un JSON SANS le champresults. Testerif "results" not in data or not data["results"]— ne jamais faire confiance à la forme d'une réponse.- Confondre l'échec réseau (exception à attraper) et l'échec HTTP (status 4xx/5xx, PAS d'exception sans
raise_for_status()). Les deux existent, les deux se testent. - Oublier que
dailyrenvoie des listes parallèles (une liste de dates, une liste de min, une liste de max) et non une liste de dicts par jour. L'indexation croisée à la main se rate ;zip()la rend lisible. except:nu qui avale tout, y compris tes propres bugs (NameErrorinclus !) : n'attrape QUErequests.exceptions.RequestExceptionet les cas que tu as identifiés. Un except large est un mensonge de robustesse.- Tester uniquement le chemin heureux : le cahier des charges impose de PROVOQUER chaque panne. Si tu n'as pas vu le message « pas de connexion » de tes yeux, il n'existe pas.
Critères de réussite
-
python weather.py Niceaffiche météo actuelle + 3 jours, descriptions en français. - Ville inexistante : message clair, exit code non nul, pas de traceback.
- Wi-Fi coupé : message clair, pas de traceback (testé réellement).
- Timeout provoqué (timeout ridicule) : message clair (puis timeout remis à 10 s).
- Chaque
requests.geta untimeout. - Les fonctions réseau ne font aucun
print; l'affichage est séparé.
Commit conseillé
git add projects/mini-projects/11-meteo-api/
git commit -m "project: add weather CLI using Open-Meteo API"
Puis entrée de journal + git push.