Se sei arrivato a questa lezione, significa che hai già una buona idea di come funziona il web scraping e come utilizzare il fantastico BeautifulSoup
per estrarre i dati HTML di cui hai bisogno. Tuttavia, nel mondo del web scraping, non è tutto rose e fiori come si legge nei libri di testo. A volte, il nostro sogno di estrarre dati si trasforma in una lotta contro gli errori, quindi parliamo di come evitare ostacoli e rendere il nostro scraper il più stabile possibile.
1. Errori comuni nel web scraping
Errore 404 e altri errori HTTP
Un classico problema: provi a ottenere una pagina e invece del contenuto ricevi un orgoglioso "404 Not Found". Questo può succedere perché la pagina è stata rimossa o spostata. Altri errori HTTP comuni includono 403 (Forbidden), 500 (Internal Server Error) e 503 (Service Unavailable).
Modifica della struttura HTML
Hai passato un sacco di tempo a scrivere il codice per estrarre i dati e il giorno dopo il sito decide di "rifarsi il look" e cambia la struttura HTML. Eh sì, tocca riscrivere tutto da capo!
Limitazioni sul numero di richieste
Alcuni siti iniziano a sospettare qualcosa di strano quando sono bersagliati tutto il giorno da scraper. Nel migliore dei casi, ti bloccheranno per un po', nel peggiore — per sempre.
Tempi di attesa e timeout
A volte le pagine si caricano lentamente e il tuo script può andare in crash se l'attesa supera il tempo di timeout standard.
2. Metodi per la gestione degli errori
Uso di try-except
I tuoi script non dovrebbero bloccarsi per ogni imprevisto. L'aggiunta di blocchi try-except
aiuta a gestire gli errori e a fare in modo che il tuo scraper continui a funzionare come se nulla fosse successo.
import requests
from bs4 import BeautifulSoup
url = 'http://example.com/some-nonexistent-page'
try:
response = requests.get(url)
response.raise_for_status() # Lancia HTTPError per risposte errate
except requests.exceptions.HTTPError as errh:
print("Errore HTTP:", errh)
except requests.exceptions.ConnectionError as errc:
print("Errore di Connessione:", errc)
except requests.exceptions.Timeout as errt:
print("Errore di Timeout:", errt)
except requests.exceptions.RequestException as err:
print("OOps: Qualcos'altro è andato storto", err)
Uno script ben fatto non si limita a intercettare le eccezioni — ha una risposta per ogni tipo di errore. Sei stato bannato per IP? Passa al prossimo proxy. Il sito è caduto? Intanto analizza un altro. Se non vengono trovati alcuni elementi nella pagina che dovrebbero esserci, puoi avvisare il proprietario dello scraper che il codice deve essere aggiornato e inviargli un'email.
Logging
"A cosa servono i log?" — potresti chiederti. Beh, i log sono come avere una seconda vista. Ti aiutano a capire cosa è andato storto e a risolvere il problema il più rapidamente possibile.
import logging
logging.basicConfig(filename='scraper.log', level=logging.INFO)
try:
# Il tuo codice di scraping
pass
except Exception as e:
logging.error("Si è verificata un'eccezione", exc_info=True)
Uso di timeout e retry
A volte tutto ciò che serve è aspettare un po' e riprovare. A questo scopo, i timeout e i retry sono perfetti.
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
except requests.exceptions.Timeout:
print("Timeout rilevato. Riprovo...")
# Ripeti la richiesta o esegui un'altra azione
3. Esempi di scraping resiliente
Uno scraper semplice con gestione degli errori
Creiamo un piccolo, ma affidabile scraper che può gestire alcuni errori comuni.
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("Errore HTTP: %s", errh)
except requests.exceptions.ConnectionError as errc:
logging.error("Errore di Connessione: %s", errc)
except requests.exceptions.Timeout as errt:
logging.warning("Timeout rilevato. Riprovo...")
time.sleep(1) # Aspetta un po' e riprova
return fetch_html(url)
except requests.exceptions.RequestException as err:
logging.error("OOps: Qualcos'altro è andato storto %s", err)
return None
html_content = fetch_html(url)
if html_content:
soup = BeautifulSoup(html_content, 'html.parser')
# Il tuo codice per estrarre i dati
else:
print("Impossibile recuperare il contenuto della pagina.")
Salvataggio dei dati a pezzi
Per non perdere i dati già estratti in caso di crash, salva i dati a pezzi. Ad esempio, se stai estraendo informazioni da un elenco di pagine, salvando i risultati man mano che procedi, minimizzi il rischio di perdere dati.
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)
In questo modo, anche se il tuo script si blocca a metà, non perderai tutti i dati e potrai continuare dallo stato salvato più recente.
La pagina che stai analizzando potrebbe cambiare parzialmente e la maggior parte dei dati sarà ancora disponibile. Non sarebbe bello annullare lo scraping e perdere dati preziosi se manca solo una piccola percentuale di essi.
GO TO FULL VERSION