Skip to content

Commit

Permalink
Webscraping exercice nom et age ministres (#326)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people committed Nov 22, 2022
1 parent 738c074 commit 037842a
Showing 1 changed file with 163 additions and 13 deletions.
176 changes: 163 additions & 13 deletions content/course/manipulation/04a_webscraping_TP/index.qmd
Expand Up @@ -989,9 +989,6 @@ et enregistrer en local les images.
:::





```{python}
#| include: false
#| echo: false
Expand Down Expand Up @@ -1031,7 +1028,6 @@ shutil.copyfile("bulbasaur.jpg", "featured.jpg")




# `Selenium` : mimer le comportement d'un utilisateur internet


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

# Exercices supplémentaires

## Récupérer les noms et âges des ministres français

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.
La solution pour cet exercice a été proposée
par [@tttienthinh](https://github.com/tttienthinh)
et [Antoine Palazzolo](https://github.com/antoine-palazz).

Pour être en mesure de faire cet exercice, il est
recommandé d'installer le package `dateparser`

```{python}
#| output: false
!pip install dateparser
#depuis un notebook. En ligne de commande, retirer le !
```

Pour cet exercice, nous proposons d'utiliser les _packages_
suivants:

# Exercice supplémentaire [BONUS]
```{python}
import time
from tqdm import tqdm
import urllib
import re, datetime
from dateutil.parser import parse as parse_dt
import dateparser
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import bs4
```

Nous proposons également d'utiliser la fonction suivante
pour calculer l'âge à partir de la date de naissance.

```{python}
def from_birth_to_age(birth):
today = datetime.date.today()
return today.year - birth.year - ((today.month, today.day) < (birth.month, birth.day))
```


::: {.cell .markdown}
```{=html}
<div class="alert alert-success" role="alert">
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice : liste des ministres français grâce à Wikipedia </h3>
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice : Les ministres français </h3>
```


1. Créer des variables globales `url_gouvernement` et `url_gouvernement` qui représenteront,
respectivement, la racine de l'URL du site web et le chemin au sein de celui-ci ;
2. Utiliser `bs4` pour récupérer la composition du gouvernement, qui est contenue dans un `<div>`
ayant une classe _ad hoc_. Nommer cet objet `compo`
3. Utiliser `find_all` pour récupérer la liste des ministres dans `compo`. Nommer
cet objet `ministres`
4. Inspecter la structure des champs au sein de `ministres`. Répérer les id `biography`. Comme
la structure est générique, on va écrire une fonction `from_bio_to_age` sur laquelle on va itérer
pour chaque élément de la liste `ministres`. Cette fonction effectuera les opérations suivantes:
+ Remplacer les champs de dates de naissance non numériques (par exemple _"1er"_), en valeur numérique (par exemple 1).
+ Utiliser la regex `[0-3]?\d \S* \d{4}` avec le _package_ `re` pour extraire les dates
de naissance. Nommer l'objet `str_date`.
+ Appliquer `dateparser.parse` pour convertir sous forme de date
+ Appliquer `from_birth_to_age` pour transformer cette date de naissance en âge
5. Pour chaque élément de la liste `ministres`, faire une boucle (en introduisant un
`time.sleep(0.25)` entre chaque itération pour ne pas surcharger le site):
+ Récupérer les noms et prénoms, fonctions pour chaque ministre
+ Récupérer l'URL de la photo
+ Créer un URL pour chaque ministre afin d'appliquer la fonction
`from_bio_to_age`
6. Utiliser `matplotlib` ou `seaborn` pour faire un histogramme d'âge

```{=html}
</div>
```
:::

```{python}
#| echo: false
# 1/ Créer des variables globales
url_gouvernement = "https://www.gouvernement.fr"
suffixe_ministres = "/composition-du-gouvernement"
```

```{python}
#| echo: false
# 2/ Récupérer compo gouvernement
url = f"{url_gouvernement}/{suffixe_ministres}"
html = urllib.request.urlopen(url).read()
page = bs4.BeautifulSoup(html)
compo = page.find("div", {"class":"composition-du-gouvernement-contenu"}) # Nous n'avons besoin que de la composition
```

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

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

```{=html}
</div>
```{python}
print(f"Nous retrouvons ainsi {len(ministres)} ministres.")
```

```{python}
def from_bio_to_age(url):
html = urllib.request.urlopen(url).read()
page = bs4.BeautifulSoup(html)
s = page.find("div", {"id":"biography"}).text.replace("1er", "1") # un peu ad hoc
expression = re.compile("[0-3]?\d \S* \d{4}") # renvoie parfois des dates autres que dates de naissance
str_date = expression.findall(s)[0]
date_de_naissance = dateparser.parse(str_date).date()
return from_birth_to_age(date_de_naissance)
```
:::

```{python}
#| include: false
liste = []
for ministre in tqdm(ministres):
prenom_nom = ministre.find("a", {"class":"ministre-nom"}).text
fonction = ministre.find("p", {"class":"ministre-fonction"}).text
photo = ministre.find("img")["src"]
href = url_gouvernement + ministre.find("a", {"class":"ministre-nom"})["href"]
try:
age = from_bio_to_age(href)
except:
age = np.NaN
liste.append({
'Nom complet': prenom_nom,
'Fonction': fonction,
'Photo': photo,
'href': href,
'Age': age
})
time.sleep(0.25) # Ne pas surcharger les requêtes
```

_In fine_, on obtient une liste dont le premier élément
prend la forme suivante:

```{python}
liste[0]
```

Finalement, le `DataFrame` pourra être
structuré sous la forme suivante. On va éliminer
les âges égaux à 0 sont qui sont des erreurs
de scraping:
lorsque la date de naissance complète n'est pas disponible
sur la biographie d'un ministre.

```{python}
df = pd.DataFrame(liste)
df = df.loc[df['Age'] != 0]
df.head(3)
```

Finalement, l'histogramme aura l'aspect suivant:

```{python}
plt.hist(df.Age, bins=np.arange(25, 80, 4))
```



0 comments on commit 037842a

Please sign in to comment.