Programmation client, année 2018

Annonce: le cours du 06 Novembre sera un TD noté de 2h. Tout les TD ainsi que les contenus des corrections sont exigibles. Une partie du TD sera rédactionnelle et une partie sera de la programmation en Python.

Correction du TD 4

Les fonctions ne sont pas détaillés dans la correction. C’est le travail de l’étudiant d’aller voir dans la documentation de Python leur fonctionnement sur des exemples. Vous pouvez également utiliser ipython pour tester directement vous même.

Question 1

Écrire une fonction Python qui prend en argument une chaîne de caractère et retourne la liste des villes de France qui débute par cette chaîne de caractères.

Pour ce faire, on utilise l’url obtenu au TD précédant: https://geo.api.gouv.fr/communes?&nom=chaine_de_carctère

from urllib.request import urlopen
import json
def recuperer_villes(argument):
    url = "https://geo.api.gouv.fr/communes?&nom={}".format(argument)

    reponse_binaire = urlopen(url).read()
    reponse_str = reponse_binaire.decode()
    reponse_json = json.loads(reponse_str) #format [{'nom':nom_ville, 'population':nombre, ...}, {...}]
    # équivalent à:
    # reponse_json = json.loads(urlopen(url.format(argument)).read().decode())
    liste_ville = list(map(lambda e:e["nom"], reponse_json))
    return liste_ville
print("\n".join(recuperer_villes("Lil"))) # ceci est juste un teste, 
# Il faut toujours vérifier que la fonction marche !

Pour aller plus loin:

Il est possible de vouloir retourner une liste contenant le nom de la ville mais aussi le nombre d’habitants. On peut ajouter des arguments à la fonctions:

from urllib.request import urlopen
import json
def recuperer_villes(argument, population=False): #le comportement par défaut est de retourner que les noms
    url = "https://geo.api.gouv.fr/communes?&nom={}".format(argument)
    reponse_binaire = urlopen(url).read()
    reponse_str = reponse_binaire.decode()
    reponse_json = json.loads(reponse_str) #format [{'nom':nom_ville, 'population':nombre, ...}, {...}]
    # équivalent à:
    # reponse_json = json.loads(urlopen(url.format(argument)).read().decode())
    if population:
        liste_ville = list(map(lambda e:(e["nom"], e["population"] if "population" in e else 0), reponse_json))
    else:
        liste_ville = list(map(lambda e:e["nom"], reponse_json))
    return liste_ville
print("\n".join(map(lambda e:"{}, {}".format(e[0],e[1]), recuperer_villes("Lil", population=True)))) # ceci est juste un teste,
print("#"*20)
print("\n".join(recuperer_villes("Lil",))) # ceci est juste un teste, 

Question 2

Créer une nouvelle fonction Python qui retourne un tableau HTML de la liste de la question 1.

L’objectif ici et d’améliorer la sortie de la fonction précédante pour l’afficher en HTML. L’intérêt des fonctions, c’est qu’il n’est pas nécessaire de recoder la première partie, on peut juste utiliser la fonction de la question 1.

def recuperer_villes_html(argument):
    villes = recuperer_villes(argument, population=True)
    liste_html = map(lambda e:"<tr><td>{}</td><td>{}</td><tr>".format(e[0],e[1]), villes)
    return "<table>\n{}\n</table>".format("\n".join(liste_html))

Ici on utilise l’opération map pour produire chaque ligne.

Question 3

Créer une fonction en Python qui retourne la température des 10 villes les plus peuplées de chaque région à l’aide des données de Météo France extraite à partir de BeautifulSoup.

On utilise les donnée de l’API pour récupérer les 10 villes les plus peuplées de chaque région. On peut récupérer la liste des régions avec les codes régions à l’adresse https://geo.api.gouv.fr/regions. Pour se simplifier la vie, on peut créer une fonction qui retourne ses données:

def recuperer_regions():
    url = 'https://geo.api.gouv.fr/regions'
    return json.loads(urlopen(url).read().decode()) #retourne [{'code': '01', 'nom': 'Guadeloupe'},...]

On peut récupérer les villes de chaque régions à l’aide de l’url: ̀https://geo.api.gouv.fr/communes?codeRegion=nombre` où nombre est le code donnée par codeRegion retourner par la fonction recuperer_regions. On va donc modifier la fonction de la question 1 pour prendre en compte ce nouveau filtre:

def recuperer_villes_par_region(code_region): 
    url = "https://geo.api.gouv.fr/communes?codeRegion={}".format(codeRegion)   
    reponse_json = json.loads(urlopen(url.format(argument)).read().decode())
    if population:
liste_ville = list(map(lambda e:(e["nom"], e["population"] if "population" in e else 0), reponse_json))
    else:
        liste_ville = list(map(lambda e:e["nom"], reponse_json))
    return liste_ville
