Si tu es arrivé jusqu'à cette conférence, cela signifie que tu as déjà une bonne idée de comment fonctionne le web scraping et comment utiliser l'excellent BeautifulSoup
pour extraire les données nécessaires à partir de HTML. Cependant, dans le monde du web scraping, tout n'est pas aussi simple qu'on le lit dans les manuels. Parfois, notre rêve de collecter des données se transforme en lutte contre les erreurs. Alors, parlons de comment éviter les pièges et rendre notre scraper aussi solide que possible.
1. Erreurs fréquentes dans le web scraping
Erreur 404 et autres erreurs HTTP
Problème classique : tu essaies d'accéder à une page, mais à la place d'un contenu, tu obtiens un fier "404 Not Found". Cela peut arriver parce que la page a été supprimée ou déplacée. D'autres erreurs HTTP courantes incluent 403 (Forbidden), 500 (Internal Server Error) et 503 (Service Unavailable).
Changement de structure HTML
Tu as passé un temps fou à écrire du code pour extraire les données, et le lendemain, le site décide de se "refaire une beauté" et change sa structure HTML. Oups, il faut tout réécrire !
Limites sur le nombre de requêtes
Certains sites commencent à trouver ça louche quand ils se font marteler toute la journée par des scrapers. Dans le meilleur des cas, tu seras temporairement bloqué, et dans le pire - banni à jamais.
Temps d'attente et délais
Parfois, les pages mettent du temps à se charger et ton script peut planter si l'attente dépasse le délai standard.
2. Méthodes de gestion des erreurs
Utilisation de try-except
Tes scripts ne doivent pas planter pour n'importe quelle surprise. Ajouter des blocs try-except
aide à attraper les erreurs et permet à ton web scraper de continuer de fonctionner comme si de rien n'était.
import requests
from bs4 import BeautifulSoup
url = 'http://example.com/some-nonexistent-page'
try:
response = requests.get(url)
response.raise_for_status() # Déclenche HTTPError pour les mauvaises réponses
except requests.exceptions.HTTPError as errh:
print("Erreur HTTP :", errh)
except requests.exceptions.ConnectionError as errc:
print("Erreur de connexion :", errc)
except requests.exceptions.Timeout as errt:
print("Temps d'attente dépassé :", errt)
except requests.exceptions.RequestException as err:
print("Oups : Quelque chose s’est mal passé", err)
Un bon script ne se contente pas de capturer une exception - il propose une action pour chaque type d'erreur. Tu es banni par IP ? Passe au proxy suivant. Le site est tombé ? Pendant ce temps, tu pars d'autres pages. Si certains éléments sur la page, censés être présents, ne le sont pas, tu peux alerter le propriétaire du scraper qu'une mise à jour du script est nécessaire et lui envoyer un email.
Journalisation
"Pourquoi les logs ?" — demanderais-tu. Parce que les logs, c'est comme tes secondes lunettes. Ils t’aident à comprendre ce qui n’a pas fonctionné et à corriger l’erreur au plus vite.
import logging
logging.basicConfig(filename='scraper.log', level=logging.INFO)
try:
# Ton code de scraping
pass
except Exception as e:
logging.error("Une exception est survenue", exc_info=True)
Utilisation des délais et des nouvelles tentatives
Parfois, tout ce qu’il faut, c’est attendre un peu et réessayer. Pour cela, les délais et les nouvelles tentatives sont parfaits.
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
except requests.exceptions.Timeout:
print("Temps d'attente dépassé. Nouvelle tentative...")
# Répète la requête ou effectue une autre action
3. Exemples de web scraping robuste
Scraper simple avec gestion des erreurs
Créons un petit scraper robuste capable de gérer certaines erreurs courantes.
import requests
from bs4 import BeautifulSoup
import time
import logging
logging.basicConfig(filename='scraper.log', level=logging.INFO)
url = 'http://example.com/some-nonexistent-page'
def fetch_html(url):
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.text
except requests.exceptions.HTTPError as errh:
logging.error("Erreur HTTP : %s", errh)
except requests.exceptions.ConnectionError as errc:
logging.error("Erreur de connexion : %s", errc)
except requests.exceptions.Timeout as errt:
logging.warning("Temps d'attente dépassé. Nouvelle tentative...")
time.sleep(1) # Attends un peu et réessaye
return fetch_html(url)
except requests.exceptions.RequestException as err:
logging.error("Oups : Quelque chose s’est mal passé %s", err)
return None
html_content = fetch_html(url)
if html_content:
soup = BeautifulSoup(html_content, 'html.parser')
# Ton code pour extraire les données
else:
print("Impossible de récupérer le contenu de la page.")
Sauvegarde des données par morceaux
Pour ne pas perdre les données déjà extraites en cas de panne, sauvegarde-les par morceaux. Par exemple, si tu extrais des infos à partir d’une liste de pages, en sauvegardant les résultats au fur et à mesure, tu minimises le risque de perte de données.
import csv
def save_to_csv(data, filename='data.csv'):
with open(filename, mode='a', newline='') as file:
writer = csv.writer(file)
writer.writerow(data)
Ainsi, même si ton script plante en plein milieu, tu ne perdras pas toutes les données et tu pourras reprendre à partir de l’état sauvegardé.
La page que tu pars peut changer partiellement, et la majorité des données restera disponible. Ce serait dommage d'annuler le parsing et perdre des données précieuses si seulement un tout petit pourcentage est manquant.
GO TO FULL VERSION