• 12 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 20/04/2020

Créez une API avec Flask

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Tu veux encore nous parler de Flask ? :o

Oui, mais dans un autre objectif, cette fois-ci !

Au chapitre, précédent, le serveur Flask nous renvoyait du code HTML. Ce code était destiné à afficher une belle page web interprétable par vous, être humain.

La page que vous êtes en train de lire est codée en HTML. Tout y est inscrit : la police et la taille des mots que vous lisez, votre photo de profil en haut à droite, la couleur propre au site, tous les boutons de la barre du haut, etc.

Tout cela vient du code HTML envoyé par le serveur.

Mais ici, nous n’allons pas demander au serveur du code HTML, mais plutôt un fichier au format JSON. Le but ici n’est pas de demander une page affichable pour un être humain, mais plutôt des données brutes interprétables par un programme. Ce programme (situé côté client), c’est vous qui le programmerez plus tard. En gros, nous allons programmer une API.

Une API, qu’est-ce que c’est ?

Vous souhaitez un petit exemple ? Jetez un petit coup d’œil à ce lien.

Pas très agréable à lire, n’est-ce pas ? Normal, ces données sont formatées pour être interprétées par un programme. Pas besoin que ce soit beau : pas de belles couleurs ni d’images ! Juste du texte, des parenthèses, des accolades, etc. Ce format de données est le format JSON.

En fait, l’équivalent lisible par un humain est disponible à ce lien.

Dans les deux cas, l’opération est la même : en cliquant sur ces liens, vous avez demandé au site Wikipédia de rechercher les articles contenant le mot "Python". Wikipédia vous renvoie donc la liste des articles qu’il a trouvés, soit en version lisible dans le second cas, soit en version JSON quand vous demandez à l’API de Wikipédia.

Programmez votre premier "endpoint"

Pour programmer une API, c’est le même principe que ce que nous avons déjà vu : le navigateur envoie une requête HTTP au serveur. Mais ici, le serveur répondra par des données brutes plutôt que par du code HTML.

Dans une API, chaque URL interrogeable s’appelle un endpoint.

Dans le fichier app.py, ajoutez ces quelques lignes de code qui reprennent la même structure que ce que nous avons déjà écrit. Pour nous y retrouver, faisons commencer toutes les URL des endpoints de l’API par  /api.

Notre premier endpoint donnera les prévisions météo, attribuons-lui l’URL /api/meteo/  :

@app.route('/api/meteo/')
def meteo():
    pass # ligne temporaire

Ensuite, renvoyons un JSON.

Ce qui est pratique, c’est que la syntaxe de JSON est très similaire à celle de l’objet Python dictionnaire. On peut donc facilement convertir un dictionnaire en un JSON. Créons un dictionnaire contenant 3 clés : type, valeurs et unite. Remplacez les 3 lignes de code précédentes par celles-ci :

from flask import Flask, render_template, jsonify

@app.route('/api/meteo/')
def meteo():
    dictionnaire = {
        'type': 'Prévision de température',
        'valeurs': [24, 24, 25, 26, 27, 28],
        'unite': "degrés Celcius"
    }
    return jsonify(dictionnaire)

Ici, la réponse est renvoyée grâce à la fonction Flask  jsonify, qui convertit le dictionnaire en JSON, puis le renvoie sous forme de réponse HTTP.

Testez l’API dans le navigateur

Nous saisissons l’URL de l’endpoint :  http://localhost:5000/api/meteo/, et nous obtenons notre fichier JSON !

Mais où trouver les données météo ?

Eh bien, nous allons utiliser un service de météo en ligne : Openweathermap, qui propose lui aussi une API !

Ouh là ! Ça fait beaucoup d’API tout ça ! Je ne m’y retrouve plus. :waw:

Résumons. Jusqu’à maintenant, nous avons parlé de 2 API différentes :

  • celle que nous programmons en Python/Flask ;

  • celle que nous allons requêter pour obtenir les données météo : Openweathermaps.

La première est celle que nous programmons en Python. Elle recevra les requêtes du client (c’est-à-dire le navigateur). Mais ce client attendra du programme Python qu’il leur renvoie les données météo. Pour les obtenir, le programme Python se tournera alors vers l’API Openweathermaps.

