Le web scraping permet d'extraire automatiquement des données depuis des sites web. Python offre d'excellents outils : requests pour récupérer les pages, BeautifulSoup pour parser le HTML, et Selenium pour les sites dynamiques JavaScript.
import requests
from time import sleep
# Requête GET simple
url = "https://example.com"
response = requests.get(url)
# Vérifier le statut
if response.status_code == 200:
print("Page récupérée avec succès")
html_content = response.text
else:
print(f"Erreur: {response.status_code}")
# Headers personnalisés (simuler un navigateur)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.get(url, headers=headers)
# Timeout pour éviter blocages
try:
response = requests.get(url, timeout=10)
except requests.Timeout:
print("Timeout - site trop lent")
except requests.RequestException as e:
print(f"Erreur: {e}")
# Gérer les redirections
response = requests.get(url, allow_redirects=True)
print(f"URL finale: {response.url}")
# Télécharger fichier
response = requests.get("https://example.com/image.jpg")
with open("image.jpg", "wb") as f:
f.write(response.content)
pip install beautifulsoup4 lxml
import requests
from bs4 import BeautifulSoup
# Récupérer et parser
url = "https://example.com"
response = requests.get(url)
soup = BeautifulSoup(response.content, 'html.parser')
# Trouver éléments
titre = soup.find('h1') # Premier h1
tous_liens = soup.find_all('a') # Tous les liens
premier_lien = soup.find('a') # Premier lien
# Par classe CSS
articles = soup.find_all('div', class_='article')
# OU
articles = soup.find_all('div', {'class': 'article'})
# Par ID
header = soup.find(id='header')
# Sélecteurs CSS
articles = soup.select('div.article') # Classe
titres = soup.select('h1, h2, h3') # Plusieurs éléments
liens = soup.select('div.content a') # Descendance
# Extraire données
print(titre.text) # Texte
print(premier_lien.get('href')) # Attribut
print(premier_lien['href']) # Alternative
# Parents et enfants
element = soup.find('div', class_='content')
parent = element.parent
enfants = element.children # Itérable
tous_enfants = element.descendants # Récursif
# Frères et sœurs
suivant = element.next_sibling
precedent = element.previous_sibling
tous_suivants = element.find_next_siblings()
# Recherche dans sous-arbre
div = soup.find('div', class_='container')
liens_dans_div = div.find_all('a')
import requests
from bs4 import BeautifulSoup
import csv
from time import sleep
def scraper_articles(url_base, nb_pages=5):
"""Scrape articles d'un site avec pagination"""
tous_articles = []
for page in range(1, nb_pages + 1):
url = f"{url_base}?page={page}"
print(f"Scraping page {page}...")
try:
response = requests.get(url, timeout=10)
soup = BeautifulSoup(response.content, 'html.parser')
# Trouver tous les articles
articles = soup.find_all('article', class_='post')
for article in articles:
# Extraire données
titre_elem = article.find('h2', class_='title')
date_elem = article.find('time')
auteur_elem = article.find('span', class_='author')
lien_elem = article.find('a')
# Créer dictionnaire
data = {
'titre': titre_elem.text.strip() if titre_elem else None,
'date': date_elem.get('datetime') if date_elem else None,
'auteur': auteur_elem.text.strip() if auteur_elem else None,
'lien': lien_elem.get('href') if lien_elem else None
}
tous_articles.append(data)
# Pause pour ne pas surcharger
sleep(2)
except Exception as e:
print(f"Erreur page {page}: {e}")
continue
return tous_articles
# Utilisation
articles = scraper_articles("https://example-blog.com/posts", nb_pages=3)
# Sauvegarder en CSV
with open('articles.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['titre', 'date', 'auteur', 'lien'])
writer.writeheader()
writer.writerows(articles)
print(f"Total articles récupérés: {len(articles)}")
BeautifulSoup ne peut pas exécuter JavaScript. Pour les sites avec contenu dynamique (React, Vue, chargement AJAX), utilisez Selenium qui pilote un vrai navigateur.
pip install selenium
# Télécharger ChromeDriver ou utiliser webdriver-manager
pip install webdriver-manager
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
# Initialiser navigateur
options = webdriver.ChromeOptions()
options.add_argument('--headless') # Mode sans interface
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)
try:
# Charger page
driver.get("https://example.com")
# Attendre chargement (max 10 secondes)
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, "content"))
)
# Trouver éléments
titre = driver.find_element(By.TAG_NAME, "h1")
liens = driver.find_elements(By.TAG_NAME, "a")
# Extraire données
print(titre.text)
for lien in liens:
print(lien.get_attribute('href'))
# Prendre screenshot
driver.save_screenshot("page.png")
finally:
driver.quit()
from selenium.webdriver.common.keys import Keys
import time
# Remplir formulaire
champ_recherche = driver.find_element(By.NAME, "q")
champ_recherche.send_keys("Python scraping")
champ_recherche.send_keys(Keys.RETURN)
# Cliquer bouton
bouton = driver.find_element(By.ID, "submit-button")
bouton.click()
# Scroller en bas de page
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# Attendre chargement dynamique
time.sleep(2)
# Changer de fenêtre/onglet
driver.switch_to.window(driver.window_handles[1])
# Exécuter JavaScript
titre = driver.execute_script("return document.title")
# Gérer popup
alert = driver.switch_to.alert
alert.accept() # OK
# alert.dismiss() # Annuler
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
def scraper_scroll_infini(url, nb_scrolls=5):
"""Scrape site avec chargement infini (scroll)"""
driver = webdriver.Chrome()
driver.get(url)
tous_elements = []
derniere_hauteur = driver.execute_script("return document.body.scrollHeight")
for i in range(nb_scrolls):
print(f"Scroll {i+1}/{nb_scrolls}")
# Récupérer éléments actuels
elements = driver.find_elements(By.CLASS_NAME, "product-card")
for elem in elements:
titre = elem.find_element(By.CLASS_NAME, "title").text
prix = elem.find_element(By.CLASS_NAME, "price").text
tous_elements.append({'titre': titre, 'prix': prix})
# Scroller en bas
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(3) # Attendre chargement nouveau contenu
# Vérifier si nouveau contenu chargé
nouvelle_hauteur = driver.execute_script("return document.body.scrollHeight")
if nouvelle_hauteur == derniere_hauteur:
print("Plus de contenu à charger")
break
derniere_hauteur = nouvelle_hauteur
driver.quit()
# Éliminer doublons
elements_uniques = list({elem['titre']: elem for elem in tous_elements}.values())
return elements_uniques
produits = scraper_scroll_infini("https://example-shop.com/products", nb_scrolls=10)
print(f"Total produits: {len(produits)}")
import time
import random
# Délai entre requêtes
time.sleep(random.uniform(2, 5)) # 2-5 secondes aléatoire
# User-Agent réaliste
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
# Vérifier robots.txt
from urllib.robotparser import RobotFileParser
rp = RobotFileParser()
rp.set_url("https://example.com/robots.txt")
rp.read()
if rp.can_fetch("*", "https://example.com/page"):
print("Scraping autorisé")
else:
print("Scraping non autorisé")
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
def create_session():
"""Session avec retry automatique"""
session = requests.Session()
retry = Retry(
total=3,
backoff_factor=1,
status_forcelist=[500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session
# Utilisation
session = create_session()
try:
response = session.get(url, timeout=10)
response.raise_for_status() # Lève exception si erreur HTTP
except requests.RequestException as e:
print(f"Erreur: {e}")
import requests_cache
from datetime import timedelta
# Activer cache SQLite
requests_cache.install_cache(
'scraping_cache',
expire_after=timedelta(hours=24)
)
# Les requêtes identiques utilisent le cache
response = requests.get(url) # 1ère fois: requête réelle
response = requests.get(url) # 2ème fois: depuis cache
import requests
from bs4 import BeautifulSoup
import pandas as pd
from datetime import datetime
def surveiller_prix(urls_produits):
"""Surveille prix de plusieurs produits"""
resultats = []
for url in urls_produits:
try:
response = requests.get(url, timeout=10)
soup = BeautifulSoup(response.content, 'html.parser')
# Adapter sélecteurs selon site
nom = soup.find('h1', class_='product-title').text.strip()
prix_elem = soup.find('span', class_='price')
prix = float(prix_elem.text.replace('CHF', '').strip())
dispo = soup.find('div', class_='stock')
en_stock = 'En stock' in dispo.text if dispo else False
resultats.append({
'date': datetime.now(),
'produit': nom,
'prix': prix,
'en_stock': en_stock,
'url': url
})
except Exception as e:
print(f"Erreur {url}: {e}")
# Sauvegarder historique
df = pd.DataFrame(resultats)
df.to_csv('historique_prix.csv', mode='a', header=False, index=False)
return resultats
# Liste produits à surveiller
produits = [
"https://shop.com/product1",
"https://shop.com/product2"
]
prix = surveiller_prix(produits)
# Alerter si prix baisse
for p in prix:
if p['prix'] < 100: # Seuil
print(f"⚠️ ALERTE: {p['produit']} à {p['prix']} CHF !")
def scraper_offres_emploi(mot_cle, ville):
"""Scrape offres d'emploi selon critères"""
url = f"https://job-site.com/search?q={mot_cle}&location={ville}"
response = requests.get(url)
soup = BeautifulSoup(response.content, 'html.parser')
offres = []
for job in soup.find_all('div', class_='job-listing'):
titre = job.find('h2', class_='job-title').text.strip()
entreprise = job.find('span', class_='company').text.strip()
lieu = job.find('span', class_='location').text.strip()
lien = job.find('a')['href']
# Scraper page détail
detail_response = requests.get(lien)
detail_soup = BeautifulSoup(detail_response.content, 'html.parser')
description = detail_soup.find('div', class_='description').text.strip()
offres.append({
'titre': titre,
'entreprise': entreprise,
'lieu': lieu,
'description': description[:200], # Extrait
'lien': lien
})
time.sleep(1) # Pause entre pages
return offres
# Rechercher développeur Python à Lausanne
offres = scraper_offres_emploi("Python Developer", "Lausanne")
# Sauvegarder
df = pd.DataFrame(offres)
df.to_excel('offres_emploi.xlsx', index=False)