Skip to content

Utiliser une API REST

1. Introduction

Pour découvrir Python, nous allons d'abord apprendre à manipuler des données.

Pour cela nous nous appuierons sur un jeu de données accessible via une API REST.

Une APi (Application Programming Interface) est un programme qui permet à deux programmes de communiquer ensemble.

Ici nous voulons accéder aux données proposées par un site web.

L'API utilisée, appellée REST API communique avec le site web par des requêtes http.

Nous allons utiliser l'APi de Gitlab qui contient énormément d'informations sur les projets hébergés.


2. Import des librairies utiles

En plus des fonctions intégrées à python (comme la fonction print() que nous avons utilisée), nous allons importer des fonctions provenant d'autres librairies.

Il nous faut donc:
1. installer les librairies sur notre système
2. importer les fonctions utiles dans notre projet

2.1 Installer les librairies

Pour installer des librairies python supplémentaires, nous allons utiliser pip

On peut utiliser pip pour installer directement une librairie:

pip install pytest
Pytest est une librairie pour réaliser des tests sur le code.

Pour notre projet, il y a deux librairies dont nous aurons absolument besoin: requests et json
- requests permet d'envoyer et de recevoir des requêtes html. C'est un peu l'équivalent de curlen bash.
- json permet de traiter des données au format json

Comme ces deux librairies sont nécessaires au fonctionnement du projet, nous allons créer un fichier requirements.txt à la racine de notre projet.

requirements.txt

requests
json

Pour les installer, un simple:

pip install -r requirements.txt

2.2 Importation dans le projet

Dans notre fichier main.py nous devons maintenant importer les libraires

main.py

#!/usr/bin/env python3

import requests
import json

api_url = "https://gitlab.com/api/v4"
dataset = "/groups"
endpoint = api_url + dataset

3. Accès à l'API

Nous allons maintenant éxecuter ce code en mode interactif, et accéder à l'API dans la console python:

user@host:~$ python -i main.py
>>> r = requests.get(endpoint)
Nous avons placé dans la variable rle résultat de la fonction get, prise de la bibliothèque requests, avec comme 1er argument endpoint, c'est à dire https://gitlab.com/api/v4/groups

>>> print(r)
<Response [200]>
Le résultat ne semble pas encourageant, et pourtant ! Le code 200 signifie qu'une URL a bien été trouvée, ce qu'on préfère à une erreur 404!

>>> type(r)
<class 'requests.models.Response'>
Le type de r est une classe, c'est un objet Python, qui contient d'autres données que ce simple **Status Code 200**

>>> help(r)
>>> dir(r)
['__attrs__', '__bool__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_content', '_content_consumed', '_next', 'apparent_encoding', 'close', 'connection', 'content', 'cookies', 'elapsed', 'encoding', 'headers', 'history', 'is_permanent_redirect', 'is_redirect', 'iter_content', 'iter_lines', 'json', 'links', 'next', 'ok', 'raise_for_status', 'raw', 'reason', 'request', 'status_code', 'text', 'url']
Les fonctions help() et dir() nous aident à manipuler cet objet.

>>> r.__attrs__
['_content', 'status_code', 'headers', 'url', 'history', 'encoding', 'reason', 'cookies', 'elapsed', 'request'] 
La méthode __attrs nous donne une liste d'attributs de notre objet r

>>> r.url
'https://gitlab.com/api/v4/groups'
On peut ainsi vérifier l'URL sollicitée.

>>> r.content
Avant d'accéder au contenu, il va falloir le mettre en forme!


4. Formuler un requête à l'API

