``.
# ----------
#
# 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.
# ### Optionnel - CSS - le style de la page WEB
#
# 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](https://en.wikipedia.org/wiki/Cascading_Style_Sheets). 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](http://flask.pocoo.org/) (module Python de création de site internet).
# ## Scrapper avec python
#
# Nous allons essentiellement utiliser le package [BeautifulSoup4](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) pour ce cours, mais d'autres packages existent ([Selenium](https://selenium-python.readthedocs.io/), [Scrapy](https://scrapy.org/)...).
#
# [BeautifulSoup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) 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](https://fr.wikipedia.org/wiki/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](https://scrapy.org/), qui passe facilement d'une page à une autre ("crawl"). Scrapy est plus complexe à manipuler que [BeautifulSoup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) : si vous voulez plus de détails, rendez-vous sur la page du tutorial [Scrapy Tutorial](https://doc.scrapy.org/en/latest/intro/tutorial.html).
# ### Utiliser BeautifulSoup
#
# Les packages pour scrapper des pages HTML :
# - [BeautifulSoup4](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) (``pip install bs4``)
# - [urllib](https://docs.python.org/3/library/urllib.html#module-urllib)
# In[2]:
import urllib
import bs4
#help(bs4)
# #### 1ere page HTML
#
# On va commencer facilement, prenons une page wikipedia, par exemple celle de la Ligue 1 de football : [Championnat de France de football 2016-2017](https://fr.wikipedia.org/wiki/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.
# In[3]:
# 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])
# In[4]:
# 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"
# In[5]:
print(page.find("title"))
# La methode ``.find`` ne renvoie que la première occurence de l'élément
# In[6]:
print(page.find("table"))
# ---------------
# Pour trouver toutes les occurences, on utilise ``.findAll()``.
# In[7]:
print("Il y a", len(page.findAll("table")), "éléments dans la page qui sont des
")
# In[8]:
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])
# ### Exercice guidé : obtenir la liste des équipes de Ligue 1
#
# 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
# ```
# Nom du club
# ```
# In[9]:
for item in page.find('table', {'class' : 'DebutCarte'}).findAll({'a'})[0:5] :
print(item, "\n-------")
# 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.
# In[10]:
### 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)
# In[11]:
#### 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)
# 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 ````
# - ``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")__``.
# In[12]:
for item in page.find('table', {'class' : 'DebutCarte'}).findAll({'a'})[0:5] :
if item.get("title") :
print(item.get("href"))
print(item.getText())
# In[13]:
# pour avoir le nom officiel, on aurait utiliser l'élément
for item in page.find('table', {'class' : 'DebutCarte'}).findAll({'a'})[0:5] :
if item.get("title") :
print(item.get("title"))
# 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.
# In[14]:
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})
# In[15]:
df.head()
# ### Exercice de web scraping avec BeautifulSoup
#
# Pour cet exercice, nous vous demandons d'obtenir 1) les informations personnelles des 721 pokemons sur le site internet [pokemondb.net](http://pokemondb.net/pokedex/national). Les informations que nous aimerions obtenir au final pour les pokemons sont celles contenues dans 4 tableaux :
#
# - Pokédex data
# - Training
# - Breeding
# - Base stats
#
# Pour exemple : [Pokemon Database](http://pokemondb.net/pokedex/nincada).
#
# 2) Nous aimerions que vous récupériez également les images de chacun des pokémons et que vous les enregistriez dans un dossier (indice : utilisez les modules request et [shutil](https://docs.python.org/3/library/shutil.html))
# _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_.
# ### Aller sur internet avec Selenium
#
# L'avantage du package [Selenium](https://pypi.python.org/pypi/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](https://pypi.python.org/pypi/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](https://www.bing.com/news) et entrer dans la barre de recherche un sujet donné. La version de [chromedriver](https://sites.google.com/a/chromium.org/chromedriver/) doit être ``>= 2.36``.
# In[16]:
# Si selenium n'est pas installé.
# !pip install selenium
# In[17]:
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"
# In[18]:
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.
# In[19]:
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)
# In[20]:
png = browser.get_screenshot_as_png()
# In[21]:
from IPython.display import Image
Image(png, width='500')
# On extrait les résultats.
# In[22]:
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)
# In[23]:
# 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()
# In[24]:
print(results)
# ### Obtenir des informations entre deux dates sur Google News
# 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.
# In[25]:
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.
# In[26]:
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")
# In[27]:
print(articles)
# ### Utiliser selenium pour jouer à 2048
#
# 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
# In[28]:
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()
# In[29]: