Web Scraping avec Python : Guide Pratique
Web Scraping avec Python : Guide Pratique
Le webscraping désigne les techniques d’extraction du contenu des sites internet. C’est une pratique très utile pour
toute personne souhaitant travailler sur des informations disponibles en ligne, mais n’existant pas forcément sous la
forme d’un tableau Excel.
Ce TP vous présente comment créer et exécuter des robots afin de recupérer rapidement des informations utiles à vos
projets actuels ou futurs.
Le webscraping a un certain nombre d’enjeux en termes de légalité, qui ne seront pas enseignés dans ce cours mais
dans un cours de 3A à l’ENSAE. En particulier, la CNIL a publié en 2020 de nouvelles directives sur le webscraping
reprécisant que toute donnée ne peut être réutilisée à l’insu de la personne à laquelle ces données appartiennent.
Enfin, il est à noter que ce notebook est très fortement inspiré et réadapté à partir de celui de Xavier Dupré, l’ancien
professeur de la matière.
Un site Web est un ensemble de pages codées en HTML qui permet de décrire à la fois le contenu et la forme d’une
page Web.
Pour voir cela, ouvrez n’importe quelle page web et faites un clic-droit dessus.
• Sous Chrome : Cliquez ensuite sur “Affichez le code source de la page” ( CTRL + U ) ;
• Sous Firefox : “Code source de la page” ( CTRL + MAJ + K ) ;
• Sous Edge : “Affichez la page source” ( CTRL + U ) ;
• Sous Safari : voir comment faire ici
Les balises
Sur une page web, vous trouverez toujours à coup sûr des éléments comme <head>, <title>, etc. Il s’agit des codes
qui vous permettent de structurer le contenu d’une page HTML et qui s’appellent des balises. Citons, par exemple, les
balises <p>, <h1>, <h2>, <h3>, <strong> ou <em>. Le symbole < > est une balise : il sert à indiquer le début d’une
partie. Le symbole <∕ > indique la fin de cette partie. La plupart des balises vont par paires, avec une balise ouvrante
et une balise fermante (par exemple <p> et <∕p>).
<table> Tableau
1 sur 22
Webscraping avec python | Python pour les data scientists
Ligne de tableau
<td> Cellule
<table>
<caption> Le Titre de mon tableau <∕caption>
<tr>
<th>Prénom<∕th>
<th>Nom<∕th>
<th>Profession<∕th>
<∕tr>
<tr>
<td>Mike <∕td>
<td>Stuntman<∕td>
<td>Cascadeur<∕td>
<∕tr>
<tr>
<td>Mister<∕td>
<td>Pink<∕td>
<td>Gangster<∕td>
<∕tr>
<∕table>
Parent et enfant
Dans le cadre du langage HTML, les termes de parent (parent) et enfant (child) servent à désigner des élements
emboîtés les uns dans les autres. Dans la construction suivante, par exemple :
< div>
< p>
bla,bla
< ∕p>
< ∕div>
<p>
bla,bla
<∕p>
On dira que l’élément <div> est le parent de l’élément <p> tandis que l’élément <p> est l’enfant de l’élément <div>.
Parce que, pour bien récupérer les informations d’un site internet, il faut pouvoir comprendre sa structure et donc son
code HTML. Les fonctions python qui servent au scraping sont principalement construites pour vous permettre de
naviguer entre les balises.
2 sur 22
Webscraping avec python | Python pour les data scientists
BeautifulSoup sera suffisant quand vous voudrez travailler sur des pages HTML statiques. Dès que les informations
que vous recherchez sont générées via l’exécution de scripts Javascript, il vous faudra passer par des outils comme
Selenium.
De même, si vous ne connaissez pas l’URL, il faudra passer par un framework comme Scrapy, qui passe facilement
d’une page à une autre (“crawl”). Scrapy est plus complexe à manipuler que BeautifulSoup : si vous voulez plus de
détails, rendez-vous sur la page du tutoriel Scrapy.
Le webscraping est un domaine où la reproductibilité est compliquée à mettre en oeuvre. Une page web évolue
potentiellement régulièrement et d’une page web à l’autre, la structure peut être très différente ce qui rend certains
codes difficilement exportables. Par conséquent, la meilleure manière d’avoir un programme fonctionnel est de
comprendre la structure d’une page web et dissocier les éléments exportables à d’autres cas d’usages des requêtes ad
hoc.
import urllib
import bs4
import pandas
from urllib import request
url_ligue_1 = "https:∕∕[Link]∕wiki∕Championnat_de_France_de_football_2019-2020"
request_text = [Link](url_ligue_1).read()
# print(request_text[:1000])
type(request_text)
3 sur 22
Webscraping avec python | Python pour les data scientists
type(request_text)
bytes
#print(page)
Si on print l’objet, page créée avec BeautifulSoup, on voit que ce n’est plus une chaine de caractères mais bien une
page HTML avec des balises. On peut à présent chercher des élements à l’intérieur de ces balises.
La méthode find
Par exemple, si on veut connaître le titre de la page, on utilise la méthode .find et on lui demande “title”
print([Link]("title"))
print([Link]("table"))
<table><caption style="background-color:#99cc99;color:#000000;">Généralités<∕caption><tbody><tr>
<th scope="row" style="width:10.5em;">Sport<∕th>
<td>
<a href="∕wiki∕Football" title="Football">Football<∕a><∕td>
<∕tr>
<tr>
<th scope="row" style="width:10.5em;">Organisateur(s)<∕th>
<td>
<a href="∕wiki∕Ligue_de_football_professionnel" title="Ligue de football professionnel">LFP<∕a><∕td>
<∕tr>
<tr>
<th scope="row" style="width:10.5em;">Édition<∕th>
<td>
<abbr class="abbr" title="Quatre-vingt-deuxième (huitante-deuxième ∕ octante-deuxième)">82<sup>e<∕sup><∕abbr><∕td>
<∕tr>
<tr>
<th scope="row" style="width:10.5em;">Lieu(x)<∕th>
<td>
<span class="datasortkey" data-sort-value="France"><span class="flagicon"><a class="image" href="∕wiki∕Fichier:Flag_of_France.svg" title=
<∕tr>
<tr>
<th scope="row" style="width:10.5em;">Date<∕th>
<td>
Du <time class="nowrap date-lien" data-sort-value="2019-08-09" datetime="2019-08-09"><a href="∕wiki∕9_ao%C3%BBt_en_sport" title="9 août e
<∕tr>
<tr>
<th scope="row" style="width:10.5em;">Participants<∕th>
<td>
20 équipes<∕td>
<∕tr>
<tr>
<th scope="row" style="width:10.5em;">Matchs joués<∕th>
<td>
279 (sur 380 prévus)<∕td>
<∕tr>
<tr>
<th scope="row" style="width:10.5em;">Site web officiel<∕th>
4 sur 22
Webscraping avec python | Python pour les data scientists
Sport Football
Organisateur(s) LFP
France et
Lieu(x)
Monaco
Du 9 août 2019
Date
au 8 mars 2020 (arrêt définitif)
Participants 20 équipes
Généralités
La méthode findAll
Pour trouver toutes les occurences, on utilise .findAll().
print("Il y a", len([Link]("table")), "éléments dans la page qui sont des <table>")
Exercice
�. Trouver le tableau
�. Récupérer chaque ligne du table
�. Nettoyer les sorties en ne gardant que le texte sur une ligne
�. Généraliser sur toutes les lignes
�. Récupérer les entêtes du tableau
�. Finalisation du tableau
� Trouver le tableau
# on identifie le tableau en question : c'est le premier qui a cette classe "wikitable sortable"
tableau_participants = [Link]('table', {'class' : 'wikitable sortable'})
print(tableau_participants)
Nombre
Dernière Budget\[3\] Classement Capacité de
Club Entraîneur Depuis Stade \[4\]
montée en M€ 2018-2019 en L1 saisons
en L1
Paris Saint-
1974 637 1er (Premier) Thomas Tuchel 2018 Parc des Princes 47 929 46
Germain
e Christophe
LOSC Lille 2000 120 2 (Deuxième) 2017 Stade Pierre-Mauroy 49 712 59
Galtier
Olympique e
1989 310 3 (Troisième) Rudi Garcia 2019 Groupama Stadium 57 206 60
lyonnais
AS Saint-Étienne 2004 100 4e (Quatrième) Claude Puel 2019 Stade Geoffroy-Guichard 41 965 66
Olympique de e André Villas-
Cinquième Orange Vélodrome
5 sur 22
Webscraping avec python | Python pour les data scientists
table_body = tableau_participants.find('tbody')
# on obtient une liste où chaque élément est une des lignes du tableau
<tr>
<th scope="col">Club
<∕th>
<th scope="col">Dernière<br∕>montée
<∕th>
<th scope="col">Budget<sup class="reference" id="cite_ref-3"><a href="#cite_note-3"><span class="cite_crochet">[<∕span>3<span class="cite
<∕th>
<th scope="col">Classement<br∕><a href="∕wiki∕Championnat_de_France_de_football_2018-2019" title="Championnat de France de football 2018-
<∕th>
<th scope="col">Entraîneur
<∕th>
<th scope="col">Depuis
<∕th>
<th scope="col">Stade
<∕th>
<th scope="col">Capacité<br∕>en L1<sup class="reference" id="cite_ref-4"><a href="#cite_note-4"><span class="cite_crochet">[<∕span>4<span
<∕th>
<th scope="col">Nombre<br∕>de saisons<br∕>en L1
<∕th><∕tr>
# par exemple la seconde ligne va correspondre à la ligne du premier club présent dans le tableau
print(rows[1])
<tr bgcolor="#97DEFF">
<td><a href="∕wiki∕Paris_Saint-Germain_Football_Club" title="Paris Saint-Germain Football Club">Paris Saint-Germain<∕a>
<∕td>
<td>1974
<∕td>
<td>637
<∕td>
<td><span data-sort-value="101 !"><∕span><abbr class="abbr" title="Premier">1<sup>er<∕sup><∕abbr>
<∕td>
<td align="left"><span class="flagicon"><a class="image" href="∕wiki∕Fichier:Flag_of_Germany.svg" title="Drapeau : Allemagne"><img alt=""
<∕td>
<td>2018
<∕td>
<td><a href="∕wiki∕Parc_des_Princes" title="Parc des Princes">Parc des Princes<∕a>
<∕td>
6 sur 22
Webscraping avec python | Python pour les data scientists
<∕td>
<td>47 929
<∕td>
<td>46
<∕td><∕tr>
On va utiliser l’attribut text afin de se débarasser de toute la couche de HTML qu’on obtient à l’étape 2.
• on commence par prendre toutes les cellules de cette ligne, avec la balise td.
• on fait ensuite une boucle sur chacune des cellules et on ne garde que le texte de la cellule avec l’attribut text.
• enfin, on applique la méthode strip() pour que le texte soit bien mis en forme (sans espace inutile etc).
cols = rows[1].find_all('td')
print(cols[0])
print(cols[0].[Link]())
Paris Saint-Germain
1974
637
1er
Thomas Tuchel
2018
Parc des Princes
47 929
46
[]
['Paris Saint-Germain', '1974', '637', '1er', 'Thomas Tuchel', '2018', 'Parc des Princes', '47\xa0929', '46']
['LOSC Lille', '2000', '120', '2e', 'Christophe Galtier', '2017', 'Stade Pierre-Mauroy', '49\xa0712', '59']
['Olympique lyonnais', '1989', '310', '3e', 'Rudi Garcia', '2019', 'Groupama Stadium', '57\xa0206', '60']
['AS Saint-Étienne', '2004', '100', '4e', 'Claude Puel', '2019', 'Stade Geoffroy-Guichard', '41\xa0965', '66']
['Olympique de Marseille', '1996', '110', '5e', 'André Villas-Boas', '2019', 'Orange Vélodrome', '66\xa0226', '69']
['Montpellier HSC', '2009', '40', '6e', 'Michel Der Zakarian', '2017', 'Stade de la Mosson', '22\xa0000', '27']
['OGC Nice', '2002', '50', '7e', 'Patrick Vieira', '2018', 'Allianz Riviera', '35\xa0596', '60']
['Stade de Reims', '2018', '45', '8e', 'David Guion', '2017', 'Stade Auguste-Delaune', '20\xa0546', '35']
['Nîmes Olympique', '2018', '27', '9e', 'Bernard Blaquart', '2015', 'Stade des Costières', '15\xa0788', '35']
['Stade rennais FC', '1994', '65', '10e', 'Julien Stéphan', '2018', 'Roazhon Park', '29\xa0194', '62']
['RC Strasbourg Alsace', '2017', '43', '11e', 'Thierry Laurey', '2016', 'Stade de la Meinau', '26\xa0109', '58']
['FC Nantes', '2013', '70', '12e', 'Christian Gourcuff', '2019', 'Stade de la Beaujoire - Louis Fonteneau', '35\xa0322', '51']
['SCO d’Angers', '2015', '32', '13e', 'Stéphane Moulin', '2011', 'Stade Raymond-Kopa', '14\xa0582', '27']
['Girondins de Bordeaux', '1992', '70', '14e', 'Paulo Sousa', '2019', 'Matmut Atlantique', '42\xa0115', '66']
['Amiens SC', '2017', '30', '15e', 'Luka Elsner', '2019', 'Stade Crédit Agricole la Licorne', '12\xa0999', '2']
['Toulouse FC', '2003', '35', '16e', 'Denis Zanko', '2020', 'Stadium de Toulouse', '33\xa0033', '32']
['AS Monaco', '2013', '220', '17e', 'Robert Moreno', '2019', 'Stade Louis-II', '16\xa0500', '60']
['Dijon FCO', '2016', '38', '18e', 'Stéphane Jobard', '2019', 'Parc des Sports Gaston-Gérard', '15\xa0459', '4']
['FC Metz', '2019', '40', '1er (Ligue 2)', 'Vincent Hognon', '2019', 'Stade Saint-Symphorien', '25\xa0865', '61']
['Stade brestois 29', '2019', '30', '2e (Ligue 2)', "Olivier Dall'Oglio", '2019', 'Stade Francis-Le Blé', '14\xa0920', '13']
On a bien réussi à avoir les informations contenues dans le tableau des participants du championnat. Mais la première
ligne est étrange : c’est une liste vide …
Il s’agit des en-têtes : elles sont reconnues par la balise th et non td.
On va mettre tout le contenu dans un dictionnaire, pour le transformer ensuite en DataFrame pandas :
7 sur 22
Webscraping avec python | Python pour les data scientists
dico_participants = dict()
for row in rows:
cols = row.find_all('td')
cols = [[Link]() for ele in cols]
if len(cols) > 0 :
dico_participants[cols[0]] = cols[1:]
dico_participants
8 sur 22
Webscraping avec python | Python pour les data scientists
'9e',
'Bernard Blaquart',
'2015',
'Stade des Costières',
'15\xa0788',
'35'],
'Stade rennais FC': ['1994',
'65',
'10e',
'Julien Stéphan',
'2018',
'Roazhon Park',
'29\xa0194',
'62'],
'RC Strasbourg Alsace': ['2017',
'43',
'11e',
'Thierry Laurey',
'2016',
'Stade de la Meinau',
'26\xa0109',
'58'],
'FC Nantes': ['2013',
'70',
'12e',
'Christian Gourcuff',
'2019',
'Stade de la Beaujoire - Louis Fonteneau',
'35\xa0322',
'51'],
'SCO d’Angers': ['2015',
'32',
'13e',
'Stéphane Moulin',
'2011',
'Stade Raymond-Kopa',
'14\xa0582',
'27'],
'Girondins de Bordeaux': ['1992',
'70',
'14e',
'Paulo Sousa',
'2019',
'Matmut Atlantique',
'42\xa0115',
'66'],
'Amiens SC': ['2017',
'30',
'15e',
'Luka Elsner',
'2019',
'Stade Crédit Agricole la Licorne',
'12\xa0999',
'2'],
'Toulouse FC': ['2003',
'35',
'16e',
'Denis Zanko',
'2020',
'Stadium de Toulouse',
'33\xa0033',
'32'],
'AS Monaco': ['2013',
'220',
'17e',
'Robert Moreno',
'2019',
'Stade Louis-II',
'16\xa0500',
'60'],
'Dijon FCO': ['2016',
'38',
'18e',
'Stéphane Jobard',
'2019',
'Parc des Sports Gaston-Gérard',
9 sur 22
Webscraping avec python | Python pour les data scientists
'15\xa0459',
'4'],
'FC Metz': ['2019',
'40',
'1er (Ligue 2)',
'Vincent Hognon',
'2019',
'Stade Saint-Symphorien',
'25\xa0865',
'61'],
'Stade brestois 29': ['2019',
'30',
'2e (Ligue 2)',
"Olivier Dall'Oglio",
'2019',
'Stade Francis-Le Blé',
'14\xa0920',
'13']}
data_participants = [Link].from_dict(dico_participants,orient='index')
data_participants.head()
0 1 2 3 4 5 6 7
Paris Saint-Germain 1974 637 1er Thomas Tuchel 2018 Parc des Princes 47 929 46
LOSC Lille 2000 120 2e Christophe Galtier 2017 Stade Pierre-Mauroy 49 712 59
Olympique lyonnais 1989 310 3e Rudi Garcia 2019 Groupama Stadium 57 206 60
Olympique de Marseille 1996 110 5e André Villas-Boas 2019 Orange Vélodrome 66 226 69
[<th scope="col">Club
<∕th>, <th scope="col">Dernière<br∕>montée
<∕th>, <th scope="col">Budget<sup class="reference" id="cite_ref-3"><a href="#cite_note-3"><span class="cite_crochet">[<∕span>3<span clas
<∕th>, <th scope="col">Classement<br∕><a href="∕wiki∕Championnat_de_France_de_football_2018-2019" title="Championnat de France de footbal
<∕th>, <th scope="col">Entraîneur
<∕th>, <th scope="col">Depuis
<∕th>, <th scope="col">Stade
<∕th>, <th scope="col">Capacité<br∕>en L1<sup class="reference" id="cite_ref-4"><a href="#cite_note-4"><span class="cite_crochet">[<∕span
<∕th>, <th scope="col">Nombre<br∕>de saisons<br∕>en L1
<∕th>]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
columns_participants
['Club',
10 sur 22
Webscraping avec python | Python pour les data scientists
'Dernière Montée',
'Budget [ 3 ] En M €',
'Classement 2018-2019',
'Entraîneur',
'Depuis',
'Stade',
'Capacité En L1 [ 4 ]',
'Nombre De Saisons En L1']
� Finalisation du tableau
data_participants.columns = columns_participants[1:]
data_participants.head()
Olympique Groupama
1989 310 3e Rudi Garcia 2019 57 206 60
lyonnais Stadium
import urllib
import pandas as pd
import bs4
division=[]
equipe=[]
stade=[]
latitude_stade=[]
longitude_stade=[]
url_list=["http:∕∕[Link]∕wiki∕Championnat_de_France_de_football_2019-2020", "http:∕∕[Link]∕wiki∕Championnat_de_France
print(url_ligue)
sock = [Link](url_ligue).read()
page=[Link](sock)
# Rechercher les liens des équipes dans la liste disponible sur wikipedia
if url_ligue==url_list[0] :
[Link]("L1")
else :
[Link]("L2")
11 sur 22
Webscraping avec python | Python pour les data scientists
[Link]("L2")
if [Link]('a')!=None :
team_url=[Link]('a').get('href')
name_team=[Link]('a').get('title')
[Link](name_team)
url_get_info = "http:∕∕[Link]"+team_url
print(url_get_info)
search = [Link](url_get_info).read()
search_team=[Link](search)
# trouver le stade
compteur = 0
for stadium in search_team.findAll('tr'):
for x in [Link]('th' , {'scope' : 'row'} ) :
if [Link][0].string=="Stade" and compteur == 0:
compteur = 1
# trouver le lien du stade et son nom
url_stade=[Link]('a')[1].get('href')
name_stadium=[Link]('a')[1].get('title')
[Link](name_stadium)
url_get_stade = "http:∕∕[Link]"+url_stade
print(url_get_stade)
search_stade = [Link](url_get_stade).read()
soup_stade=[Link](search_stade)
kartographer = soup_stade.find('a',{'class': "mw-kartographer-maplink"})
if kartographer == None :
latitude_stade.append(None)
longitude_stade.append(None)
else :
for coordinates in kartographer :
print(coordinates)
liste = [Link](",")
latitude_stade.append(str(liste[0]).replace(" ", "") + "'")
longitude_stade.append(str(liste[1]).replace(" ", "") + "'")
dict = {'division' : division , 'equipe': equipe, 'stade': stade, 'latitude': latitude_stade, 'longitude' : longitude_stade}
data = [Link](dict)
data = [Link]()
http:∕∕[Link]∕wiki∕Championnat_de_France_de_football_2019-2020
http:∕∕[Link]∕wiki∕Paris_Saint-Germain_Football_Club
http:∕∕[Link]∕wiki∕Parc_des_Princes
http:∕∕[Link]∕wiki∕Stade_Pierre-Mauroy
50° 36′ 43″ N, 3° 07′ 50″ E
http:∕∕[Link]∕wiki∕Olympique_lyonnais
http:∕∕[Link]∕wiki∕Parc_Olympique_lyonnais
http:∕∕[Link]∕wiki∕Stade_Geoffroy-Guichard
http:∕∕[Link]∕wiki∕Orange_V%C3%A9lodrome
12 sur 22
http:∕∕[Link]∕wiki∕Montpellier_H%C3%A9rault_Sport_Club
http:∕∕[Link]∕wiki∕Stade_de_la_Mosson
http:∕∕[Link]∕wiki∕Stade_Auguste-Delaune
49° 14′ 48″ N, 4° 01′ 30″ E
http:∕∕[Link]∕wiki∕Olympique_Gymnaste_Club_Nice
http:∕∕[Link]∕wiki∕Allianz_Riviera
http:∕∕[Link]∕wiki∕Stade_des_Costi%C3%A8res
43° 48′ 58″ N, 4° 21′ 33″ E
http:∕∕[Link]∕wiki∕Racing_Club_de_Strasbourg_Alsace
http:∕∕[Link]∕wiki∕Stade_de_la_Meinau
http:∕∕[Link]∕wiki∕Roazhon_Park
http:∕∕[Link]∕wiki∕Stade_Raymond-Kopa
47° 27′ 38″ N, 0° 31′ 51″ O
http:∕∕[Link]∕wiki∕Football_Club_de_Metz
http:∕∕[Link]∕wiki∕Stade_Saint-Symphorien
49° 06′ 35″ N, 6° 09′ 33″ E
http:∕∕[Link]∕wiki∕Football_Club_de_Nantes
http:∕∕[Link]∕wiki∕Stade_de_la_Beaujoire
47° 15′ 20″ N, 1° 31′ 31″ O
http:∕∕[Link]∕wiki∕Stade_brestois_29
http:∕∕[Link]∕wiki∕Stade_Francis-Le_Bl%C3%A9
48° 24′ 11″ N, 4° 27′ 42″ O
http:∕∕[Link]∕wiki∕Football_Club_des_Girondins_de_Bordeaux
http:∕∕[Link]∕wiki∕Matmut_Atlantique
http:∕∕[Link]∕wiki∕Stadium_de_Toulouse
43° 35′ 00″ N, 1° 26′ 03″ E
http:∕∕[Link]∕wiki∕Amiens_Sporting_Club
http:∕∕[Link]∕wiki∕Stade_de_la_Licorne
49° 53′ 38″ N, 2° 15′ 49″ E
http:∕∕[Link]∕wiki∕Association_sportive_de_Monaco_football_club
http:∕∕[Link]∕wiki∕Stade_Louis-II
43° 43′ 39″ N, 7° 24′ 56″ E
http:∕∕[Link]∕wiki∕Dijon_Football_C%C3%B4te-d%27Or
http:∕∕[Link]∕wiki∕Stade_Gaston-G%C3%A9rard
47° 19′ 28″ N, 5° 04′ 06″ E
http:∕∕[Link]∕wiki∕Championnat_de_France_de_football_de_Ligue_2_2019-2020
http:∕∕[Link]∕wiki∕Athletic_Club_ajaccien
http:∕∕[Link]∕wiki∕Stade_Fran%C3%A7ois_Coty
41° 55′ 51″ N, 8° 46′ 35″ E
http:∕∕[Link]∕wiki∕Association_sportive_Nancy-Lorraine
http:∕∕[Link]∕wiki∕Stade_Marcel-Picot
48° 41′ 43″ N, 6° 12′ 38″ E
13 sur 22
Webscraping avec python | Python pour les data scientists
http:∕∕[Link]∕wiki∕Le_Havre_Athletic_Club_(football)
http:∕∕[Link]∕wiki∕Stade_Oc%C3%A9ane
http:∕∕[Link]∕wiki∕Stade_Michel-d%27Ornano
http:∕∕[Link]∕wiki∕Stade_Gabriel-Montpied
45° 48′ 57″ N, 3° 07′ 18″ E
http:∕∕[Link]∕wiki∕En_Avant_de_Guingamp
http:∕∕[Link]∕wiki∕Stade_de_Roudourou
48° 33′ 58″ N, 3° 09′ 52″ O
http:∕∕[Link]∕wiki∕Football_Club_Lorient
http:∕∕[Link]∕wiki∕Stade_du_Moustoir
http:∕∕[Link]∕wiki∕Stade_Charl%C3%A9ty
http:∕∕[Link]∕wiki∕Stade_Gaston-Petit
46° 48′ 07″ N, 1° 43′ 18″ E
http:∕∕[Link]∕wiki∕Association_de_la_jeunesse_auxerroise
http:∕∕[Link]∕wiki∕Stade_de_l%27Abb%C3%A9-Deschamps
47° 47′ 12″ N, 3° 35′ 19″ E
http:∕∕[Link]∕wiki∕Union_sportive_Orl%C3%A9ans_Loiret_football
http:∕∕[Link]∕wiki∕Stade_de_la_Source
47° 50′ 25″ N, 1° 56′ 28″ E
http:∕∕[Link]∕wiki∕Valenciennes_Football_Club
http:∕∕[Link]∕wiki∕Stade_du_Hainaut
50° 20′ 55″ N, 3° 31′ 56″ E
http:∕∕[Link]∕wiki∕Chamois_niortais_Football_Club
http:∕∕[Link]∕wiki∕Stade_Ren%C3%A9-Gaillard
46° 19′ 01″ N, 0° 29′ 21″ O
http:∕∕[Link]∕wiki∕Grenoble_Foot_38
http:∕∕[Link]∕wiki∕Stade_des_Alpes
http:∕∕[Link]∕wiki∕Stade_Auguste-Bonal
47° 30′ 44″ N, 6° 48′ 41″ E
http:∕∕[Link]∕wiki∕Rodez_Aveyron_Football
http:∕∕[Link]∕wiki∕Stade_Paul-Lignon
http:∕∕[Link]∕wiki∕Stade_Walter_Luzi
49° 10′ 45″ N, 2° 14′ 01″ E
http:∕∕[Link]∕wiki∕Esp%C3%A9rance_sportive_Troyes_Aube_Champagne
http:∕∕[Link]∕wiki∕Stade_de_l%27Aube
http:∕∕[Link]∕wiki∕Stade_Bollaert-Delelis
14 sur 22
Webscraping avec python | Python pour les data scientists
http:∕∕[Link]∕wiki∕Stade_Marie-Marvingt
47° 57′ 32″ N, 0° 13′ 29″ E
[Link](5)
0 L1 Paris Saint-Germain Football Club Parc des Princes 48° 50′ 29″ N' 2° 15′ 11″ E'
1 L1 LOSC Lille Stade Pierre-Mauroy 50° 36′ 43″ N' 3° 07′ 50″ E'
2 L1 Olympique lyonnais Parc Olympique lyonnais 45° 45′ 55″ N' 4° 58′ 55″ E'
3 L1 Association sportive de Saint-Étienne Stade Geoffroy-Guichard 45° 27′ 39″ N' 4° 23′ 25″ E'
4 L1 Olympique de Marseille Orange Vélodrome 43° 16′ 11″ N' 5° 23′ 45″ E'
On va transformer les coordonnées en degrés en coordonnées numériques afin d’être en mesure de faire une carte
import re
def parse_dms(dms):
parts = [Link]('[^\d\w]+', dms)
lat = dms2dd(parts[0], parts[1], parts[2], parts[3])
#lng = dms2dd(parts[4], parts[5], parts[6], parts[7])
return lat
data['latitude'] = data['latitude'].apply(parse_dms)
data['longitude'] = data['longitude'].apply(parse_dms)
Tous les éléments sont en place pour faire une belle carte à ce stade. On va utilisee folium pour celle-ci, qui est
présenté dans la partie visualisation.
gdf = [Link](
data, geometry=gpd.points_from_xy([Link], [Link]))
Path("leaflet").mkdir(parents=True, exist_ok=True)
m.fit_bounds([sw, ne])
Make this Notebook Trusted to load map: File -> Trust Notebook
+
−
15 sur 22
Webscraping avec python | Python pour les data scientists
Leaflet ([Link] | Map tiles by Stamen Design ([Link] under CC BY 3.0 ([Link] Data by ©
OpenStreetMap ([Link] under ODbL ([Link]
Pour cet exercice, nous vous demandons d’obtenir différentes informations sur les pokémons à partir du site
internet [Link].
�. les informations personnelles des 893 pokemons sur le site internet [Link]. Les informations que
nous aimerions obtenir au final dans un DataFrame sont celles contenues dans 4 tableaux :
• Pokédex data
• Training
• Breeding
• Base stats
�. Nous aimerions que vous récupériez également les images de chacun des pokémons et que vous les
enregistriez dans un dossier
Pour la question 1, l’objectif est d’obtenir le code source d’un tableau comme celui qui suit (Pokemon Nincada.)
Pokédex data
National № 290
1. Compound Eyes
Abilities
Run Away (hidden ability)
042 (Ruby/Sapphire/Emerald)
111 (X/Y --- Central Kalos)
Local №
043 (Omega Ruby/Alpha Sapphire)
104 (Sword/Shield)
Training
EV yield 1 Defense
16 sur 22
Webscraping avec python | Python pour les data scientists
Base Exp. 53
Breeding
Egg Groups Bug
Base stats
HP 31 172 266
Attack 45 85 207
Speed 40 76 196
Pour cet exercice, nous vous demandons d’obtenir différentes informations sur les pokémons à partir du site
internet [Link].
Etape 1
Nous souhaitons tout d’abord obtenir les informations personnelles de tous les pokemons sur [Link].
Les informations que nous aimerions obtenir au final pour les pokemons sont celles contenues dans 4 tableaux :
• Pokédex data
• Training
• Breeding
• Base stats
�. Trouvez la page principale du site et la transformez en un objet intelligible pour votre code. Les fonctions
suivantes vous seront utiles :
• [Link]
• [Link]
• [Link]
Pour ce site, par rapport à tout à l’heure, il faudra ajouter un paramètre supplémentaire à la fonction Request:
�. Créez une fonction qui permet de récupérer la page d’un pokémon à partir de son nom.
�. A partir de la page de bulbasaur, obtenez les 4 tableaux qui nous intéressent :
17 sur 22
Webscraping avec python | Python pour les data scientists
Vous devriez obtenir une liste de caractéristiques proche de celle-ci (la structure est ici en dictionnaire, ce qui est
pratique)
defaultdict(None,
{'National №': '001',
'name': 'bulbasaur',
'Type': ' Grass Poison ',
'Species': 'Seed Pokémon',
'Height': '0.7\xa0m (2′04″)',
'Weight': '6.9\xa0kg (15.2\xa0lbs)',
'Abilities': '1. OvergrowChlorophyll (hidden ability)',
'Local №': "001 (Red∕Blue∕Yellow)226 (Gold∕Silver∕Crystal)001 (FireRed∕LeafGreen)231 (HeartGold∕SoulSilver)080 (X∕Y — Cen
'EV yield': ' 1 Special Attack ',
'Catch rate': ' 45 (5.9% with PokéBall, full HP) ',
'Base Friendship': ' 50 (normal) ',
'Base Exp.': '64',
'Growth Rate': 'Medium Slow',
'Egg Groups': 'Grass, Monster',
'Gender': '87.5% male, 12.5% female',
'Egg cycles': '20 (4,884–5,140 steps) ',
'HP': '45',
'Attack': '49',
'Defense': '49',
'Sp. Atk': '65',
'Sp. Def': '65',
'Speed': '45'})
�. Récupérez par ailleurs la liste de noms des pokémons qui nous permettra de faire une boucle par la suite.
Combien trouvez-vous de pokémons ?
�. Ecrire une fonction qui récupère l’ensemble des informations sur les dix premiers pokémons de la liste et les
intègre dans un [Link]
001
45 (5.9%
1. (Red/Blue 1
Grass Seed 0.7 m 6.9 kg with Medium
0 001 bulbasaur OvergrowChlorophyll /Yellow)226 Special ...
Poison Pokémon (2′04″) (15.2 lbs) PokéBall, Slow
(hidden ability) (Gold/Silver Attack
full HP)
/Crystal)...
1
002
Special 45 (5.9%
1. (Red/Blue
Grass Seed 1.0 m 13.0 kg Attack, with Medium
1 002 ivysaur OvergrowChlorophyll /Yellow)227 ...
Poison Pokémon (3′03″) (28.7 lbs) 1 PokéBall, Slow
(hidden ability) (Gold/Silver
Special full HP)
/Crystal)...
Defense
2
003
Special 45 (5.9%
1. (Red/Blue
Grass Seed 2.0 m 100.0 kg Attack, with Medium
2 003 venusaur OvergrowChlorophyll /Yellow)228 ...
Poison Pokémon (6′07″) (220.5 lbs) 1 PokéBall, Slow
(hidden ability) (Gold/Silver
Special full HP)
/Crystal)...
Defense
004
45 (5.9%
(Red/Blue
Lizard 0.6 m 8.5 kg 1. BlazeSolar Power with Medium
3 004 charmander Fire /Yellow)229 1 Speed ...
Pokémon (2′00″) (18.7 lbs) (hidden ability) PokéBall, Slow
(Gold/Silver
full HP)
/Crystal)...
005
1 45 (5.9%
(Red/Blue
Flame 1.1 m 19.0 kg 1. BlazeSolar Power Special with Medium
4 005 charmeleon Fire /Yellow)230 ...
Pokémon (3′07″) (41.9 lbs) (hidden ability) Attack, PokéBall, Slow
(Gold/Silver
1 Speed full HP)
/Crystal)...
5 rows × 22 columns
Etape 2
Nous aimerions que vous récupériez également les images des 5 premiers pokémons et que vous les enregistriez
dans un dossier
� Pour cette question, il faut que vous cherchiez de vous même certains éléments, tout n’est pas présent dans le
18 sur 22
Webscraping avec python | Python pour les data scientists
�
�Pour cette question, il faut que vous cherchiez de vous même certains éléments, tout n’est pas présent dans le
TD.
Scraper la liste des ministres français depuis wikipedia. Faire une graphique qui représente la distribution de leur
âge.
Si vous avez une solution satisfaisante, n’hésitez pas à la soumettre sur (car je n’ai pas encore testé…)
Nous allons voir à présent comment nous en sortir pour remplir des champs sur un site web et récupérer ce qui nous
intéresse. La réaction d’un site web à l’action d’un utilisateur passe régulièrement par l’usage de JavaScript dans le
monde du développement web. Le package Selenium permet de reproduire, depuis un code automatisé, le
comportement manuel d’un utilisateur. Il permet ainsi d’obtenir des informations du site qui ne sont pas dans le code
HTML mais qui apparaissent uniquement à la suite de l’exécution de script JavaScript en arrière plan.
Selenium se comporte comme un utilisateur lambda sur internet : il clique sur des liens, il remplit des formulaires, etc.
L’installation de Selenium nécessite d’avoir Chromium qui est un navigateur Google Chrome minimaliste. La version de
chromedriver doit être >= 2.36 et dépend de la version de Chrome que vous avez sur votre environnement de travail.
Pour installer cette version minimaliste de Chrome sur un environnement Linux, vous pouvez vous référer à l’encadré
dédié
Note
L’installation nécessite de passer par le terminal. D’abord, il convient d’installer les dépendances. Sur Colab, vous
pouvez utiliser les commandes suivantes:
Si vous êtes sur le SSP-Cloud, vous pouvez exécuter les commandes ci-dessous dans un terminal, en retirant les !
en début de ligne.
19 sur 22
Webscraping avec python | Python pour les data scientists
en début de ligne.
Vous pouvez ensuite installer Selenium. Par exemple, depuis une cellule de Notebook:
import sys
[Link](0,'∕usr∕lib∕chromium-browser∕chromedriver')
import selenium
path_to_web_driver = "chromedriver"
En premier lieu, il convient d’initialiser le comportement de Selenium en répliquant les paramètres du navigateur:
import time
chrome_options = [Link]()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
#chrome_options.add_argument('--verbose')
#chrome_options.add_argument('--disable-dev-shm-usage')
browser = [Link](executable_path=path_to_web_driver,
options=chrome_options)
On va sur le site de Bing Actualités, et on lui indique le mot clé que nous souhaitons chercher.
[Link]('https:∕∕[Link]∕news')
# on envoie à cet endroit le mot qu'on aurait tapé dans la barre de recherche
search.send_keys("Trump")
search_button.click()
png = browser.get_screenshot_as_png()
results = []
for link in links:
try:
url = link.get_attribute('href')
except StaleElementReferenceException as e:
print("Issue with '{0}' and '{1}'".format(url, link))
print("It might be due to slow javascript which produces the HTML page.")
[Link](url)
len(results)
20 sur 22
Webscraping avec python | Python pour les data scientists
# on a une pause de 10 secondes pour aller voir ce qui se passe sur la page internet
# on demande de quitter le navigateur quand tout est fini
[Link]()
print(results)
Note : ce bout de code ne donne pas une solution à 2048, il permet juste de voir ce qu’on peut faire avec Selenium
browser = [Link](executable_path=path_to_web_driver,
options=chrome_options)
[Link]('https:∕∕[Link]∕∕')
# Ce qu'on va faire : une boucle qui répète inlassablement la même chose : haut ∕ droite ∕ bas ∕ gauche
# on commence par cliquer sur la page pour que les touches sachent
browser.find_element("class name", 'grid-container').click()
grid = browser.find_element("tag name", 'body')
while True:
try: # on vérifie que le bouton "Try again" n'est pas là - sinon ça veut dire que le jeu est fini
retryButton = browser.find_element("link text",'Try again')
scoreElem = browser.find_element("class name", 'score-container')
break
except:
#Do nothing. Game is not over yet
pass
# on continue le jeu - on appuie sur la touche suivante pour le coup d'après
count += 1
grid.send_keys(direction[count % 4])
[Link](0.1)
21 sur 22