Salta al contenuto

Come attendere il caricamento della pagina in Selenium? Una guida per esperti

Fammi indovinare: hai iniziato a raschiare un sito con Selenium e all'improvviso ti trovi di fronte a temuti errori di timeout, eccezioni di elementi obsoleti e localizzatori instabili. Suona familiare?

Molti di noi sono stati lì! Nel Web dinamico di oggi, attendere adeguatamente il caricamento completo delle pagine prima di interagire è fondamentale per un'automazione affidabile.

In questa guida completa di oltre 3200 parole, sfrutterò i miei oltre 5 anni come esperto professionista di web scraping per esplorare i vari metodi e le migliori pratiche per un'attesa aggraziata in Selenium.

Che tu sia appena agli inizi o un professionista esperto, la solida logica di attesa è uno strumento indispensabile per la stabilità. Immergiamoci!

Perché non puoi semplicemente precipitarti

Agli albori del web, le pagine erano per lo più semplici HTML renderizzate in sequenza. Gli scraper potrebbero iniziare l'estrazione immediatamente al caricamento della pagina.

Ma il web di oggi è altamente dinamico. Secondo Ricerca su Google, il tempo medio per dipingere per la prima volta è 1.7 secondi, ma il tempo medio per diventare completamente interattivo è enorme 15 secondi. È molto tempo per caricare i contenuti.

Come raschietto, se ti affretti troppo velocemente, ecco alcuni problemi comuni che dovrai affrontare:

  • Errori di clic sul pulsante perché l'elemento non è stato ancora visualizzato
  • Tentativo di leggere i dati da una tabella che non ha caricato il contenuto del server
  • Invio di testo a un input non visibile sullo schermo
  • Raschiando elementi vuoti che verranno popolati dopo il caricamento della pagina

Questi tipi di eccezioni indicano che è necessario attendere più a lungo affinché la pagina sia pronta prima di interagire.

Dai numeri: tempi di caricamento della pagina

Per capire quanto tempo potremmo dover aspettare, diamo un'occhiata ad alcuni parametri reali sulle prestazioni di caricamento della pagina dal Rapporto 2020 sullo stato del web di Akamai:

  • Tempo medio di interattività: 15 secondi
  • Peso medio della pagina: 2744KB
  • Numero medio di richieste: 105
  • Immagini medie per pagina: 53
  • Byte JavaScript per pagina: 453KB

Oggi le pagine sono più grandi e complesse e dopo la risposta iniziale viene svolto molto più lavoro. È fondamentale che i raschiatori attendano l'interattività, non solo la prima vernice.

Eccezioni comuni causate dalla mancata attesa

Ecco alcune eccezioni specifiche che possono verificarsi quando gli elementi non sono ancora pronti:

  • StaleElementReferenceException – Elemento rimosso dal DOM dopo il recupero
  • ElementNotInteractableException – Tentativo di fare clic su un elemento invisibile
  • NoSuchElementException – La ricerca è scaduta perché l'elemento non esiste ancora

Ognuno di questi indica che è necessaria una maggiore attesa da parte del raschietto.

Le attese esplicite sono tue amiche

Per evitare questi errori, dobbiamo attendere il rendering completo della pagina prima di interagire. Esistono due approcci principali nel selenio:

Attese implicite – Imposta un tempo di attesa globale sull'autista

Attese esplicite – Attendere che si verifichino condizioni specifiche

Nella maggior parte dei casi, un'attesa esplicita è di gran lunga preferibile rispetto a un'attesa implicita. Capiamo perché.

Attese implicite: l'approccio del martello

Le attese implicite impostano un timeout sul driver per interrogare il DOM durante la ricerca di elementi. Ciò significa che ogni volta che chiami:

driver.find_element_by_id(‘some-id‘)

Il driver riproverà fino alla durata di attesa implicita per individuare l'elemento prima di generare un'eccezione NoSuchElementException.

Potresti usarlo come:

driver = webdriver.Chrome()
driver.implicitly_wait(10) 

Ora tutte le ricerche verranno ritentate fino a 10 secondi per trovare elementi se non immediatamente presenti.

Lo svantaggio è che attende ogni localizzatore, anche quelli che non sono necessari per determinare la disponibilità della pagina. Questo può davvero rallentare il tuo raschietto.

Pensa alle attese implicite come all'aggiunta di una sospensione di 5 secondi a ogni recupero di elementi. I conti tornano!

La precisione delle attese esplicite

Le attese esplicite ci consentono di attendere con precisione condizioni specifiche che indichino la disponibilità prima di procedere.

Le idee chiave sono:

  • Aspetta solo quando necessario – Evita attese inutili non legate alla disponibilità della pagina
  • Condizioni precise – Attendere elementi o stati esatti, non solo tempo generale
  • Flessibilità – Personalizza la logica di attesa per pagina con condizioni diverse
  • leggibile – Intento facile da comprendere quando si rivisita il vecchio codice

Ecco un tipico esempio in attesa che appaia un elemento:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait 

WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "myDynamicElement"))
)

Ciò sospende l'esecuzione finché non viene caricato l'elemento con ID "myDynamicElement" o finché non trascorrono 10 secondi.

Altre condizioni attese utili fornito dal selenio includono:

  • title_contains() – Attendi l'aggiornamento del titolo della pagina
  • staleness_of() – Attendi che l'elemento non sia più collegato al DOM
  • element_to_be_clickable() – Attendi che l'elemento sia visibile e abilitato

L'esplicito batte l'implicito: un esempio del mondo reale

Confrontiamo le due attese con un esempio reale.

Supponiamo che sto raschiando un sito che ha una barra di navigazione, un pannello laterale sinistro e un contenuto principale.

L'elemento chiave che devo aspettare è un ID "#main-content" in cui vengono visualizzati i miei dati.

Con attesa implicita:

  • 10 secondi aggiunti a ogni ricerca di elementi, anche se non necessari
  • Ancora incline a errori di elementi obsoleti se troppo veloce

Con attesa esplicita:

  • Attendi solo quando necessario per il selettore #main-content
  • Evita inutili attese per il navigatore e il pannello laterale
  • Attendere specificatamente il caricamento dei dati prima di continuare

Aspettando selettivamente una singola condizione di disponibilità come un elemento, evito ritardi inutili.

Modelli per attese esplicite efficaci

Ora che sei convinto che le attese esplicite siano la strada da percorrere, esploriamo alcune best practice per utilizzarle in modo efficace.

Il caricamento della pagina attende

Attendere lo stato pronto del documento è una tecnica comune per determinare quando il caricamento è completo:

WebDriverWait(driver, 10).until(
   lambda d: d.execute_script(‘return document.readyState‘) == ‘complete‘
)

Questo interroga il browser finché lo stato pronto non è "completo", indicando che tutte le risorse sono caricate.

Un modello più leggero cerca elementi specifici di alto livello:

WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "main-content"))
) 

Ciò riesce quando viene caricata la sezione del contenuto principale, senza attendere tutto il resto.

Attese per azione

Puoi anche attendere subito prima di eseguire un'azione, ad esempio facendo clic su un elemento:

menu = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "top-menu"))
)

submenu = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "submenu"))
)

submenu.click()

Ciò garantisce che sia il menu principale che il sottomenu siano pronti prima di fare clic.

Attese parallele

L'attesa di più condizioni può confermare che la pagina è pronta:

WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "header")),
    EC.presence_of_element_located((By.ID, "footer")), 
    EC.presence_of_element_located((By.ID, "main"))
)

Richiedere il caricamento dell'intestazione, del piè di pagina e del contenuto principale riduce i falsi positivi.

Attese concatenate e annidate

Per gli scenari avanzati, puoi anche annidare le attese:

element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "dropdown"))
)

menu = WebDriverWait(element, 10).until(
    EC.presence_of_element_located((By.ID, "menu"))  
)

Questo attende prima un elemento genitore, quindi un elemento figlio al suo interno.

Il polling AJAX attende

Alcuni siti vengono caricati tramite richieste AJAX continue. Puoi eseguire il loop in attesa delle modifiche:

while True:

    current_count = driver.find_element_by_id(‘result-count‘).text

    # If count changed since last check, page is still loading
    if current_count != previous_count:
        previous_count = current_count
        continue 

    break # Page loaded!

Questo interroga un elemento alla ricerca di modifiche per rilevare il caricamento.

Attese asincrone

Nei framework asincroni come asyncio, puoi attendere le promesse:

await page.waitForSelector(‘#content‘)

La sintassi è leggermente diversa ma fornisce un'attesa asincrona.

Combinazione implicita + esplicita

Puoi anche combinare sia le attese implicite che quelle esplicite:

driver.implicitly_wait(10) 

my_element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "my-element"))
)

In questo modo hai un'attesa globale oltre che specifica. Assicurati solo che utilizzino durate ragionevoli.

Scelta dei localizzatori che segnalano la disponibilità

Quando selezioni i localizzatori da attendere, desideri elementi che corrispondano a questi criteri:

  • Appaiono in ritardo nel processo di caricamento
  • Avere ID o classi univoci che non cambieranno
  • Posizionato sopra la piega per controlli rapidi
  • È improbabile che vengano trasferiti a causa di modifiche al sito
  • Non farti rimuovere dal DOM e non diventare obsoleto

Alcuni esempi comuni sono:

  • Intestazione principale o navigazione caricata dopo le risorse
  • Contenitori o widget di contenuti primari
  • Piè di pagina
  • Piccoli elementi dell'interfaccia utente dinamica come i pulsanti

Anche gli indicatori di carico come gli spinner sono ottimi trigger di attesa quando scompaiono.

Ottimizzazione dei timeout per un'attesa ottimale

L'impostazione di timeout troppo lunghi può rallentare davvero il raschiatore, ma un timeout troppo breve può causare guasti instabili.

Ecco alcune best practice sull'ottimizzazione delle durate:

  • Imposta timeout di caricamento della pagina più lunghi, circa 10-20 secondi.
  • Utilizza timeout più brevi, ad esempio 3-5 secondi, per i singoli elementi.
  • Considera le prestazioni del browser, mobile e desktop.
  • Considera la latenza della rete, la banda larga rispetto al 3G.
  • Monitorare gli errori di timeout e regolarli più in alto se necessario.
  • Analizza la cascata di caricamento della pagina per i tempi di caricamento tipici.
  • Budget 1-2 secondi extra come buffer.
  • Standardizza attese simili nella tua codebase.

Man mano che raccogli più pagine, otterrai una migliore intuizione sulle attese ottimali per l'affidabilità.

Gestione degli errori di attesa e timeout

Anche con attese prolungate, potresti comunque riscontrare timeout occasionali. Ecco alcuni modi per gestirli:

  • Registra i dettagli di debug – L'aggiunta di stampe aiuta a diagnosticare dove le attese falliscono.
  • Riprovare al timeout – Riprovare brevi attese esplicite fino a 3 volte in caso di errore.
  • Aumenta il timeout – Se si verificano molti timeout, aumentare in modo incrementale le attese.
  • Utilizzare prova/eccetto – Cattura eccezioni specifiche come StaleElementReference.
  • Disabilita in caso di fallimento – È possibile saltare le attese dopo ripetuti errori per consentire la continuazione dei test.

Con una certa resilienza incorporata, questi problemi sporadici non romperanno il tuo raschietto.

Aspettando in altre lingue

Finora gli esempi sono stati in Python, ma le attese esplicite sono disponibili in tutti i linguaggi:

  • Java - WebDriverWait ed ExpectedConditions
  • C# - WebDriverWait ed ExpectedConditions
  • Ruby - WebDriver::Wait ed ExpectedConditions
  • JavaScript - browser.wait() e metodi di utilità

I concetti sono molto simili, solo la sintassi differisce leggermente.

Oltre il selenio: più strumenti di attesa

Ci sono anche altre utili librerie di attesa oltre a Selenium:

  • Ora - time.sleep() è semplice ma mette in pausa tutta l'esecuzione.
  • Riprova - L' Riprova il pacchetto rende i tentativi e le attese facili.
  • Aiohttp - await response.text() attende il completamento delle chiamate di rete.
  • Bella zuppa - BeautifulSoup(page.content, features="lxml") aspetterà l'analisi completa.
  • Scrapy - yield scrapy.Request(url, callback=self.parse) è asincrono.

Mescolarli con Selenium fornisce attese robuste nel codice.

In sintesi: aspetta bene e scarta in modo affidabile

In chiusura, ecco cinque punti chiave:

  1. Utilizza attese esplicite – Evitano timeout non necessari e mirano a condizioni specifiche.

  2. Attendi più segnali – Combina attende l'intestazione, il corpo, il piè di pagina, ecc. per confermare la disponibilità della pagina.

  3. Regola saggiamente i timeout – Imposta valori basati sui dati di caricamento delle pagine reali per ottimizzare i ritardi.

  4. Standardizzare le attese – Riutilizza modelli coerenti nella tua base di codice.

  5. Aggiungi resilienza – Implementare i nuovi tentativi e la gestione degli errori per tenere conto delle pagine dinamiche.

All'inizio l'attesa può sembrare noiosa. Ma investire in una solida logica di attesa ti ricompenserà con scraper affidabili e resilienti preparati per il web moderno.

Spero che questi modelli e suggerimenti distillati dai miei anni come specialista professionista del web scraping ti aiuteranno ad aspettare con successo. Rottami!

Partecipa alla conversazione

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati con *