Comme c’est Python qui envoie la requête, il devient alors lui-même client d’Openweathermaps ! Une fois que l’API Openweathermaps aura répondu, alors Python renverra les données reçues à son client : le navigateur.

Pour avoir une idée de la forme que prennent une requête et une réponse sur API, il existe une URL de test, avec de fausses données. Cliquez dessus pour explorer la structure du JSON.

Votre compte sera associé à une clé (une longue chaîne de caractères), qu’il faudra fournir à chaque requête à l’API :

Requêtez Openweathermaps

Enregistrons la clé dans le code (ligne 3 ci-dessous).

Enregistrons également l’endpoint de l’API (ligne 10 ci-dessous). Ajoutez ces lignes de code juste après  app = Flask(__name__)  :

from flask import Flask, render_template, jsonify

METEO_API_KEY = "cOZ97dje87dc7sj39b5c0kl825fcd830" # Remplacez cette ligne par votre clé OPENWEATHERMAP

if METEO_API_KEY is None:
    # URL de test :
    METEO_API_URL = "https://samples.openweathermap.org/data/2.5/forecast?lat=0&lon=0&appid=xxx"
else: 
    # URL avec clé :
    METEO_API_URL = "https://api.openweathermap.org/data/2.5/forecast?lat=48.883587&lon=2.333779&appid=" + METEO_API_KEY

Quand le client appellera l’endpoint  /api/meteo/, le programme Python enverra une requête vers l’API Openwheatermap (ligne 7 ci-dessus). On stocke la réponse d’Openweathermap dans la variable  response, puis on en extrait les données qui sont au format JSON, et on les convertit en dictionnaire Python grâce à  json.loads (ligne 8).

from flask import Flask, render_template, jsonify
import json
import requests

@app.route('/api/meteo/')
def meteo():
    response = requests.get(METEO_API_URL)
    content = json.loads(response.content.decode('utf-8'))

Si la requête HTTP à l’API Openweathermap a fonctionné, elle renvoie (en plus de la réponse) le statut 200, qui signifie que tout s’est bien passé.

Si la requête n’a pas fonctionné, alors le programme Python renverra au client un message d’erreur, avec le statut 500  (ligne 10 ci-dessous). Modifiez encore la fonction  meteo  par celle-ci :

@app.route('/api/meteo/')
def meteo():
    response = requests.get(METEO_API_URL)
    content = json.loads(response.content.decode('utf-8'))

    if response.status_code != 200:
        return jsonify({
            'status': 'error',
            'message': 'La requête à l\'API météo n\'a pas fonctionné. Voici le message renvoyé par l\'API : {}'.format(content['message'])
        }), 500

Ensuite, pour traiter les données JSON que nous avons récupérées, il faut regarder leur forme telle qu’elle est renvoyée par Openweathermaps :

ii

On y voit plusieurs choses :

  • Les prévisions météo se trouvent dans la clé  list. La valeur associée à cette clé est une liste de 40 éléments.

  • Chacun des 40 éléments est une prévision à une heure précise.

  • Dans chaque prévision, la date se retrouve par la clé  dt. Elle est codée sous forme de timestamp, c’est-à-dire un nombre de secondes.

  • La température est accessible par la clé  main  puis  temp. Cette température est exprimée en Kelvin.

Pour traiter toutes les prévisions de température, on va donc parcourir chacun des 40 éléments de la liste  list  grâce à une boucle Python : for prev in content["list"].

Voici cette boucle Python :

    data = [] # On initialise une liste vide
    for prev in content["list"]:
        datetime = prev['dt'] * 1000
        temperature = prev['main']['temp'] - 273.15 # Conversion de Kelvin en °c
        temperature = round(temperature, 2)
        data.append([datetime, temperature])

On peut d’ailleurs vérifier la forme de  data  en la retournant comme réponse...

    return jsonify({
      'status': 'ok', 
      'data': data
    })

 ... puis en entrant l’URL http://localhost:5000/api/meteo/  dans un navigateur.

