Sous ce nom se cache 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... Bref, il s'agit de récupérer des informations depuis Internet.
Le webscraping désigne les techniques d'extraction du contenu des sites internet. Via un programme informatique : nous allons aujourd'hui vous présenter comme créer et exécuter ces robots afin de recupérer rapidement des informations utiles à vos projets actuels ou futurs.
from jyquickhelper import add_notebook_menu
add_notebook_menu()
Même si nous n'allons pas aujourd'hui faire un cours de web, il vous faut néanmoins certaines bases pour comprendre comment un site internet fonctionne et comment sont structurées les informations sur une page.
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.
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>
).
Le code HTML du tableau suivant
Donnera dans le navigateur
Prénom | Mike | Mister |
---|---|---|
Nom | Stuntman | Pink |
Profession | Cascadeur | Gangster |
Dans le cadre du langage HTML, les termes de parents (parent) et enfants (child) servent à désigner des élements emboîtés les uns dans les autres. Dans la construction suivante, par exemple :
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>
.
Mais pourquoi apprendre ça pour scraper me direz-vous ?
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 scrapping sont principalement construites pour vous permettre de naviguer entre les balises.
Quand le bout de code html est écrit, il apaprait sous la forme d'un texte noir sur un fond blanc. Une manière simple de rendre la page plus belle, c'est d'y ajouter de la couleur.
La feuille de style qui permet de rendre la page plus belle correspond au(x) fichier(s) CSS. Toutes les pages HTML qui font référence à cette feuille de style externe hériteront de toutes ses définitions. Nous y reviendrons plus en détail dans le TD sur Flask (module Python de création de site internet).
Nous allons essentiellement utiliser le package BeautifulSoup4 pour ce cours, mais d'autres packages existent (Selenium, Scrapy...).
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 tutorial Scrapy Tutorial.
Les packages pour scrapper des pages HTML :
pip install bs4
)import urllib
import bs4
#help(bs4)
On va commencer facilement, prenons une page wikipedia, par exemple celle de la Ligue 1 de football : Championnat de France de football 2016-2017. On va souhaiter récupérer la liste des équipes, ainsi que les url des pages Wikipedia de ces équipes.
# Etape 1 : se connecter à la page wikipedia et obtenir le code source
url_ligue_1 = "https://fr.wikipedia.org/wiki/Championnat_de_France_de_football_2016-2017"
from urllib import request
request_text = request.urlopen(url_ligue_1).read()
print(request_text[:1000])
b'<!DOCTYPE html>\n<html class="client-nojs" lang="fr" dir="ltr">\n<head>\n<meta charset="UTF-8"/>\n<title>Championnat de France de football 2016-2017 \xe2\x80\x94 Wikip\xc3\xa9dia</title>\n<script>document.documentElement.className="client-js";RLCONF={"wgBreakFrames":false,"wgSeparatorTransformTable":[",\\t.","\xc2\xa0\\t,"],"wgDigitTransformTable":["",""],"wgDefaultDateFormat":"dmy","wgMonthNames":["","janvier","f\xc3\xa9vrier","mars","avril","mai","juin","juillet","ao\xc3\xbbt","septembre","octobre","novembre","d\xc3\xa9cembre"],"wgRequestId":"ea8aa125-b829-4dfe-a828-4222cfa58da5","wgCSPNonce":false,"wgCanonicalNamespace":"","wgCanonicalSpecialPageName":false,"wgNamespaceNumber":0,"wgPageName":"Championnat_de_France_de_football_2016-2017","wgTitle":"Championnat de France de football 2016-2017","wgCurRevisionId":196876773,"wgRevisionId":196876773,"wgArticleId":9734718,"wgIsArticle":true,"wgIsRedirect":false,"wgAction":"view","wgUserName":null,"wgUserGroups":["*"],"wgCategories":["Page utilisant une frise chronologique","Article ut'
# Etape 2 : utiliser le package BeautifulSoup
# qui "comprend" les balises contenues dans la chaine de caractères renvoyée par la fonction request
page = bs4.BeautifulSoup(request_text, "lxml")
#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ésenter chercher des élements à l'intérieur de ces balises.
par exemple, si on veut connaire le titre de la page, on utilise la méthode .find et on lui demande "title"
print(page.find("title"))
<title>Championnat de France de football 2016-2017 — Wikipédia</title>
La methode .find
ne renvoie que la première occurence de l'élément
print(page.find("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="Soixante-dix-neuvième (septante-neuvième)">79<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="Drapeau de la France"><img alt="Drapeau de la France" class="noviewer thumbborder" data-file-height="600" data-file-width="900" decoding="async" height="13" src="//upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/20px-Flag_of_France.svg.png" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/30px-Flag_of_France.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/40px-Flag_of_France.svg.png 2x" width="20"/></a> </span><a href="/wiki/France" title="France">France</a></span> et <span class="datasortkey" data-sort-value="Monaco"><span class="flagicon"><a class="image" href="/wiki/Fichier:Flag_of_Monaco.svg" title="Drapeau de Monaco"><img alt="Drapeau de Monaco" class="noviewer thumbborder" data-file-height="800" data-file-width="1000" decoding="async" height="16" src="//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Flag_of_Monaco.svg/20px-Flag_of_Monaco.svg.png" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Flag_of_Monaco.svg/30px-Flag_of_Monaco.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Flag_of_Monaco.svg/40px-Flag_of_Monaco.svg.png 2x" width="20"/></a> </span><a href="/wiki/Monaco" title="Monaco">Monaco</a></span></td> </tr> <tr> <th scope="row" style="width:10.5em;">Date</th> <td> du <time class="nowrap date-lien" data-sort-value="2016-08-12" datetime="2016-08-12"><a href="/wiki/12_ao%C3%BBt" title="12 août">12</a> <a href="/wiki/Ao%C3%BBt_2016" title="Août 2016">août</a> <a href="/wiki/2016" title="2016">2016</a></time><br/>au <time class="nowrap date-lien" data-sort-value="2017-05-20" datetime="2017-05-20"><a href="/wiki/20_mai" title="20 mai">20</a> <a href="/wiki/Mai_2017" title="Mai 2017">mai</a> <a href="/wiki/2017" title="2017">2017</a></time></td> </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> 380</td> </tr> <tr> <th scope="row" style="width:10.5em;">Affluence</th> <td> 7965830 <small>(20963 par match)</small></td> </tr> <tr> <th scope="row" style="width:10.5em;">Site web officiel</th> <td> <a class="external text" href="http://www.lfp.fr" rel="nofollow">Site officiel</a></td> </tr></tbody></table>
Pour trouver toutes les occurences, on utilise .findAll()
.
print("Il y a", len(page.findAll("table")), "éléments dans la page qui sont des <table>")
Il y a 32 éléments dans la page qui sont des <table>
print(" Le 2eme tableau de la page : Hiérarchie \n", page.findAll("table")[1])
print("--------------------------------------------------------")
print("Le 3eme tableau de la page : Palmarès \n",page.findAll("table")[2])
Le 2eme tableau de la page : Hiérarchie <table><caption style="background-color:#99cc99;color:#000000;">Hiérarchie</caption><tbody><tr> <th scope="row" style="width:10.5em;">Hiérarchie</th> <td> <abbr class="abbr" title="Premier">1<sup>er</sup></abbr> échelon</td> </tr> <tr> <th scope="row" style="width:10.5em;">Niveau inférieur</th> <td> <a class="mw-redirect" href="/wiki/Championnat_de_France_de_football_de_Ligue_2_2016-2017" title="Championnat de France de football de Ligue 2 2016-2017">Ligue 2 2016-2017</a></td> </tr></tbody></table> -------------------------------------------------------- Le 3eme tableau de la page : Palmarès <table><caption style="background-color:#99cc99;color:#000000;">Palmarès</caption> <tbody><tr> <th scope="row" style="width:10.5em;">Tenant du titre</th> <td> <a href="/wiki/Paris_Saint-Germain_Football_Club" title="Paris Saint-Germain Football Club">Paris Saint-Germain</a> (6)</td> </tr> <tr> <th scope="row" style="width:10.5em;">Promu(s) en début de saison</th> <td> <a href="/wiki/Association_sportive_Nancy-Lorraine" title="Association sportive Nancy-Lorraine">AS Nancy-Lorraine</a><br/><a href="/wiki/Dijon_Football_C%C3%B4te-d%27Or" title="Dijon Football Côte-d'Or">Dijon FCO</a><br/><a href="/wiki/Football_Club_de_Metz" title="Football Club de Metz">FC Metz</a></td> </tr> <tr> <th scope="row" style="width:10.5em;">Vainqueur</th> <td> <b><a href="/wiki/Association_sportive_de_Monaco_Football_Club" title="Association sportive de Monaco Football Club">AS Monaco</a></b> (8)</td> </tr> <tr> <th scope="row" style="width:10.5em;">Deuxième</th> <td> <a href="/wiki/Paris_Saint-Germain_Football_Club" title="Paris Saint-Germain Football Club">Paris Saint-Germain</a></td> </tr> <tr> <th scope="row" style="width:10.5em;">Troisième</th> <td> <a class="mw-redirect" href="/wiki/Olympique_Gymnaste_Club_Nice_C%C3%B4te_d%27Azur" title="Olympique Gymnaste Club Nice Côte d'Azur">OGC Nice</a></td> </tr> <tr> <th scope="row" style="width:10.5em;">Relégué(s)</th> <td> <a class="mw-redirect" href="/wiki/AS_Nancy-Lorraine" title="AS Nancy-Lorraine">AS Nancy-Lorraine</a><br/><a class="mw-redirect" href="/wiki/SC_Bastia" title="SC Bastia">SC Bastia</a><br/><a class="mw-redirect" href="/wiki/FC_Lorient" title="FC Lorient">FC Lorient</a></td> </tr> <tr> <th scope="row" style="width:10.5em;">Buts</th> <td> 991 <small>(2,61 par match)</small></td> </tr> <tr> <th scope="row" style="width:10.5em;"><img alt="Averti" data-file-height="666" data-file-width="512" decoding="async" height="13" src="//upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Yellow_card.svg/10px-Yellow_card.svg.png" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Yellow_card.svg/15px-Yellow_card.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Yellow_card.svg/20px-Yellow_card.svg.png 2x" title="Averti" width="10"/> Cartons jaunes</th> <td> 1297</td> </tr> <tr> <th scope="row" style="width:10.5em;"><a class="image" href="/wiki/Fichier:Red_card.svg"><img alt="Red card.svg" data-file-height="260" data-file-width="200" decoding="async" height="13" src="//upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Red_card.svg/10px-Red_card.svg.png" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Red_card.svg/15px-Red_card.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Red_card.svg/20px-Red_card.svg.png 2x" width="10"/></a> Cartons rouges</th> <td> 96</td> </tr> <tr> <th scope="row" style="width:10.5em;">Meilleur joueur</th> <td> <span class="flagicon"><a class="image" href="/wiki/Fichier:Flag_of_Uruguay.svg" title="Drapeau : Uruguay"><img alt="" class="noviewer thumbborder" data-file-height="630" data-file-width="945" decoding="async" height="13" src="//upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Uruguay.svg/20px-Flag_of_Uruguay.svg.png" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Uruguay.svg/30px-Flag_of_Uruguay.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Uruguay.svg/40px-Flag_of_Uruguay.svg.png 2x" width="20"/></a></span> <a href="/wiki/Edinson_Cavani" title="Edinson Cavani">Edinson Cavani</a></td> </tr> <tr> <th scope="row" style="width:10.5em;">Meilleur(s) buteur(s)</th> <td> <span class="flagicon"><a class="image" href="/wiki/Fichier:Flag_of_Uruguay.svg" title="Drapeau : Uruguay"><img alt="" class="noviewer thumbborder" data-file-height="630" data-file-width="945" decoding="async" height="13" src="//upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Uruguay.svg/20px-Flag_of_Uruguay.svg.png" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Uruguay.svg/30px-Flag_of_Uruguay.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Flag_of_Uruguay.svg/40px-Flag_of_Uruguay.svg.png 2x" width="20"/></a></span> <a href="/wiki/Edinson_Cavani" title="Edinson Cavani">Edinson Cavani</a> (35)</td> </tr> <tr> <th scope="row" style="width:10.5em;">Meilleur(s) passeur(s)</th> <td> <span class="flagicon"><a class="image" href="/wiki/Fichier:Flag_of_France.svg" title="Drapeau : France"><img alt="" class="noviewer thumbborder" data-file-height="600" data-file-width="900" decoding="async" height="13" src="//upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/20px-Flag_of_France.svg.png" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/30px-Flag_of_France.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/40px-Flag_of_France.svg.png 2x" width="20"/></a></span> <a href="/wiki/Morgan_Sanson" title="Morgan Sanson">Morgan Sanson</a> (12)</td> </tr> <tr> <th scope="row" style="width:10.5em;">Barragiste(s)</th> <td> <a class="mw-redirect" href="/wiki/FC_Lorient" title="FC Lorient">FC Lorient</a></td> </tr></tbody></table>
La liste des équipes est dans le tableau "Participants"
: dans le code source, on voit que ce tableau est celui qui a class="DebutCarte"
. On voit également que les balises qui encerclent les noms et les urls des clubs sont de la forme suivante
<a href="url_club" title="nom_club"> Nom du club </a>
for item in page.find('table', {'class' : 'DebutCarte'}).findAll({'a'})[0:5] :
print(item, "\n-------")
<a class="image" href="/wiki/Fichier:France_location_map-Regions-2016.svg"><img alt="France location map-Regions-2016.svg" data-file-height="1922" data-file-width="2000" decoding="async" height="288" src="//upload.wikimedia.org/wikipedia/commons/thumb/b/b1/France_location_map-Regions-2016.svg/300px-France_location_map-Regions-2016.svg.png" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/b/b1/France_location_map-Regions-2016.svg/450px-France_location_map-Regions-2016.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/b/b1/France_location_map-Regions-2016.svg/600px-France_location_map-Regions-2016.svg.png 2x" width="300"/></a> ------- <a href="/wiki/Paris_Saint-Germain_Football_Club" title="Paris Saint-Germain Football Club">Paris SG</a> ------- <a href="/wiki/Association_sportive_de_Monaco_Football_Club" title="Association sportive de Monaco Football Club">AS Monaco</a> ------- <a href="/wiki/Olympique_lyonnais" title="Olympique lyonnais">Olympique lyonnais</a> ------- <a href="/wiki/Stade_rennais_Football_Club" title="Stade rennais Football Club">Stade rennais FC</a> -------
On n'a pas envie de prendre le premier élément qui ne correspond pas à un club mais à une image.
Or cet élément est le seul qui n'ait pas de title=""
. Il est conseillé d'exclure les élements qui ne nous intéressent pas en indiquant les éléments que la ligne doit avoir au lieu de les exclure en fonction de leur place dans la liste.
### condition sur la place dans la liste >>>> MAUVAIS
for e, item in enumerate(page.find('table', {'class' : 'DebutCarte'}).findAll({'a'})[0:5]) :
if e == 0:
pass
else :
print(item)
<a href="/wiki/Paris_Saint-Germain_Football_Club" title="Paris Saint-Germain Football Club">Paris SG</a> <a href="/wiki/Association_sportive_de_Monaco_Football_Club" title="Association sportive de Monaco Football Club">AS Monaco</a> <a href="/wiki/Olympique_lyonnais" title="Olympique lyonnais">Olympique lyonnais</a> <a href="/wiki/Stade_rennais_Football_Club" title="Stade rennais Football Club">Stade rennais FC</a>
#### condition sur les éléments que doit avoir la ligne >>>> BIEN
for item in page.find('table', {'class' : 'DebutCarte'}).findAll({'a'})[0:5] :
if item.get("title") :
print(item)
<a href="/wiki/Paris_Saint-Germain_Football_Club" title="Paris Saint-Germain Football Club">Paris SG</a> <a href="/wiki/Association_sportive_de_Monaco_Football_Club" title="Association sportive de Monaco Football Club">AS Monaco</a> <a href="/wiki/Olympique_lyonnais" title="Olympique lyonnais">Olympique lyonnais</a> <a href="/wiki/Stade_rennais_Football_Club" title="Stade rennais Football Club">Stade rennais FC</a>
Enfin la dernière étape, consiste à obtenir les informations souhaitées, c'est à dire dans notre cas, le nom et l'url des 20 clubs. Pour cela, nous allons utiliser deux méthodes de l'élement item :
getText()
qui permet d'obtenir le texte qui est sur la page web et dans la balise <a>
get('xxxx')
qui permet d'obtenir l'élément qui est égal à xxxx
Dans notre cas, nous allons vouloir le nom du club ainsi que l'url : on va donc utiliser __getText__
et __get("href")__
.
for item in page.find('table', {'class' : 'DebutCarte'}).findAll({'a'})[0:5] :
if item.get("title") :
print(item.get("href"))
print(item.getText())
/wiki/Paris_Saint-Germain_Football_Club Paris SG /wiki/Association_sportive_de_Monaco_Football_Club AS Monaco /wiki/Olympique_lyonnais Olympique lyonnais /wiki/Stade_rennais_Football_Club Stade rennais FC
# pour avoir le nom officiel, on aurait utiliser l'élément <title>
for item in page.find('table', {'class' : 'DebutCarte'}).findAll({'a'})[0:5] :
if item.get("title") :
print(item.get("title"))
Paris Saint-Germain Football Club Association sportive de Monaco Football Club Olympique lyonnais Stade rennais Football Club
Toutes ces informations, on souhaite les conserver dans un tableau Excel pour pouvoir les réuitiliser à l'envie : pour cela, rien de plus simple, on va passer par pandas, parce qu'on le maitrise parfaitement à ce stade de la formation.
import pandas
liste_noms = []
liste_urls = []
for item in page.find('table', {'class' : 'DebutCarte'}).findAll({'a'}) :
if item.get("title") :
liste_urls.append(item.get("href"))
liste_noms.append(item.getText())
df = pandas.DataFrame.from_dict( {"clubs" : liste_noms, 'url' : liste_urls})
df.head()
clubs | url | |
---|---|---|
0 | Paris SG | /wiki/Paris_Saint-Germain_Football_Club |
1 | AS Monaco | /wiki/Association_sportive_de_Monaco_Football_... |
2 | Olympique lyonnais | /wiki/Olympique_lyonnais |
3 | Stade rennais FC | /wiki/Stade_rennais_Football_Club |
4 | OGC Nice | /wiki/Olympique_gymnaste_club_Nice_C%C3%B4te_d... |
Pour cet exercice, nous vous demandons d'obtenir 1) les informations personnelles des 721 pokemons sur le site internet pokemondb.net. Les informations que nous aimerions obtenir au final pour les pokemons sont celles contenues dans 4 tableaux :
Pour exemple : Pokemon Database.
pour cette question ci, il faut que vous cherchiez de vous même certains éléments, tout n'est pas présent dans le TD.
L'avantage du package Selenium est 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 qui surfe sur internet : il clique sur des liens, il remplit des formulaires etc. Dans cet exemple, nous allons essayer de aller sur le site de Bing Actualités et entrer dans la barre de recherche un sujet donné. La version de chromedriver doit être >= 2.36
.
# Si selenium n'est pas installé.
# !pip install selenium
import selenium #pip install selenium
# télécharger le chrome driver https://chromedriver.storage.googleapis.com/index.html?path=74.0.3729.6/
path_to_web_driver = "chromedriver"
import os, sys
from pyquickhelper.filehelper import download, unzip_files
version = "73.0.3683.68"
url = "https://chromedriver.storage.googleapis.com/%s/" % version
if "win" in sys.platform:
if not os.path.exists("chromedriver_win32.zip"):
d = download(url + "chromedriver_win32.zip")
if not os.path.exists("chromedriver.exe"):
unzip_files("chromedriver_win32.zip", where_to=".")
elif sys.platform.startswith("linux"):
if not os.path.exists("chromedriver_linux64.zip"):
d = download(url + "chromedriver_linux64.zip")
if not os.path.exists("chromedriver"):
unzip_files("chromedriver_linux64.zip", where_to=".")
elif sys.platform.startswith("darwin"):
if not os.path.exists("chromedriver_mac64.zip"):
d = download(url + "chromedriver_mac64.zip")
if not os.path.exists("chromedriver"):
unzip_files("chromedriver_mac64.zip", where_to=".")
On soumet la requête.
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--verbose')
browser = webdriver.Chrome(executable_path=path_to_web_driver,
options=chrome_options)
browser.get('https://www.bing.com/news')
# on cherche l'endroit où on peut remplir un formulaire
# en utilisant les outils du navigateur > inspecter les éléments de la page
# on voit que la barre de recherche est un élement du code appelé 'q' comme query
# on lui demande de chercher cet élément
search = browser.find_element_by_name('q')
print(search)
print([search.text, search.tag_name, search.id])
# on envoie à cet endroit le mot qu'on aurait tapé dans la barre de recherche
search.send_keys("alstom")
search_button = browser.find_element_by_xpath("//input[@id='sb_form_go']")
#search_button = browser.find_element_by_id('search_button_homepage')
search_button.click()
# on appuie sur le bouton "Entrée" Return en anglais
#search.send_keys(Keys.RETURN)
--------------------------------------------------------------------------- SessionNotCreatedException Traceback (most recent call last) Cell In [20], line 11 8 chrome_options.add_argument('--no-sandbox') 9 chrome_options.add_argument('--verbose') ---> 11 browser = webdriver.Chrome(executable_path=path_to_web_driver, 12 options=chrome_options) 14 browser.get('https://www.bing.com/news') 16 # on cherche l'endroit où on peut remplir un formulaire 17 # en utilisant les outils du navigateur > inspecter les éléments de la page 18 # on voit que la barre de recherche est un élement du code appelé 'q' comme query 19 # on lui demande de chercher cet élément File C:\Python3105_x64\lib\site-packages\selenium\webdriver\chrome\webdriver.py:69, in WebDriver.__init__(self, executable_path, port, options, service_args, desired_capabilities, service_log_path, chrome_options, service, keep_alive) 66 if not service: 67 service = Service(executable_path, port, service_args, service_log_path) ---> 69 super().__init__(DesiredCapabilities.CHROME['browserName'], "goog", 70 port, options, 71 service_args, desired_capabilities, 72 service_log_path, service, keep_alive) File C:\Python3105_x64\lib\site-packages\selenium\webdriver\chromium\webdriver.py:92, in ChromiumDriver.__init__(self, browser_name, vendor_prefix, port, options, service_args, desired_capabilities, service_log_path, service, keep_alive) 89 self.service.start() 91 try: ---> 92 super().__init__( 93 command_executor=ChromiumRemoteConnection( 94 remote_server_addr=self.service.service_url, 95 browser_name=browser_name, vendor_prefix=vendor_prefix, 96 keep_alive=keep_alive, ignore_proxy=_ignore_proxy), 97 options=options) 98 except Exception: 99 self.quit() File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\webdriver.py:272, in WebDriver.__init__(self, command_executor, desired_capabilities, browser_profile, proxy, keep_alive, file_detector, options) 270 self._authenticator_id = None 271 self.start_client() --> 272 self.start_session(capabilities, browser_profile) File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\webdriver.py:364, in WebDriver.start_session(self, capabilities, browser_profile) 362 w3c_caps = _make_w3c_caps(capabilities) 363 parameters = {"capabilities": w3c_caps} --> 364 response = self.execute(Command.NEW_SESSION, parameters) 365 if 'sessionId' not in response: 366 response = response['value'] File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\webdriver.py:429, in WebDriver.execute(self, driver_command, params) 427 response = self.command_executor.execute(driver_command, params) 428 if response: --> 429 self.error_handler.check_response(response) 430 response['value'] = self._unwrap_value( 431 response.get('value', None)) 432 return response File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\errorhandler.py:243, in ErrorHandler.check_response(self, response) 241 alert_text = value['alert'].get('text') 242 raise exception_class(message, screen, stacktrace, alert_text) # type: ignore[call-arg] # mypy is not smart enough here --> 243 raise exception_class(message, screen, stacktrace) SessionNotCreatedException: Message: session not created: Missing or invalid capabilities (Driver info: chromedriver=73.0.3683.68 (47787ec04b6e38e22703e856e101e840b65afe72),platform=Windows NT 10.0.19044 x86_64)
png = browser.get_screenshot_as_png()
from IPython.display import Image
Image(png, width='500')
On extrait les résultats.
from selenium.common.exceptions import StaleElementReferenceException
links = browser.find_elements_by_xpath("//div/a[@class='title'][@href]")
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.")
results.append(url)
len(results)
# 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
browser.quit()
print(results)
En réalité, l'exemple de Google News aurait pu se passer de Selenium et être utilisé directement avec BeautifulSoup et les url qu'on réussit à deviner de Google.
Ici, on utilise l'url de Google News pour créer une petite fonction qui donne pour chaque ensemble de (sujet, debut d'une période, fin d'une période) des liens pertinents issus de la recherche Google.
import time
from selenium import webdriver
def get_news_specific_dates (beg_date, end_date, subject, hl="fr",
gl="fr", tbm="nws", authuser="0") :
'''
Permet d obtenir pour une requete donnée et un intervalle temporel
précis les 10 premiers résultats
d articles de presse parus sur le sujet
'''
get_string = 'https://www.google.com/search?hl={}&gl={}&tbm={}&authuser={}&q={}&tbs=cdr%3A1%2Ccd_min%3A{}%2Ccd_max%3A{}&tbm={}'.format(
hl, gl, tbm, authuser, subject, beg_date, end_date,tbm)
print(get_string)
browser.get(get_string)
# La class peut changer si Google met à jour le style de sa page.
# Cela arrive régulièrement. Dans ce cas, il faut utiliser des
# outils de débuggage web (Chrome - Outils de développement)
# links = browser.find_elements_by_xpath("//h3[@class='r dO0Ag']/a[@href]")
links = browser.find_elements_by_xpath("//h3/a[@href]")
print(len(links))
results = []
for link in links:
url = link.get_attribute('href')
results.append(url)
browser.quit()
return results
On appelle la fonction créée à l'instant.
browser = webdriver.Chrome(executable_path=path_to_web_driver,
options=chrome_options)
articles = get_news_specific_dates("3/15/2018", "3/31/2018", "alstom", hl="fr")
--------------------------------------------------------------------------- SessionNotCreatedException Traceback (most recent call last) Cell In [22], line 1 ----> 1 browser = webdriver.Chrome(executable_path=path_to_web_driver, 2 options=chrome_options) 3 articles = get_news_specific_dates("3/15/2018", "3/31/2018", "alstom", hl="fr") File C:\Python3105_x64\lib\site-packages\selenium\webdriver\chrome\webdriver.py:69, in WebDriver.__init__(self, executable_path, port, options, service_args, desired_capabilities, service_log_path, chrome_options, service, keep_alive) 66 if not service: 67 service = Service(executable_path, port, service_args, service_log_path) ---> 69 super().__init__(DesiredCapabilities.CHROME['browserName'], "goog", 70 port, options, 71 service_args, desired_capabilities, 72 service_log_path, service, keep_alive) File C:\Python3105_x64\lib\site-packages\selenium\webdriver\chromium\webdriver.py:92, in ChromiumDriver.__init__(self, browser_name, vendor_prefix, port, options, service_args, desired_capabilities, service_log_path, service, keep_alive) 89 self.service.start() 91 try: ---> 92 super().__init__( 93 command_executor=ChromiumRemoteConnection( 94 remote_server_addr=self.service.service_url, 95 browser_name=browser_name, vendor_prefix=vendor_prefix, 96 keep_alive=keep_alive, ignore_proxy=_ignore_proxy), 97 options=options) 98 except Exception: 99 self.quit() File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\webdriver.py:272, in WebDriver.__init__(self, command_executor, desired_capabilities, browser_profile, proxy, keep_alive, file_detector, options) 270 self._authenticator_id = None 271 self.start_client() --> 272 self.start_session(capabilities, browser_profile) File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\webdriver.py:364, in WebDriver.start_session(self, capabilities, browser_profile) 362 w3c_caps = _make_w3c_caps(capabilities) 363 parameters = {"capabilities": w3c_caps} --> 364 response = self.execute(Command.NEW_SESSION, parameters) 365 if 'sessionId' not in response: 366 response = response['value'] File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\webdriver.py:429, in WebDriver.execute(self, driver_command, params) 427 response = self.command_executor.execute(driver_command, params) 428 if response: --> 429 self.error_handler.check_response(response) 430 response['value'] = self._unwrap_value( 431 response.get('value', None)) 432 return response File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\errorhandler.py:243, in ErrorHandler.check_response(self, response) 241 alert_text = value['alert'].get('text') 242 raise exception_class(message, screen, stacktrace, alert_text) # type: ignore[call-arg] # mypy is not smart enough here --> 243 raise exception_class(message, screen, stacktrace) SessionNotCreatedException: Message: session not created: Missing or invalid capabilities (Driver info: chromedriver=73.0.3683.68 (47787ec04b6e38e22703e856e101e840b65afe72),platform=Windows NT 10.0.19044 x86_64)
print(articles)
Dans cet exemple, on utilise le module pour que python appuie lui même sur les touches du clavier afin de jouer à 2048.
Note : ce bout de code ne donne pas une solution à 2048, il permet juste de voir ce qu'on peut faire avec selenium
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
# on ouvre la page internet du jeu 2048
browser = webdriver.Chrome(executable_path=path_to_web_driver,
options=chrome_options)
browser.get('https://gabrielecirulli.github.io/2048/')
# 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_by_class_name('grid-container').click()
grid = browser.find_element_by_tag_name('body')
# pour savoir quels coups faire à quel moment, on crée un dictionnaire
direction = {0: Keys.UP, 1: Keys.RIGHT, 2: Keys.DOWN, 3: Keys.LEFT}
count = 0
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_by_link_text('Try again')
scoreElem = browser.find_element_by_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])
time.sleep(0.1)
print('Score final : {} en {} coups'.format(scoreElem.text, count))
browser.quit()
--------------------------------------------------------------------------- SessionNotCreatedException Traceback (most recent call last) Cell In [24], line 6 2 from selenium.webdriver.common.keys import Keys 4 # on ouvre la page internet du jeu 2048 ----> 6 browser = webdriver.Chrome(executable_path=path_to_web_driver, 7 options=chrome_options) 8 browser.get('https://gabrielecirulli.github.io/2048/') 10 # Ce qu'on va faire : une boucle qui répète inlassablement la même chose : haut / droite / bas / gauche 11 12 # on commence par cliquer sur la page pour que les touches sachent File C:\Python3105_x64\lib\site-packages\selenium\webdriver\chrome\webdriver.py:69, in WebDriver.__init__(self, executable_path, port, options, service_args, desired_capabilities, service_log_path, chrome_options, service, keep_alive) 66 if not service: 67 service = Service(executable_path, port, service_args, service_log_path) ---> 69 super().__init__(DesiredCapabilities.CHROME['browserName'], "goog", 70 port, options, 71 service_args, desired_capabilities, 72 service_log_path, service, keep_alive) File C:\Python3105_x64\lib\site-packages\selenium\webdriver\chromium\webdriver.py:92, in ChromiumDriver.__init__(self, browser_name, vendor_prefix, port, options, service_args, desired_capabilities, service_log_path, service, keep_alive) 89 self.service.start() 91 try: ---> 92 super().__init__( 93 command_executor=ChromiumRemoteConnection( 94 remote_server_addr=self.service.service_url, 95 browser_name=browser_name, vendor_prefix=vendor_prefix, 96 keep_alive=keep_alive, ignore_proxy=_ignore_proxy), 97 options=options) 98 except Exception: 99 self.quit() File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\webdriver.py:272, in WebDriver.__init__(self, command_executor, desired_capabilities, browser_profile, proxy, keep_alive, file_detector, options) 270 self._authenticator_id = None 271 self.start_client() --> 272 self.start_session(capabilities, browser_profile) File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\webdriver.py:364, in WebDriver.start_session(self, capabilities, browser_profile) 362 w3c_caps = _make_w3c_caps(capabilities) 363 parameters = {"capabilities": w3c_caps} --> 364 response = self.execute(Command.NEW_SESSION, parameters) 365 if 'sessionId' not in response: 366 response = response['value'] File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\webdriver.py:429, in WebDriver.execute(self, driver_command, params) 427 response = self.command_executor.execute(driver_command, params) 428 if response: --> 429 self.error_handler.check_response(response) 430 response['value'] = self._unwrap_value( 431 response.get('value', None)) 432 return response File C:\Python3105_x64\lib\site-packages\selenium\webdriver\remote\errorhandler.py:243, in ErrorHandler.check_response(self, response) 241 alert_text = value['alert'].get('text') 242 raise exception_class(message, screen, stacktrace, alert_text) # type: ignore[call-arg] # mypy is not smart enough here --> 243 raise exception_class(message, screen, stacktrace) SessionNotCreatedException: Message: session not created: Missing or invalid capabilities (Driver info: chromedriver=73.0.3683.68 (47787ec04b6e38e22703e856e101e840b65afe72),platform=Windows NT 10.0.19044 x86_64)