def recuperer_villes_par_region(code_region): 
    url = "https://geo.api.gouv.fr/communes?codeRegion={}".format(code_region)  
    reponse_json = json.loads(urlopen(url).read().decode())
    liste_ville = list(map(lambda e:(e["nom"], e["population"] if "population" in e else 0), reponse_json))
    return liste_ville
def villes_peuples_region():
    liste_regions = recuperer_regions()
    resultat = {} #dictionnaire
    for region in liste_regions:
        liste_villes = recuperer_villes_par_region(region["code"]) #récupérer toutes les villes de la région 
        villes_peuples = list(sorted(liste_villes, key=lambda e:e[1]))[-10:]
        resultat[region["nom"]] = villes_peuples
    return resultat

print(villes_peuples_region()["Martinique"])

Extraire les données de Météo-France

Si on dispose l’URL adéquate, il est simple de récupérer avec BeautifulSoup la température d’un lieu. Nous allons dans un premier temps comprendre comment récupérer cette URL puis comment récupérer l’information pertinente depuis cette URL.

En utilisant le navigateur et l’explorateur d’échanges de données (onglet network ou réseaux en français, dans les outils de développements) on peut comprendre comment fonctionne la barre de recherche présente sur le site de météo-France. À chaque entrée du clavier, l’url suivante est interrogée: http://www.meteofrance.com/mf3-rpc-portlet/rest/lieu/facet/previsions/search et retourne une liste de communes possibles. On va l’utiliser pour transcrire les codes postaux en url pour le site de météo France.

Le format de retour de cette URL est un document JSON qui contient deux informations indispensables pour poursuivre: le nom de la commune au format utilisé par Météo-France et son code postal. Il s’agit des champs: slug et codePostal.

Par exemple, pour les villes ayant des arrondissements, comme Paris, l’adresse de la météo dans le 15ème arrondissement de Paris est: http://www.meteofrance.com/previsions-meteo-france/paris-15e-arrondissement/75015 alors que pour Lyon, le 2ème arrondissement n’a pas de page attitré qui renvois vers celles de la ville.

On va donc faire une fonction qui récupère ces informations.

def code_postal_to_meteo_france(code_postal):
    url_infos = "http://www.meteofrance.com/mf3-rpc-portlet/rest/lieu/facet/previsions/search/{}".format(code_postal)
    infos = json.load(urlopen(url_infos)) #récupère le document JSON
    slug = infos[0]["slug"] #ce dernier est de la forme [ {'slug':...,..., 'codePostal':...},{...},..]
    codePostal = infos[0]["codePostal"] # on s'occupe que du premier résultat 
    return "http://www.meteofrance.com/previsions-meteo-france/{}/{}".format(slug,codePostal)

Maintenant qu’on une URL. On peut miner l’information nécessaire pour récupérer la météo associée à un code postal. Pour ce faire, on se balade dans le DOM à l’aide de BeautifulSoup. Il existe pleins de manière différentes de procéder.

Par exemple, li = soup.find("div", attrs={"class":"liste-jours"}).find("li") sélectionne la première entrée d’une liste qui appartient au premier div dont la classe est “liste-jours”. Cet élément de liste contient l’information qu’on recherche. En faisant: li.find_all("span") on récupère les deux éléments HTML qui contiennent la température du jour.

def meteo_france_temperature(url):
    soup = BeautifulSoup(urlopen(url), "html5lib")
    li = soup.find("div", attrs={"class":"liste-jours"}).find("li")
    return list(map(lambda e:e.get_text().split("C")[0], li.find_all("span")))

On peut regrouper ces deux fonctions en une fonction meteo qui les appelles successivement. Cette approche entraîne néanmoins quelques problèmes avec des régions comme la Guadeloupe. Afin de gérer ces problèmes, on peut utiliser un simple bloc try ... except ...:

def meteo(code_postal):
    try:
        return meteo_france_temperature(code_postal_to_meteo_france(code_postal))
    except:
        return ("N/A","N/A") #retourne que l'information n'est pas connue

Fin de la question

On peut maintenant regrouper les fonctions précédente pour ajouter à chaque ville la température minimale et maximale en plus de la population.

def villes_peuples_region_meteo():
    liste_regions = villes_peuples_region() #on récupère la liste des
    # villes par régions déjà connue
    for region in liste_regions:
        villes_peuples = liste_regions[region]
        villes_peuples_meteo = list(map(lambda e:e+tuple(meteo(e[2])), villes_peuples))     
        #On ajoute les résultats de météo.
        resultat[region["nom"]] = villes_peuples_meteo
    return resultat

Attention, essayez de comprendre pourquoi on utilise le mot clef tuple ici.

Conclusion

Le fichier de script peut être trouvé ici.

La question 4 est une adaptation à faire seul de la question 2. Les questions 5 et 6 doivent être étudiés comme exercice avant le devoir sur table de la rentrée.


Compiled the: dim. 07 janv. 2024 23:19:25 CET