Comme il y a beaucoup de données (ici, tous les groupes créés sur Gitlab, il est nécessaire de filtrer notre requête.

Nous allons pour cela utiliser des paramètres, écrits au format json et stockés dans une variable nommée payload

>>> payload = {"search": "garage"}
>>> r = requests.get(endpoint,params=payload)
nous chercherons ainsi les groupes qui contiennent le mot garage
La fonction get prend comme deuxième argument les données json.
On place ces données dans la variable params afin de préserver payload

>>> r.headers
{'Date': 'Wed, 05 Aug 2020 18:42:47 GMT', 'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked', 
'Connection': 'keep-alive', 'Set-Cookie': '__cfduid=dcbe4c6164f5e175674b0abff0fb011c71596652967; expires=Fri, 04-Sep-20 18:42:47 GMT; 
path=/; domain=.gitlab.com; HttpOnly; SameSite=Lax; Secure', 'Vary': 'Accept-Encoding, Origin', 'Cache-Control': 'max-age=0, 
private, must-revalidate', 'Etag': 'W/"44cfa1c513193c8d78cdfc584f6dea72"', 'Link': '<https://gitlab.com/api/v4/groups?order_by=name&owned=false&page=2&per_page=20&search=garage&sort=asc&statistics=false&with_custom_attributes=false>; 
rel="next", <https://gitlab.com/api/v4/groups?order_by=name&owned=false&page=1&per_page=20&search=garage&sort=asc&statistics=false&with_custom_attributes=false>; 
rel="first", <https://gitlab.com/api/v4/groups?order_by=name&owned=false&page=2&per_page=20&search=garage&sort=asc&statistics=false&with_custom_attributes=false>; 
rel="last"', 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'SAMEORIGIN', 'X-Next-Page': '2', 'X-Page': '1', 'X-Per-Page': '20', 'X-Prev-Page': '', 
'X-Request-Id': 'wo9iLfsTax', 'X-Runtime': '0.116039', 'X-Total': '37', 'X-Total-Pages': '2', 'Strict-Transport-Security': 'max-age=31536000', 
'Referrer-Policy': 'strict-origin-when-cross-origin', 'Content-Encoding': 'gzip', 'GitLab-LB': 'fe-12-lb-gprd', 'GitLab-SV': 'api-18-sv-gprd', 
'CF-Cache-Status': 'DYNAMIC', 'cf-request-id': '046187edf40000edab20af4200000001', 'Expect-CT': 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"', 
'Server': 'cloudflare', 'CF-RAY': '5be2a8f65905edab-CDG'}
>>>     
>>> r.headers['X-Total']
'37'
Les headers nous donnent des informations intéressantes, et notamment ce X-Total, qui est le nombre total de résultats obtenus.
**Rappel : ** Les headers sont encadrés par des accolades ( curly brackets : { } ). C'est donc un objet de la classe dic. On accède à ses éléments en mettant entre crochets et guillemets la clé dont on veut voir la valeur, ici r.headers['X-Total']


5. Accéder au contenu de la requête

Il nous faut maintenant accéder aux données contenues dans la requête

>>> data = r.json()
>>>
>>> type(data)
<class 'list'>
On créé la variable data, dans lequelle on met le contenu r sur lequel on a appliqué la fonction json()

>>> data[0].keys()
dict_keys(['id', 'web_url', 'name', 'path', 'description', 'visibility', 'share_with_group_lock', 
'require_two_factor_authentication', 'two_factor_grace_period', 'project_creation_level', 'auto_devops_enabled', 
'subgroup_creation_level', 'emails_disabled', 'mentions_disabled', 'lfs_enabled', 'default_branch_protection', 
'avatar_url', 'request_access_enabled', 'full_name', 'full_path', 'created_at', 'parent_id', 'ldap_cn', 'ldap_access'])
Prenons le 1er élément de la liste data (numéroté 0), et consultons la liste de ses keys(). Chaque élément de la liste datacorrespond à un résultat de la recherche, et est constitué d'un dictionnaire dans lequel nous allons pouvoir itérer


6. Itération dans la liste de résultats

6.1 for

Nous allons utiliser la boucle for pour répéter une action sur chaque élément de la liste (=itérer)

>>> for i in data:
...     print(i['name'])  # Appuyez 2 fois Entrée pour créer un 1ère ligne non indentée 
...                       # et marquer la fin des instructions de for
AppGarage
Atheed-Garage
Code Garage
[..]
Garagenum
>>> 
Retenez bien la syntaxe simple de la boucle fordans Python: les : pour marquer le début des instructions, et c'est l'indentation ( 4 Space en début de ligne) seulement qui marque la liste des instructions.

6.2 if

Nous pouvons maintenant utiliser if pour afficher les détails d'un des résultats obtenus, que nous stockerons dans une variable nommée result:

>>> for i in data:
...     if i['name'] == "Garagenum":
...             result = i
... 
>>> print(result)
{'id': 4160307, 'web_url': 'https://gitlab.com/groups/garagenum', 'name': 'Garagenum', 'path': 'garagenum', 'description': '', 'visibility': 'public', 'share_with_group_lock': False, 'require_two_factor_authentication': False, 'two_factor_grace_period': 48, 'project_creation_level': 'developer', 'auto_devops_enabled': None, 'subgroup_creation_level': 'owner', 'emails_disabled': None, 'mentions_disabled': None, 'lfs_enabled': True, 'default_branch_protection': 2, 'avatar_url': None, 'request_access_enabled': False, 'full_name': 'Garagenum', 'full_path': 'garagenum', 'created_at': '2018-11-30T14:35:23.504Z', 'parent_id': None, 'ldap_cn': None, 'ldap_access': None}
Syntaxe similaire que pour le for, ici le ifest imbriqué dans le for

6.3 Affichage

Nous utiliserons aussi la boucle forpour afficher les résultats de manière lisible:

>>> for i in result:
...     print(i + " : " + str(result[i]))
... 
id : 4160307
web_url : https://gitlab.com/groups/garagenum
name : Garagenum
[...]

Le str(result[i])permet de transformer chaque valeur de resulten chaîne de caractères (str), pour permettre l'affichage par print()


7. Authentification

Certaines données peuvent demander une authentification pour en autoriser l'accès.

Cherchons par exemple, dans les sous-groupes du garage, un groupe privé nommé interventions:

>>> r = requests.get(endpoint,{"search":"garage"})
>>>
>>> for i in r.json():
...     if i['name'] == "Garagenum":
...             gn_id = i['id']
... 
>>> print("L'ID du groupe Garagenum est " + gn_id)
L'ID du groupe Garagenum est 4160307
>>>
>>>
>>> gn_endpoint = endpoint + "/" + gn_id + "/subgroups"
>>>
>>> payload = {'search': 'interventions'}
>>>
>>> r = requests.get(gn_endpoint, params = payload)
>>> for i in r.json():
...     print(i['name'])
... 
>>>

Aucun résultat, et pourtant, ce groupe existe!

Pour vous authentifier, il vous faut d'abord créer un jeton d'authentification dans gitlab

8. Générer un jeton d'authentification

Pour accéder aux données présentées par l'API (par exemple, la liste des 'issues' qui me sont attribuées), Gitlab a besoin de m'identifier.

Pour cela, à partir de notre interface web utilisateur de Gitlab, nous allons générer un jeton d'authentification.

Vous pouvez utiliser ce tutoriel pour générer votre jeton d'API

8.1 Accès authentifié

Il vous suffit maintenant de modifier la variable payload , qui contient la liste des options pour la requête.

>>> payload['private_token'] = "G9ajiTNzqGMSNxajgQx2"
>>>
>>> r = requests.get(gn_endpoint, payload)
>>>
>>> for i in r.json():
...     print(i['name'])
... 
interventions
Notez bien la manière dont une nouvelle clé private-token a été ajoutée au dictonnaire payload


9. Pour les étudiants

Conditions de validation

Pour valider cette unité, il faut que vous fassiez une merge-request vers la branche start Votre code doit permettre d'obtenir le résultat