Skip to content

Commit 037842a

Browse files
linogalianaantoine-palazzgithub-actions[bot]
authored
Webscraping exercice nom et age ministres (#326)
* Proposition exo ministre (#325) * bloc code * modifie blocs * ajouts TP scraping dans le qmd * Automated changes * Automated changes * ministre * Automated changes * Automated changes * commandes * Automated changes * Automated changes * hide output * Automated changes * Automated changes * Enrobage * Automated changes * Automated changes Co-authored-by: Antoine Palazzolo <97433407+antoine-palazz@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 738c074 commit 037842a

File tree

1 file changed

+163
-13
lines changed
  • content/course/manipulation/04a_webscraping_TP

1 file changed

+163
-13
lines changed

content/course/manipulation/04a_webscraping_TP/index.qmd

Lines changed: 163 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -989,9 +989,6 @@ et enregistrer en local les images.
989989
:::
990990

991991

992-
993-
994-
995992
```{python}
996993
#| include: false
997994
#| echo: false
@@ -1031,7 +1028,6 @@ shutil.copyfile("bulbasaur.jpg", "featured.jpg")
10311028

10321029

10331030

1034-
10351031
# `Selenium` : mimer le comportement d'un utilisateur internet
10361032

10371033

@@ -1252,23 +1248,177 @@ print('Score final : {} en {} coups'.format(scoreElem.text, count))
12521248
browser.quit()
12531249
```
12541250

1251+
# Exercices supplémentaires
1252+
1253+
## Récupérer les noms et âges des ministres français
1254+
1255+
Pour cet exercice, on propose de scraper la liste des ministres français depuis le [site du gouvernement](https://www.gouvernement.fr/composition-du-gouvernement). L'objectif sera, _in fine_ de faire un graphique qui représente la distribution de leurs âges.
1256+
La solution pour cet exercice a été proposée
1257+
par [@tttienthinh](https://github.com/tttienthinh)
1258+
et [Antoine Palazzolo](https://github.com/antoine-palazz).
1259+
1260+
Pour être en mesure de faire cet exercice, il est
1261+
recommandé d'installer le package `dateparser`
1262+
1263+
```{python}
1264+
#| output: false
1265+
!pip install dateparser
1266+
#depuis un notebook. En ligne de commande, retirer le !
1267+
```
1268+
1269+
Pour cet exercice, nous proposons d'utiliser les _packages_
1270+
suivants:
12551271

1256-
# Exercice supplémentaire [BONUS]
1272+
```{python}
1273+
import time
1274+
from tqdm import tqdm
1275+
import urllib
1276+
import re, datetime
1277+
from dateutil.parser import parse as parse_dt
1278+
import dateparser
1279+
1280+
import matplotlib.pyplot as plt
1281+
1282+
import numpy as np
1283+
import pandas as pd
1284+
import bs4
1285+
```
1286+
1287+
Nous proposons également d'utiliser la fonction suivante
1288+
pour calculer l'âge à partir de la date de naissance.
1289+
1290+
```{python}
1291+
def from_birth_to_age(birth):
1292+
today = datetime.date.today()
1293+
return today.year - birth.year - ((today.month, today.day) < (birth.month, birth.day))
1294+
```
12571295

12581296

12591297
::: {.cell .markdown}
12601298
```{=html}
12611299
<div class="alert alert-success" role="alert">
1262-
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice : liste des ministres français grâce à Wikipedia </h3>
1300+
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice : Les ministres français </h3>
1301+
```
1302+
1303+
1304+
1. Créer des variables globales `url_gouvernement` et `url_gouvernement` qui représenteront,
1305+
respectivement, la racine de l'URL du site web et le chemin au sein de celui-ci ;
1306+
2. Utiliser `bs4` pour récupérer la composition du gouvernement, qui est contenue dans un `<div>`
1307+
ayant une classe _ad hoc_. Nommer cet objet `compo`
1308+
3. Utiliser `find_all` pour récupérer la liste des ministres dans `compo`. Nommer
1309+
cet objet `ministres`
1310+
4. Inspecter la structure des champs au sein de `ministres`. Répérer les id `biography`. Comme
1311+
la structure est générique, on va écrire une fonction `from_bio_to_age` sur laquelle on va itérer
1312+
pour chaque élément de la liste `ministres`. Cette fonction effectuera les opérations suivantes:
1313+
+ Remplacer les champs de dates de naissance non numériques (par exemple _"1er"_), en valeur numérique (par exemple 1).
1314+
+ Utiliser la regex `[0-3]?\d \S* \d{4}` avec le _package_ `re` pour extraire les dates
1315+
de naissance. Nommer l'objet `str_date`.
1316+
+ Appliquer `dateparser.parse` pour convertir sous forme de date
1317+
+ Appliquer `from_birth_to_age` pour transformer cette date de naissance en âge
1318+
5. Pour chaque élément de la liste `ministres`, faire une boucle (en introduisant un
1319+
`time.sleep(0.25)` entre chaque itération pour ne pas surcharger le site):
1320+
+ Récupérer les noms et prénoms, fonctions pour chaque ministre
1321+
+ Récupérer l'URL de la photo
1322+
+ Créer un URL pour chaque ministre afin d'appliquer la fonction
1323+
`from_bio_to_age`
1324+
6. Utiliser `matplotlib` ou `seaborn` pour faire un histogramme d'âge
1325+
1326+
```{=html}
1327+
</div>
1328+
```
1329+
:::
1330+
1331+
```{python}
1332+
#| echo: false
1333+
# 1/ Créer des variables globales
1334+
url_gouvernement = "https://www.gouvernement.fr"
1335+
suffixe_ministres = "/composition-du-gouvernement"
1336+
```
1337+
1338+
```{python}
1339+
#| echo: false
1340+
# 2/ Récupérer compo gouvernement
1341+
url = f"{url_gouvernement}/{suffixe_ministres}"
1342+
html = urllib.request.urlopen(url).read()
1343+
page = bs4.BeautifulSoup(html)
1344+
compo = page.find("div", {"class":"composition-du-gouvernement-contenu"}) # Nous n'avons besoin que de la composition
12631345
```
12641346

1265-
Scraper la liste des ministres français depuis wikipedia. Faire une graphique qui représente la distribution de leur âge.
1347+
```{python}
1348+
#| echo: false
1349+
# 3/ Récupérer les ministres
1350+
ministres = compo.find_all("div", {"class":"ministre"})
1351+
```
12661352

1267-
Si vous avez une solution satisfaisante, n'hésitez pas à la soumettre
1268-
sur <a href="https://github.com/linogaliana/python-datascientist" class="github"><i class="fab fa-github"></i></a>
1269-
(car je n'ai pas encore testé...)
1353+
A l'issue de la question 4, on devrait
1354+
retrouver les informations suivantes:
12701355

1271-
```{=html}
1272-
</div>
1356+
```{python}
1357+
print(f"Nous retrouvons ainsi {len(ministres)} ministres.")
1358+
```
1359+
1360+
```{python}
1361+
def from_bio_to_age(url):
1362+
html = urllib.request.urlopen(url).read()
1363+
page = bs4.BeautifulSoup(html)
1364+
s = page.find("div", {"id":"biography"}).text.replace("1er", "1") # un peu ad hoc
1365+
expression = re.compile("[0-3]?\d \S* \d{4}") # renvoie parfois des dates autres que dates de naissance
1366+
str_date = expression.findall(s)[0]
1367+
date_de_naissance = dateparser.parse(str_date).date()
1368+
return from_birth_to_age(date_de_naissance)
12731369
```
1274-
:::
1370+
1371+
```{python}
1372+
#| include: false
1373+
1374+
liste = []
1375+
1376+
for ministre in tqdm(ministres):
1377+
prenom_nom = ministre.find("a", {"class":"ministre-nom"}).text
1378+
fonction = ministre.find("p", {"class":"ministre-fonction"}).text
1379+
photo = ministre.find("img")["src"]
1380+
href = url_gouvernement + ministre.find("a", {"class":"ministre-nom"})["href"]
1381+
try:
1382+
age = from_bio_to_age(href)
1383+
except:
1384+
age = np.NaN
1385+
1386+
liste.append({
1387+
'Nom complet': prenom_nom,
1388+
'Fonction': fonction,
1389+
'Photo': photo,
1390+
'href': href,
1391+
'Age': age
1392+
})
1393+
1394+
time.sleep(0.25) # Ne pas surcharger les requêtes
1395+
```
1396+
1397+
_In fine_, on obtient une liste dont le premier élément
1398+
prend la forme suivante:
1399+
1400+
```{python}
1401+
liste[0]
1402+
```
1403+
1404+
Finalement, le `DataFrame` pourra être
1405+
structuré sous la forme suivante. On va éliminer
1406+
les âges égaux à 0 sont qui sont des erreurs
1407+
de scraping:
1408+
lorsque la date de naissance complète n'est pas disponible
1409+
sur la biographie d'un ministre.
1410+
1411+
```{python}
1412+
df = pd.DataFrame(liste)
1413+
df = df.loc[df['Age'] != 0]
1414+
df.head(3)
1415+
```
1416+
1417+
Finalement, l'histogramme aura l'aspect suivant:
1418+
1419+
```{python}
1420+
plt.hist(df.Age, bins=np.arange(25, 80, 4))
1421+
```
1422+
1423+
1424+

0 commit comments

Comments
 (0)