Open Data & Python

L’objectif de ce tutoriel est de voir comment, depuis Python, on peut accéder à des données publiées sur le net en Open Data. Un premier exemple très rudimentaire avait déjà été présenté dans mon support de cours du module R207 - Sources de données (accés à la liste des départements français).

Nous allons cette fois récupérer des données météorologiques. Il existe de nombreux sites météo proposant des API pour accéder à leurs données, soit gratuitement, soit sur abonnement. Nous allons utiliser le site du Norwegian Meteorological Institute qui propose plusieurs services de téléchargement gratuits (ici).


Le service météo

Parmi les services proposés nous utiliserons le MET Norway Weather API v.3, et plus précisément le service Locationforecast. Il propose différents jeux de données que l’on peut récupérer soit au format XML soit au format JSON. Nous choisirons le jeu de données /compact au format JSON.

Leur site propose même de “tester” l’accés en indiquant l’emplacement dont on veut les prévisions météo (latitude, longitude). Pour les besoins de ce tutoriel nous nous positionnons à l’IUT des Pays de l’Adour à Mont de Marsan. Grâce à Google Maps (par exemple), nous trouvons que les coordonnées de l’IUT sont (lat=43.88566272770907, lon=-0.5092243304975015).

L’URL d’accès aux prévisions météo s’écrit donc ainsi: https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=43.88566272770907&lon=-0.5092243304975015

Nous pouvons d’ailleurs tester imméditement cette URL dans notre navigateur web préféré. Ce sont bien des données JSON et cela nous permet également de voir la structure du jeu de données.

api.met.no_iut_mdm_01
Données JSON (raw)

api.met.no_iut_mdm_02
Données JSON (pretty print)


Récupération des données JSON depuis Python

Idéalement, le programme Python suivant devrait être suffisant pour récupérer les données voulues au format JSON.

import json, requests

url = requests.get("https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=43.88566272770907&lon=-0.5092243304975015")
text = url.text

# Get JSON data
data = json.loads(text)
print(data)

Malheureusement, l’éxecution nous donne le message d’erreur suivant:

403 Forbidden
User-Agent header python-requests/2.22.0 is not allowed, use a unique identifier

Une solution consiste donc à modifier le User-Agent dans l’entête de notre requête HTTP. Il ne faut bien évidemment pas abuser de ce genre de tour de passe-passe ! Dans le cadre de ce tutoriel nous allons faire croire que la requête a été émise par notre navigateur Firefox sous Ubuntu via la valeur Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0.

Notre code Python devient le suivant, et ça fonctionne:

import json, requests

headers = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0'}
url = requests.get("https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=43.88566272770907&lon=-0.5092243304975015", headers=headers)
text = url.text

# Get JSON data
data = json.loads(text)
print(data)

Traitement des données JSON

Dans le programme précédent, notre variable data contient finalement le jeu de données structurées issu des données JSON reçues. Étant donné qu’en testant l’URL dans notre navigateur web nous avons vu quelle était la structure des données, nous pouvons maintenant en extraire les informations qui nous intéresse.

Par exemple, pour accéder à la température de la 1ère prévision de ce jeu de données, nous parcourons la structure de données ainsi: data["properties"]["timeseries"][0]["data"]["instant"]["details"]["air_temperature"]. Ce qui nous donne le code Python suivant:

# Process JSON data
print("temperature = %2.2f" %(data["properties"]["timeseries"][0]["data"]["instant"]["details"]["air_temperature"]))

NB: À noter que les valeurs sont stockées en “brut”; les unités sont précisées ailleurs dans la structure de donnés (data["properties"]["meta"]["units"]).


Problème de cache

En testant mon programme à plusieurs reprises je me suis rendu compte d’un problème: les données ne changeaient pas ! Or, en testant l’URL directement dans le navigateur web les données étaient bien mises à jour.

Bien que mon programme soit redémarré à chaque fois, il semblerait que Python implémente un système de cache directement au niveau de l’environnement Python sur l’ordinateur, indépendamment des programmes. Après quelques recherche sur le net il apparaît qu’il est nécessaire de préciser quelques paramètres supplémentaires dans l’entête de notre requête HTTP pour bypasser le cache et forcer l’envoi de la requête au serveur. Ce qui nous donne finalement le code Python suivant:

headers = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0',
           'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0'}
url = requests.get("https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=43.88566272770907&lon=-0.5092243304975015", headers=headers)
text = url.text

Exemple complet

Voici le code complet mon script Python pour tester la récupération de données météorologiques au format JSON sur le site MET Norway Weather API v.3:

# Python program to import JSON open data to Python

import json, requests

# Dans cet exemple nous allons utiliser le service météo du Meteorologisk institutt de Norvège
# (api.met.no/weatherapi is an interface to a selection of data produced by MET Norway)
# url: https://api.met.no/weatherapi/locationforecast/2.0/documentation
#
# Grâce à Google Maps, nous trouvons que les coordonnées de l'IUT à Mont de Marsan sont
# latitude 43.88566272770907, longitude -0.5092243304975015
#
# Ce qui nous donne l'URL suivante pour accéder aux prévisions météo au format JSON:
# https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=43.88566272770907&lon=-0.5092243304975015

# Fetch data from URL
headers = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0',
           'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0'}
url = requests.get("https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=43.88566272770907&lon=-0.5092243304975015", headers=headers)
text = url.text

# Get JSON data
data = json.loads(text)
#print(data)

# Process JSON data
units = data["properties"]["meta"]["units"]
weather = data["properties"]["timeseries"][0]["data"]["instant"]["details"]
for key in weather.keys():
    print("%s = %2.2f %s" %(key, weather[key], units[key]))

Son exécution vous donnera quelque chose de ce style:

login@host:dir$ python3 opendataTest002.py
air_pressure_at_sea_level = 1020.50 hPa
air_temperature = 28.40 celsius
cloud_area_fraction = 100.00 %
relative_humidity = 27.90 %
wind_from_direction = 237.80 degrees
wind_speed = 2.00 m/s

Conclusion

Finalement, une fois que l’on a compris comment envoyer une requête HTTP depuis Python, récupérer le contenu texte de la réponse HTTP, et convertir les données JSON ainsi reçues en structures de données Python (tableaux, listes, dictionnaires, etc.), il n’y a plus rien de bien compliqué ☺

Prochaine étape: faire en sorte que notre script Python envoie les données qu’il a récupérées dans des messages MQTT…

Précédent
Suivant