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: mer. 08 janv. 2025 11:51:36 CET