Voici donc l’état actuel du fichier  app.py, où la ligne 8 doit contenir votre clé (ou bien  None) :

# -*- coding: utf-8 -*-
from flask import Flask, render_template, jsonify
import json
import requests

app = Flask(__name__)

METEO_API_KEY = "c30c785207dc7f397b5c036ba5fc70xx"

if METEO_API_KEY is None:
    # URL de test :
    METEO_API_URL = "https://samples.openweathermap.org/data/2.5/forecast?lat=0&lon=0&appid=xxx"
else: 
    # URL avec clé :
    METEO_API_URL = "https://api.openweathermap.org/data/2.5/forecast?lat=48.883587&lon=2.333779&appid=" + METEO_API_KEY

@app.route("/")
def hello():
    return "Hello World!"

@app.route('/dashboard/')
def dashboard():
    return render_template("dashboard.html")

@app.route('/api/meteo/')
def meteo():
    response = requests.get(METEO_API_URL)
    content = json.loads(response.content.decode('utf-8'))

    if response.status_code != 200:
        return jsonify({
            'status': 'error',
            'message': 'La requête à l\'API météo n\'a pas fonctionné. Voici le message renvoyé par l\'API : {}'.format(content['message'])
        }), 500

    data = [] # On initialise une liste vide
    for prev in content["list"]:
        datetime = prev['dt'] * 1000
        temperature = prev['main']['temp'] - 273.15 # Conversion de Kelvin en °c
        temperature = round(temperature, 2)
        data.append([datetime, temperature])
 
    return jsonify({
      'status': 'ok', 
      'data': data
    })

if __name__ == "__main__":
    app.run(debug=True)

Et voici le résultat dans le navigateur :

ii

Requêtez newsapi.org

Pour notre endpoint qui renverra les mots-clés des articles d’actualité, c’est exactement la même chose et la même structure du code. Il faudra récupérer une clé sur le site de l’API newsapi.org. On renverra 2 éléments au client : la liste de 100 mots-clés, appelée  keywords  (ligne 30), et la liste des articles récupérés, appelée  articles (ligne 31) :

from functions import extract_keywords

NEWS_API_KEY = None # Remplacez None par votre clé NEWSAPI, par exemple "4116306b167e49x993017f089862d4xx"

if NEWS_API_KEY is None:
    # URL de test :
    NEWS_API_URL = "https://s3-eu-west-1.amazonaws.com/course.oc-static.com/courses/4525361/top-headlines.json" # exemple de JSON
else:
    # URL avec clé :
    NEWS_API_URL = "https://newsapi.org/v2/top-headlines?sortBy=publishedAt&pageSize=100&language=fr&apiKey=" + NEWS_API_KEY

@app.route('/api/news/')
def get_news():
 
    response = requests.get(NEWS_API_URL)

    content = json.loads(response.content.decode('utf-8'))

    if response.status_code != 200:
        return jsonify({
            'status': 'error',
            'message': 'La requête à l\'API des articles d\'actualité n\'a pas fonctionné. Voici le message renvoyé par l\'API : {}'.format(content['message'])
        }), 500

    keywords, articles = extract_keywords(content["articles"])

    return jsonify({
        'status'   : 'ok',
        'data'     :{
            'keywords' : keywords[:100], # On retourne uniquement les 100 premiers mots
            'articles' : articles
        }
    })

Vous aurez ici besoin d’une fonction qui détecte les mots-clés des articles. Il vous faut pour cela télécharger le fichier  __init__.py  (présent sur le git, dans le dossier Partie_3/functions), ainsi qu’une liste des mots fréquents en français, mais qui n’ont pas de sens, que l’on appelle "stopwords".

Placez __init__.py  dans un nouveau dossier appelé functions (il doit se trouver au même endroit que  app.py). Le fichier  stop_words.txt  doit quant à lui se trouver dans le même dossier que  app.py.

Voilà ! Vous étiez jusqu’à présent du côté du serveur. Et maintenant, je vous propose de passer du côté client, en récupérant les données fournies par notre API.

Exemple de certificat de réussite
Exemple de certificat de réussite