Scraping Web avec requests, BeautifulSoup et Selenium
Introduction au Web Scraping
Le web scraping, ou "récolte de données web", est le processus d'extraction de données structurées à partir de sites web, généralement en utilisant des programmes informatiques. Plutôt que de copier manuellement les données, le scraping automatise la collecte, permettant de transformer le contenu non structuré des pages web en un format utilisable (CSV, JSON, base de données, etc.).
Pourquoi Scraper le Web ?
Le scraping est une compétence précieuse dans de nombreux domaines :
- Analyse de marché : Collecter des prix de produits concurrents, suivre les tendances.
- Recherche académique : Accumuler des corpus de texte pour l'analyse linguistique, des données pour des études sociales.
- Agrégation de contenu : Créer des flux d'actualités personnalisés, des comparateurs.
- Surveillance : Suivre les mentions de marque, les avis clients.
- Science des données : Obtenir des datasets pour l'apprentissage automatique.
Éthique et Légalité du Scraping
Avant de commencer, il est crucial de comprendre que le scraping n'est pas toujours autorisé et doit être pratiqué avec prudence.
robots.txt: Ce fichier (accessible viavotresite.com/robots.txt) indique aux "robots" (comme les scrappers) les parties du site qu'ils ne devraient pas explorer. Respectez-le toujours.- Termes de service : La plupart des sites web ont des conditions d'utilisation qui peuvent interdire explicitement le scraping. Ignorer ces termes peut entraîner des actions légales.
- Charge serveur : Faites des requêtes à une cadence raisonnable. Des requêtes trop fréquentes peuvent surcharger le serveur du site, entraînant un blocage de votre adresse IP ou même des poursuites. Introduisez des délais (
time.sleep()) entre les requêtes. - Données personnelles : Ne collectez jamais de données personnelles sans consentement explicite. Le respect du RGPD (GDPR) est primordial.
- Propriété intellectuelle : Les données que vous scrapeez peuvent être la propriété intellectuelle du site web. L'utilisation commerciale de données scrapées sans permission est risquée.
En tant qu'étudiant en développement avancé, il est de votre responsabilité d'utiliser ces outils de manière éthique et légale.
Les Outils de Notre Boîte à Outils
Dans cette leçon, nous allons explorer trois bibliothèques Python essentielles pour le web scraping :
requests: Pour envoyer des requêtes HTTP et récupérer le contenu brut d'une page web.BeautifulSoup: Pour analyser le HTML/XML reçu et naviguer dans la structure de la page.Selenium: Pour interagir avec des pages web dynamiques rendues par JavaScript, simulant un navigateur réel.
Les bases du Scraping : requests et BeautifulSoup
Ces deux bibliothèques forment la base de la plupart des projets de scraping simples.
1. requests : La clé d'accès au contenu web
requests est une bibliothèque HTTP élégante et simple qui vous permet d'envoyer toutes sortes de requêtes HTTP (GET, POST, PUT, DELETE, etc.) et de gérer les réponses.
Installation
pip install requests
Utilisation Basique
Pour obtenir le contenu d'une page web, il suffit de faire une requête GET.
import requests
# L'URL du site à scraper (exemple : un blog fictif)
url = "https://www.exemple.com/blog" # Remplacez par une URL réelle pour tester
# Effectuer une requête GET
response = requests.get(url)
# Vérifier le code de statut HTTP
# 200 OK indique que la requête a réussi
if response.status_code == 200:
print("Requête réussie ! Contenu HTML de la page :")
# Afficher les 500 premiers caractères du contenu HTML
print(response.text[:500])
else:
print(f"Erreur lors de la requête : Statut {response.status_code}")
# Le contenu brut de la page est disponible via response.text (pour du texte/HTML)
# ou response.content (pour du binaire, comme des images)
Explication du code :
requests.get(url)envoie une requête HTTPGETà l'URL spécifiée et retourne un objetResponse.response.status_codecontient le code de statut HTTP de la réponse (ex:200pour succès,404pour non trouvé,500pour erreur serveur).response.textcontient le contenu de la réponse sous forme de chaîne de caractères (généralement le HTML de la page).
Gérer les En-têtes (Headers)
Parfois, les sites web vérifient les en-têtes de la requête pour s'assurer qu'elle provient d'un navigateur "normal" et non d'un bot. L'en-tête User-Agent est le plus couramment vérifié.
import requests
url = "https://www.exemple.com/produits"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
print("Requête avec User-Agent réussie.")
else:
print(f"Erreur avec User-Agent : Statut {response.status_code}")
Explication : Nous passons un dictionnaire headers à la fonction requests.get(). Le User-Agent ci-dessus est un exemple courant qui simule un navigateur Chrome sur Windows.
2. BeautifulSoup : Naviguer dans le DOM (Document Object Model)
Une fois que vous avez le HTML brut de la page avec requests, vous avez besoin d'un moyen de le parser (analyser) et d'en extraire les informations pertinentes. C'est là qu'intervient BeautifulSoup. BeautifulSoup est une bibliothèque Python qui facilite l'extraction de données à partir de fichiers HTML et XML.
Installation
pip install beautifulsoup4
pip install lxml # Pour de meilleures performances, BeautifulSoup peut utiliser lxml comme parser
Analyser le Contenu HTML
from bs4 import BeautifulSoup
import requests
url = "https://www.scrapethissite.com/pages/simple/" # Un site pour s'entraîner au scraping
response = requests.get(url)
if response.status_code == 200:
# Créer un objet BeautifulSoup
# Le deuxième argument 'lxml' spécifie le parseur à utiliser (plus rapide que le parseur Python par défaut)
soup = BeautifulSoup(response.text, 'lxml')
# Afficher le titre de la page
print(f"Titre de la page : {soup.title.string}")
# Trouver le premier paragraphe
first_paragraph = soup.find('p')
if first_paragraph:
print(f"\nPremier paragraphe : {first_paragraph.text.strip()}")
else:
print("\nAucun paragraphe trouvé.")
else:
print(f"Erreur lors de la requête : Statut {response.status_code}")
Explication :
BeautifulSoup(response.text, 'lxml')crée un objetBeautifulSoupqui représente l'arbre DOM du document HTML.soup.title.stringaccède directement à la balise<title>et extrait son contenu textuel.soup.find('p')trouve la première balise<p>dans le document..textextrait le contenu textuel d'une balise et de ses enfants..strip()est utilisé pour supprimer les espaces blancs en début et fin de chaîne.
Rechercher des Éléments
BeautifulSoup offre plusieurs méthodes puissantes pour trouver des éléments :
find(name, attrs, recursive, string, **kwargs): Retourne le premier élément correspondant.find_all(name, attrs, recursive, string, limit, **kwargs): Retourne tous les éléments correspondants sous forme de liste.
Exemples de recherche :
from bs4 import BeautifulSoup
import requests
url = "https://www.scrapethissite.com/pages/simple/"
response = requests.get(url)
soup = BeautifulSoup(response.text, 'lxml')
print("\n--- Recherche par balise ---")
# Trouver toutes les balises <a> (liens)
all_links = soup.find_all('a')
print(f"Nombre de liens trouvés : {len(all_links)}")
# for link in all_links[:5]: # Afficher les 5 premiers liens
# print(link.get('href')) # Extraire l'attribut 'href'
print("\n--- Recherche par ID ---")
# Trouver un élément par son ID (les IDs sont uniques)
# Supposons un HTML : <div id="footer">...</div>
footer_div = soup.find(id='footer') # ou soup.find('div', id='footer')
if footer_div:
print(f"Contenu du footer : {footer_div.text.strip()[:100]}...") # Affiche un extrait
print("\n--- Recherche par classe CSS ---")
# Trouver des éléments par leur classe CSS
# Supposons un HTML : <p class="intro">...</p>
intro_paragraphs = soup.find_all('p', class_='lead') # 'class_' car 'class' est un mot-clé Python
print(f"Nombre de paragraphes d'introduction : {len(intro_paragraphs)}")
for p in intro_paragraphs:
print(f"Paragraphe lead : {p.text.strip()}")
print("\n--- Recherche par Sélecteur CSS (Méthode .select()) ---")
# BeautifulSoup supporte aussi les sélecteurs CSS, très pratiques
# pour les requêtes complexes, similaires à JavaScript ou jQuery.
# Utilise .select() pour tous les éléments, .select_one() pour le premier.
# Tous les éléments <p> ayant la classe 'lead'
lead_paragraphs_css = soup.select('p.lead')
print(f"\nNombre de paragraphes d'introduction (CSS) : {len(lead_paragraphs_css)}")
# Un élément avec l'ID 'country-list'
country_list_div = soup.select_one('#countries')
if country_list_div:
print(f"ID #countries trouvé : {country_list_div.name}")
# Tous les noms de pays (balises td avec la classe 'country-name')
country_names = soup.select('td.country-name')
print(f"\nLes 5 premiers pays trouvés :")
for country in country_names[:5]:
print(country.text.strip())
Explication :
soup.find(id='footer')ousoup.find('div', id='footer'): Recherche la première balise avec l'attributidégal à'footer'.soup.find_all('p', class_='lead'): Recherche toutes les balises<p>qui ont la classe CSSlead. Notez l'utilisation declass_carclassest un mot-clé réservé en Python.soup.select('p.lead'): C'est l'équivalent CSS du précédent. Plus concis pour ceux familiers avec les sélecteurs CSS.soup.select_one('#countries'): Trouve le premier élément avec l'IDcountries.soup.select('td.country-name'): Trouve toutes les balises<td>qui ont la classecountry-name.
Scraping de Contenu Dynamique avec Selenium
Les sites web modernes utilisent massivement JavaScript pour charger du contenu de manière asynchrone, animer des éléments ou même construire l'intégralité de la page après le chargement initial. requests et BeautifulSoup ne "voient" que le HTML brut renvoyé par le serveur. Ils n'exécutent pas JavaScript.
C'est là que Selenium entre en jeu. Selenium est une suite d'outils pour l'automatisation des navigateurs web. Il démarre un vrai navigateur (Chrome, Firefox, etc.) et le contrôle via du code. Ainsi, il peut interagir avec les éléments (cliquer, remplir des formulaires), attendre le chargement de contenu JavaScript, et accéder au DOM après son rendu complet.
Pourquoi Selenium ?
- Contenu généré par JavaScript : Pages avec AJAX, SPA (Single Page Applications), contenu chargé dynamiquement.
- Interactions utilisateur : Clics sur des boutons "charger plus", défilement infini, remplissage de formulaires, connexion.
- Tests automatisés :
Seleniumest aussi largement utilisé pour les tests d'interface utilisateur.
Installation et Configuration
-
Installer la bibliothèque
selenium:pip install selenium -
Télécharger un WebDriver :
Seleniuma besoin d'un "pilote" (WebDriver) pour contrôler un navigateur spécifique.- ChromeDriver pour Google Chrome : https://chromedriver.chromium.org/downloads (Choisissez la version correspondant à votre version de Chrome).
- GeckoDriver pour Mozilla Firefox : https://github.com/mozilla/geckodriver/releases
Téléchargez l'exécutable et placez-le dans un répertoire accessible par votre
PATHsystème, ou spécifiez son chemin complet dans votre code.
Principes de base de Selenium
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# --- Configuration du WebDriver ---
# Remplacez 'chemin/vers/chromedriver' par le chemin réel de votre ChromeDriver
# Ou assurez-vous qu'il est dans votre PATH système.
# service = Service('chemin/vers/chromedriver') # Utilisez cette ligne si ChromeDriver n'est pas dans le PATH
# driver = webdriver.Chrome(service=service)
# Si ChromeDriver est dans votre PATH, vous pouvez faire :
driver = webdriver.Chrome() # Ou webdriver.Firefox() pour Firefox
# --- Naviguer vers une URL ---
url_dynamic = "https://www.scrapethissite.com/pages/ajax-javascript/"
driver.get(url_dynamic)
print(f"Titre de la page (Selenium) : {driver.title}")
# --- Interagir avec des éléments (Ex: cliquer sur un bouton) ---
# Le contenu des pays est chargé via JavaScript après un clic sur une année.
# On va cliquer sur l'année 2015.
try:
# Attendre que l'élément soit cliquable. C'est CRUCIAL pour les pages dynamiques.
# On attend jusqu'à 10 secondes.
year_2015_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, '2015-page'))
)
year_2015_button.click()
# Attendre que le contenu AJAX soit chargé.
# On attend qu'un élément spécifique apparaisse ou que le texte change.
# Ici, on attend qu'une ligne de pays apparaisse dans le tableau.
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, '.country-name'))
)
# --- Extraire des données du DOM mis à jour ---
# Maintenant, nous pouvons extraire les noms des pays.
country_elements = driver.find_elements(By.CLASS_NAME, 'country-name')
print("\nNoms des pays chargés pour l'année 2015 :")
for i, country in enumerate(country_elements):
if i >= 5: # Afficher les 5 premiers pour l'exemple
break
print(country.text.strip())
except Exception as e:
print(f"Une erreur s'est produite : {e}")
finally:
# --- Fermer le navigateur ---
# Toujours fermer le navigateur pour libérer les ressources
driver.quit()
Explication du code :
- Initialisation du WebDriver :
driver = webdriver.Chrome()lance une instance de Chrome (ou Firefox, siwebdriver.Firefox()est utilisé). Une fenêtre de navigateur s'ouvre. - Navigation :
driver.get(url_dynamic)ouvre l'URL spécifiée dans le navigateur. - Localisation d'éléments :
Seleniumutilise la méthodefind_element(pour un seul élément) oufind_elements(pour une liste) avec la classeBypour spécifier le type de sélecteur :By.ID: Par l'attributid.By.NAME: Par l'attributname.By.CLASS_NAME: Par la classe CSS.By.TAG_NAME: Par le nom de la balise HTML.By.LINK_TEXTouBy.PARTIAL_LINK_TEXT: Pour les liens<a>.By.CSS_SELECTOR: Par un sélecteur CSS.By.XPATH: Par une expression XPath (très puissant mais peut être complexe).
- Attente explicite :
WebDriverWaitcombiné avecexpected_conditions(EC) est la méthode recommandée pour attendre que des éléments deviennent disponibles. Les pages dynamiques mettent du temps à charger leur contenu, et essayer d'interagir avec un élément avant qu'il n'existe déclenchera une erreur.EC.element_to_be_clickable((By.ID, '2015-page')): Attend que l'élément avec l'ID2015-pagesoit présent dans le DOM et cliquable.EC.presence_of_element_located((By.CSS_SELECTOR, '.country-name')): Attend qu'au moins un élément avec la classecountry-namesoit présent dans le DOM.
- Interactions :
year_2015_button.click(): Simule un clic de souris sur l'élément.element.send_keys("votre texte"): Simule la saisie de texte dans un champ de formulaire.
- Extraction de données : Une fois les éléments trouvés, vous pouvez accéder à leur texte (
.text) ou à leurs attributs (.get_attribute('href')). - Fermeture du navigateur :
driver.quit()est essentiel pour fermer la fenêtre du navigateur et libérer les ressources.
Mode Headless (Sans Interface Graphique)
Pour le scraping à grande échelle ou sur des serveurs, vous ne voulez pas qu'un navigateur s'affiche. Selenium peut fonctionner en mode headless (sans interface graphique).
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--headless') # Active le mode headless
options.add_argument('--disable-gpu') # Recommandé pour Linux
driver = webdriver.Chrome(options=options)
# ... votre code de scraping ...
driver.get("https://www.scrapethissite.com")
print(f"Titre de la page en mode headless : {driver.title}")
driver.quit()
Explication : Nous créons un objet Options et lui ajoutons l'argument --headless avant de le passer au constructeur de webdriver.Chrome().
Bonnes Pratiques et Pièges à Éviter
1. Respecter les Règles
- Toujours vérifier
robots.txtet les conditions d'utilisation. - Limiter la fréquence des requêtes : Utilisez
time.sleep(X)entre les requêtes pour éviter de surcharger le serveur et d'être bloqué. Soyez respectueux ! - Utiliser un
User-Agentréaliste : Cela aide à éviter d'être identifié comme un bot.
2. Gestion des Erreurs et Robustesse
- Vérifier les codes de statut HTTP : Un
status_codedifférent de 200 indique un problème.403 Forbidden: Souvent dû à un blocage ou unUser-Agentmanquant.404 Not Found: La page n'existe pas.5xx Server Error: Problème côté serveur.
- Utiliser des blocs
try-except: Pour gérer les erreurs lors de la recherche d'éléments (ex:AttributeErrorsi un élément n'est pas trouvé parBeautifulSoup,NoSuchElementExceptionavecSelenium). - Retries : Mettre en place un mécanisme de nouvelle tentative pour les erreurs temporaires (ex: 500, 503).
3. Optimisation
lxmlpourBeautifulSoup: Il est significativement plus rapide pour le parsing HTML/XML que le parseur Python par défaut. Installez-le (pip install lxml) et utilisezBeautifulSoup(html_doc, 'lxml').- Mode Headless pour
Selenium: Réduit la consommation de ressources et accélère l'exécution en évitant l'affichage graphique. - Cacher les requêtes : Pour des scripts de développement, utilisez une bibliothèque comme
requests-cachepour éviter de refaire les mêmes requêtes HTTP. - Requêtes spécifiques : Ne scrapeez que les données dont vous avez besoin. Moins de données à extraire = plus rapide.
- Limitez les opérations
Selenium: Chaque interaction avec le navigateur est lente. SirequestsetBeautifulSouppeuvent faire le travail, privilégiez-les. N'utilisezSeleniumque lorsque c'est absolument nécessaire.
4. Maintenance
- Les sites web changent : La structure HTML des pages web évolue constamment. Vos sélecteurs CSS ou XPath peuvent devenir obsolètes du jour au lendemain. Préparez-vous à mettre à jour vos scripts régulièrement.
Conclusion et Résumé
Le web scraping est une compétence puissante qui ouvre un monde de possibilités pour la collecte de données. Nous avons exploré les outils fondamentaux pour y parvenir avec Python :
requests: Votre première étape pour récupérer le contenu brut d'une page web via des requêtes HTTP. Idéal pour le contenu statique.BeautifulSoup: Votre outil de prédilection pour analyser le HTML/XML et extraire des données structurées. Il excelle dans la navigation du DOM une fois le contenu brut obtenu.Selenium: Votre solution pour les défis du contenu dynamique, là où JavaScript est roi. Il automatise un véritable navigateur, permettant des interactions complexes et l'accès au DOM entièrement rendu.
Le choix de l'outil dépend de la nature du site web que vous souhaitez scraper. Commencez toujours par requests et BeautifulSoup ; si le contenu désiré n'apparaît pas ou nécessite une interaction, alors Selenium est la voie à suivre.
N'oubliez jamais l'aspect éthique et légal. Le scraping est un privilège, pas un droit. Utilisez-le de manière responsable, respectueuse et en conformité avec les lois et les conditions d'utilisation des sites.
Pour approfondir, explorez des sujets comme :
- La gestion des proxies pour éviter les blocages IP.
- L'utilisation de bases de données pour stocker les données scrapées (SQLite, PostgreSQL, MongoDB).
- La planification de scripts de scraping (avec
cronouAPScheduler). - Des frameworks de scraping plus avancés comme Scrapy.
Le monde du web scraping est vaste et en constante évolution. Continuez à apprendre, à expérimenter, et à construire des applications intelligentes et éthiques.