Interroger un serveur via une API
La démarche est la suivante :
- envoyer une requête HTTP à un serveur Web depuis la console Python (Client) ;
- à partir des données fournies dans la requête HTTP, une API du serveur Web va générer une requête SQL pour interroger une base de données ;
- la réponse SQL parvient à l’API qui formate les données de cette réponse ;
- ces données formatées sont transmises dans une réponse HTTP à la console Python (Client)
Utilisation du module « Requests » de Python
La fonction ‘request’ du module Requests permet d’envoyer des requêtes HTTP.
>>> from requests import request as requete
Les paramètres d’une requête
>>> help(requete)
Help on function request in module requests.api:
request(method, url, **kwargs)
Constructs and sends a :class:`Request <Request>`.
…/…
Usage::
>>> import requests
>>> req = requests.request('GET', 'https://httpbin.org/get')
<Response [200]>
Les deux paramètres requis pour l’envoi d’une requête sont :
- une méthode du protocole HTTP. Nous utiliserons la méthode ‘GET’ ;
- l’url de l’API
Envoi d’une requête à l’API de « Datas Angers » pour le ‘dataset’ intitulé ‘Prénoms des enfants nés à Angers’
Contexte
Découvrir le site : https://data.angers.fr/
URL de l’API permettant de faire de l’extraction d’enregistrements
https://data.angers.fr/api/v2/catalog/datasets/prenoms-des-enfants-nes-a-angers/records?
Envoi d’une première requête
>>> from requests import request as requete
>>> methode = 'GET'
>>> api_url = "https://data.angers.fr/api/v2/catalog/datasets/prenoms-des-enfants-nes-a-angers/records"
>>> reponse = requete(methode, api_url)
>>> type(reponse)
<class 'requests.models.Response'>
>>> repjson = reponse.json()
>>> type(repjson)
<class 'dict'>
>>> len(repjson)
3
>>> repjson.keys()
dict_keys(['total_count', 'links', 'records'])
>>> repjson['total_count']
2850
Nous apprenons ainsi qu’il y a 2850 enregistrements disponibles dans le ‘dataset’.
Examinons le contenu de la réponse plus en détails.
>>> type(repjson['records'])
<class 'list'>
>>> len(repjson['records'])
10
>>> type(repjson['records'][0])
<class 'dict'>
>>> repjson['records'][0].keys()
dict_keys(['links', 'record'])
>>> type(repjson['records'][0]['record'])
<class 'dict'>
>>> repjson['records'][0]['record'].keys()
dict_keys(['id', 'timestamp', 'size', 'fields'])
>>> type(repjson['records'][0]['record']['fields'])
<class 'dict'>
>>> repjson['records'][0]['record']['fields'].keys()
dict_keys(['commune_nom', 'coll_insee', 'enfant_sexe', 'enfant_prenom', 'nombre_occurrences', 'annee'])
>>> repjson['records'][0]['record']['fields']
{'commune_nom': 'ANGERS', 'coll_insee': '49007', 'enfant_sexe': 'F', 'enfant_prenom': 'Alice', 'nombre_occurrences': 22, 'annee': 2013}
Cet examen nous apprend que :
- seulement 10 enregistrements ont été récupérés.
- les données ‘exploitables’ ne constituent qu’une partie des informations recueillies
Poursuivons nos investigations :
>>> type(repjson['links'])
<class 'list'>
>>> len(repjson['links'])
4
>>> type(repjson['links'][0])
<class 'dict'>
>>> repjson['links'][0].keys()
dict_keys(['rel', 'href'])
>>> repjson['links'][0]
{'rel': 'self', 'href': 'https://data.angers.fr/api/v2/catalog/datasets/prenoms-des-enfants-nes-a-angers/records?limit=10&offset=0'}
>>> repjson['links'][1]
{'rel': 'first', 'href': 'https://data.angers.fr/api/v2/catalog/datasets/prenoms-des-enfants-nes-a-angers/records?limit=10&offset=0'}
>>> repjson['links'][2]
{'rel': 'last', 'href': 'https://data.angers.fr/api/v2/catalog/datasets/prenoms-des-enfants-nes-a-angers/records?limit=10&offset=2840'}
>>> repjson['links'][3]
{'rel': 'next', 'href': 'https://data.angers.fr/api/v2/catalog/datasets/prenoms-des-enfants-nes-a-angers/records?limit=10&offset=10'}
On s’aperçoit que :
- seuls les dix premiers enregistrements ont été transmis
- repjson[‘links’][0] >>> ‘rel’: ‘self‘, …/… limit=10&offset=0
- repjson[‘links’][1] >>> ‘rel’: ‘first‘, …/… limit=10&offset=0
- des couples ‘parametre=valeur’ ont été ajoutés – par defaut – à l’URL de l’API, constituant ainsi une requête ‘HTTP’ basée sur la méthode ‘GET’.
Une API qui utilise ‘Opendatasoft Query Language (ODSQL)’
Une documentation est accessible ici : https://help.opendatasoft.com/
Il est possible d’écrire une requête avec les clauses ‘select’, ‘where’, ‘group_by’, ‘order_by’ et ‘limit’ similaires à celles du langage SQL.
Testons :
from requests import request as requete
def clause_formatage(clause):
clause.replace("=", "%3D")
clause.replace(" ", "%20")
clause.replace("'", "%27")
clause.replace('"', "%27")
clause.replace(',', "%2C")
clause.replace('/', "%2F")
return clause
methode = 'GET'
api_url = 'https://data.angers.fr/api/v2/catalog/datasets/prenoms-des-enfants-nes-a-angers/records'
clause_select = "enfant_prenom, nombre_occurrences"
clause_select = clause_formatage(clause_select)
clause_select = "select=" + clause_select
clause_where = "annee=2013 AND enfant_sexe='F'"
clause_where = clause_formatage(clause_where)
clause_where = "&where=" + clause_where
clause_orderby = "nombre_occurrences desc"
clause_orderby = clause_formatage(clause_orderby)
clause_orderby = "&order_by=" + clause_orderby
clause_limit = "&limit=5"
clause_offset ="&offset=0"
requeteGet = api_url+"?"+clause_select+clause_where+clause_orderby+clause_limit+clause_offset
requeteGet += clause_formatage("&lang=fr&timezone=Europe/Paris")
reponse = requete(methode, requeteGet )
repjson = reponse.json()
top = len(repjson['records'])
print("*" * 45)
titre = "TOP " + str(top) + " des prénoms"
espace = (45 - len(titre)) // 2
titre = " " * espace + titre
print(titre)
titre = "donnés aux filles nées à Angers en 2013"
espace = (45 - len(titre)) // 2
titre = " " * espace + titre
print(titre)
print("*" * 45)
print()
for compt in range(top):
prenom = repjson['records'][compt]['record']['fields']['enfant_prenom']
prenom = prenom + " " + "." * (20 - len(prenom))
score = " " + str(repjson['records'][compt]['record']['fields']['nombre_occurrences'])
print(prenom + score)
Résultat obtenu dans la console :
*********************************************
TOP 5 des prénoms
donnés aux filles nées à Angers en 2013
*********************************************
Jade ................ 43
Manon ............... 41
Louise .............. 41
Chloé ............... 40
Inès ................ 36
Extraction de données et sauvegarde de ces données dans un fichier au format ‘csv’
L’API permet d’extraire des données et de les récupérer sous la forme d’une chaîne de caractères directement sauvegardable au format ‘json’ ou ‘csv’. Testons avec le format ‘csv’.
Dans un éditeur Python saisir le programme qui suit et l’exécuter.
# *****************************************************************************
# TELECHARGEMENT ET SAUVEGARDE DE DONNEES AU FORMAT 'CSV'
# *****************************************************************************
from requests import request as requete
# requête
api_url = "https://data.angers.fr/api/v2/catalog/datasets/prenoms-des-enfants-nes-a-angers/"
api_action = "exports/"
api_format = "csv"
clause_select = "?select=enfant_prenom%2C%20nombre_occurrences"
clause_where = "&where=annee%3D2013%20and%20enfant_sexe%3D%27F%27"
clause_limit = "&limit=5&offset=0"
clause_supplem = "&lang=fr&timezone=Europe%2FParis"
req = clause_select + clause_where + clause_limit + clause_supplem
req = api_url + api_action + api_format + req
export = requete('GET', req)
export_txt = export.text
print(export_txt)
""" option :
export_txt = export_txt.replace(";", ",")
print(export_txt)
"""
export_file_name = "top-5_prenoms-F_annee-2013.csv"
fichier_csv = open(export_file_name, 'w', encoding='UTF-8', newline='')
fichier_csv.write(export_txt)
fichier_csv.close()
Idées de projets…
P1 - Écrire un programme en Python qui permet : - de saisir et d'affecter son propre prénom à une variable ; - d'effectuer une recherche du nombre d'occurrences de son prénom pour toutes les années disponibles dans le 'dataset' ; - d'afficher le résultat de cette recherche sous une forme structurée.
P2 - Écrire un programme en Python qui permet : - de saisir et d'affecter à une variable une chaine de un à trois caractères maximum correspondant aux lettres du début d'un prénom ; - d'effectuer une recherche de tous les prénoms commençant par la chaîne de caractères choisie pour toutes les années disponibles dans le 'dataset' ; - d'afficher le résultat de cette recherche sous une forme structurée.
P3 - Écrire un programme en Python qui permet : - d'effectuer une recherche du 'top 10' des prénoms des garçons et des filles pour toutes les années disponibles dans le 'dataset' ; - de calculer en pourcentage ce que représente le nombre d'occurrences de chacun de ces prénoms par rapport au nombre total de prénoms par année et par sexe ; - d'afficher le résultat de cette recherche sous une forme structurée.
P4 - Écrire un programme en Python qui permet : - d'effectuer une recherche du 'top 3' des prénoms les plus courts et les plus longs, par année et par sexe ; - d'afficher le résultat de cette recherche sous une forme structurée.