Meteen naar de inhoud

Hoe kan ik wachten tot de pagina in Selenium is geladen? Een deskundige gids

Laat me raden: u bent begonnen met het scrapen van een site met Selenium en plotseling wordt u geconfronteerd met gevreesde time-outfouten, verouderde elementuitzonderingen en schilferige locators. Klinkt bekend?

Velen van ons zijn er geweest! In het huidige dynamische internet is het van cruciaal belang voor betrouwbare automatisering dat u goed wacht tot pagina's volledig zijn geladen voordat u interactie aangaat.

In deze uitgebreide gids van meer dan 3200 woorden zal ik mijn meer dan vijf jaar als professionele webscraping-expert benutten om de verschillende methoden en best practices voor gracieus wachten in Selenium te verkennen.

Of u nu net begint of een doorgewinterde professional bent, robuuste wachtlogica is een onmisbare tool voor stabiliteit. Laten we erin duiken!

Waarom je niet zomaar naar binnen kunt haasten

In de begindagen van het web waren pagina's meestal eenvoudige HTML die opeenvolgend werd weergegeven. Scrapers kunnen onmiddellijk beginnen met extraheren wanneer de pagina wordt geladen.

Maar het internet van vandaag is zeer dynamisch. Volgens Google-onderzoekis de gemiddelde tijd tot de eerste keer schilderen 1.7 seconde, maar de gemiddelde tijd tot volledig interactief is maar liefst 15 seconden. Dat is veel tijd voordat de inhoud wordt geladen.

Als schraper: als je te snel naar binnen haast, zijn hier enkele veelvoorkomende problemen waarmee je te maken kunt krijgen:

  • Klikfouten op de knop omdat het element nog niet is weergegeven
  • Er wordt geprobeerd gegevens te lezen uit een tabel waarin geen serverinhoud is geladen
  • Tekst verzenden naar een ingang die niet zichtbaar is op het scherm
  • Lege elementen schrapen die worden gevuld nadat de pagina is geladen

Dit soort uitzonderingen zijn symptomen die erop wijzen dat u langer moet wachten totdat de pagina gereed is voordat u interactie kunt uitvoeren.

In cijfers: laadtijden van pagina's

Laten we, om te begrijpen hoe lang we mogelijk moeten wachten, eens kijken naar enkele praktijkgegevens over de laadprestaties van pagina's uit de 2020 State of the Web-rapport door Akamai:

  • Mediane tijd tot interactief: 15s
  • Gemiddeld paginagewicht: 2744KB
  • Gemiddeld aantal verzoeken: 105
  • Gemiddelde afbeeldingen per pagina: 53
  • JavaScript-bytes per pagina: 453KB

Pagina's zijn tegenwoordig groter en complexer, en er gebeurt veel meer werk na de eerste reactie. Het is van cruciaal belang dat schrapers wachten op interactiviteit, niet alleen op de eerste verf.

Veel voorkomende uitzonderingen veroorzaakt door niet wachten

Hier volgen enkele specifieke uitzonderingen die kunnen optreden als elementen nog niet klaar zijn:

  • StaleElementReferenceException – Element verwijderd uit DOM na ophalen
  • ElementNotInteractableException – Proberen op een onzichtbaar element te klikken
  • Geen DergelijkElementUitzondering – Er is een time-out opgetreden bij het opzoeken omdat het element nog niet bestaat

Elk van deze geeft aan dat de schraper langer moet wachten.

Expliciete wachttijden zijn je vriend

Om deze fouten te voorkomen, moeten we wachten tot de pagina volledig is weergegeven voordat we interactie kunnen uitvoeren. Er zijn twee hoofdbenaderingen in Selenium:

Impliciete wachttijden – Stel een globale wachttijd in voor de chauffeur

Expliciete wachttijden – Wacht tot er specifieke omstandigheden optreden

In de meeste gevallen heeft een expliciete wachttijd veel de voorkeur boven een impliciete wachttijd. Laten we begrijpen waarom.

Impliciet wachten: de Sledgehammer-aanpak

Impliciete wachttijden stellen een time-out in voor het stuurprogramma om de DOM te ondervragen bij het vinden van elementen. Dit betekent dat elke keer dat u belt:

driver.find_element_by_id(‘some-id‘)

Het stuurprogramma zal opnieuw proberen tot aan de impliciete wachttijd om dat element te lokaliseren voordat een NoSuchElementException wordt gegenereerd.

Je zou het kunnen gebruiken als:

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

Nu zullen alle zoekopdrachten gedurende maximaal 10 seconden opnieuw proberen om elementen te vinden die niet onmiddellijk aanwezig zijn.

Het nadeel is dat er op elke locator wordt gewacht, zelfs op degene die niet nodig zijn om de paginagereedheid te bepalen. Dit kan uw schraper behoorlijk vertragen.

Denk aan impliciet wachten, zoals het toevoegen van een slaap van 5 seconden aan elk ophalen van elementen. Het klopt!

De precisie van expliciet wachten

Door expliciet te wachten kunnen we nauwkeurig wachten op specifieke omstandigheden die erop wijzen dat we gereed zijn voordat we verder gaan.

De belangrijkste ideeën zijn:

  • Wacht alleen wanneer dat nodig is – Vermijd onnodig wachten dat niets te maken heeft met de gereedheid van de pagina
  • Nauwkeurige omstandigheden – Wacht op exacte elementen of toestanden, niet alleen op algemene tijd
  • Flexibiliteit – Pas de wachtlogica per pagina aan met verschillende voorwaarden
  • leesbaar – Gemakkelijk te begrijpen bedoeling bij het opnieuw bekijken van oude code

Hier is een typisch voorbeeld van wachten tot een element verschijnt:

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"))
)

Hierdoor wordt de uitvoering gepauzeerd totdat het element met ID "myDynamicElement" wordt geladen, of totdat er 10 seconden verstrijken.

Andere nuttige verwachte omstandigheden aangeboden door Selenium omvatten:

  • title_contains() – Wacht tot de paginatitel is bijgewerkt
  • staleness_of() – Wacht tot het element niet langer aan DOM is gekoppeld
  • element_to_be_clickable() – Wacht tot het element zichtbaar en ingeschakeld is

Expliciet verslaat impliciet: een voorbeeld uit de echte wereld

Laten we de twee wachttijden vergelijken met een echt voorbeeld.

Stel dat ik een site aan het scrapen ben met een navigatiebalk, een linkerzijpaneel en hoofdinhoud.

Het belangrijkste element waar ik op moet wachten is een ID "#main-content" waar mijn gegevens worden weergegeven.

Met impliciet wachten:

  • Er worden 10 seconden toegevoegd aan het opzoeken van elk element, zelfs als dit niet nodig is
  • Nog steeds gevoelig voor oude elementfouten als het te snel is

Met expliciet wachten:

  • Wacht alleen wanneer dat nodig is voor de #main-content selector
  • Voorkom onnodig wachten op het navigatiesysteem en het zijpaneel
  • Wacht specifiek tot de gegevens zijn geladen voordat u doorgaat

Door selectief te wachten op een enkele gereedheidsvoorwaarde zoals een element, voorkom ik onnodige vertragingen.

Patronen voor effectief expliciet wachten

Nu u ervan overtuigd bent dat expliciet wachten de juiste keuze is, gaan we een aantal best practices bekijken om deze effectief te gebruiken.

Pagina laden wacht

Wachten op de status Document gereed is een veelgebruikte techniek om te bepalen wanneer het laden is voltooid:

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

Hiermee wordt de browser ondervraagd totdat de gereedstatus "voltooid" is, wat aangeeft dat alle middelen zijn geladen.

Een lichter patroon let op specifieke elementen op hoog niveau:

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

Dit lukt wanneer de hoofdinhoud wordt geladen, zonder op al het andere te wachten.

Wachttijden per actie

Je kunt ook wachten voordat je een actie onderneemt, bijvoorbeeld door op een element te klikken:

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()

Dit zorgt ervoor dat zowel het hoofdmenu als het submenu gereed zijn voordat u klikt.

Parallelle wachttijden

Wachten op meerdere voorwaarden kan bevestigen dat de pagina klaar is:

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"))
)

Door te vereisen dat de kop-, voettekst en hoofdinhoud worden geladen, worden valse positieven verminderd.

Geketende en geneste wachttijden

Voor geavanceerde scenario's kunt u ook wachttijden nesten:

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"))  
)

Dit wacht eerst op een ouderelement en vervolgens op een kindelement daarbinnen.

AJAX-peiling wacht

Sommige sites worden geladen via continue AJAX-verzoeken. U kunt wachten op wijzigingen herhalen:

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!

Hiermee wordt een element ondervraagd op zoek naar wijzigingen om het laden te detecteren.

Asynchrone wachttijden

In asynchrone raamwerken zoals asyncio kun je op beloften wachten:

await page.waitForSelector(‘#content‘)

De syntaxis is iets anders, maar biedt asynchroon wachten.

Impliciete + expliciete combinatie

Je kunt zelfs zowel impliciete als expliciete wachttijden combineren:

driver.implicitly_wait(10) 

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

Op deze manier heeft u zowel een globale als een specifieke wachttijd. Zorg ervoor dat ze een redelijke duur hanteren.

Zoeksystemen kiezen die gereedheid signaleren

Wanneer u locators selecteert waarop u moet wachten, wilt u elementen die aan deze criteria voldoen:

  • Verschijnen laat in het laadproces
  • Zorg voor unieke ID's of klassen die niet veranderen
  • Gelegen boven de vouw voor snelle controles
  • Het is onwaarschijnlijk dat ze door locatiewijzigingen zullen worden verplaatst
  • Laat je niet uit de DOM verwijderen en oud worden

Enkele veel voorkomende voorbeelden zijn:

  • Hoofdkop of navigatie geladen na activa
  • Primaire inhoudcontainers of widgets
  • Pagina ondertekst
  • Kleine dynamische UI-elementen zoals knoppen

Belastingsindicatoren zoals spinners zijn ook geweldige wachttriggers wanneer ze verdwijnen.

Time-outs afstemmen voor optimaal wachten

Als u een te lange time-out instelt, kan dit uw schraper erg vertragen, maar een te korte time-out kan tot haperende storingen leiden.

Hier volgen enkele praktische tips voor het afstemmen van de duur:

  • Stel de time-outs voor het laden van pagina's langer in, ongeveer 10-20 seconden.
  • Gebruik kortere time-outs, zoals 3-5 seconden, voor individuele elementen.
  • Houd rekening met de browserprestaties, mobiel versus desktop.
  • Houd rekening met netwerklatentie, breedband versus 3G.
  • Controleer op time-outfouten en pas indien nodig hoger aan.
  • Analyseer de waterval van het laden van pagina's voor typische laadtijden.
  • Budget 1-2 seconden extra als buffer.
  • Standaardiseer vergelijkbare wachttijden in uw codebase.

Naarmate u meer pagina's schrapt, krijgt u een betere intuïtie over optimale wachttijden voor betrouwbaarheid.

Wacht- en time-outfouten afhandelen

Zelfs als u lang moet wachten, kunt u nog steeds af en toe een time-out tegenkomen. Hier zijn enkele manieren om ermee om te gaan:

  • Foutopsporingsdetails loggen – Door afdrukken toe te voegen, kunt u vaststellen waar wachttijden mislukken.
  • Probeer het opnieuw na een time-out – Probeer korte, expliciete wachttijden tot drie keer opnieuw bij een fout.
  • Verhoog de time-out – Als er veel time-outs optreden, verhoogt u de wachttijden stapsgewijs.
  • Gebruik try/except – Specifieke uitzonderingen opvangen, zoals StaleElementReference.
  • Uitschakelen bij falen – U kunt wachttijden na herhaalde mislukkingen overslaan, zodat de tests kunnen doorgaan.

Met enige ingebouwde veerkracht zullen deze sporadische problemen uw schraper niet kapot maken.

Wachten in andere talen

Tot nu toe waren de voorbeelden in Python, maar expliciete wachttijden zijn beschikbaar in alle talen:

  • Java - WebDriverWait en ExpectedConditions
  • C# - WebDriverWait en ExpectedConditions
  • Ruby - WebDriver::Wait en ExpectedConditions
  • JavaScript - browser.wait() en nutsmethoden

De concepten lijken erg op elkaar; alleen de syntaxis verschilt enigszins.

Beyond Selenium: meer wachthulpmiddelen

Er zijn ook enkele andere nuttige wachtbibliotheken buiten Selenium:

  • Tijd - time.sleep() is eenvoudig maar pauzeert alle uitvoering.
  • Opnieuw proberen - The Probeer het pakket opnieuw maakt nieuwe pogingen en wachten eenvoudig.
  • Aiohttp - await response.text() wacht tot netwerkoproepen voltooid zijn.
  • Mooie soep - BeautifulSoup(page.content, features="lxml") zal wachten op volledige parsering.
  • Scrapy - yield scrapy.Request(url, callback=self.parse) is asynchroon.

Door deze te combineren met Selenium zorgt u voor robuuste wachttijden in uw code.

Samengevat: wacht goed en schrap betrouwbaar

Tot slot zijn hier vijf belangrijke conclusies:

  1. Gebruik expliciete wachttijden – Ze vermijden onnodige time-outs en richten zich op specifieke omstandigheden.

  2. Wacht op meerdere signalen – Combineer wachttijden voor koptekst, hoofdtekst, voettekst, enz. om de gereedheid van de pagina te bevestigen.

  3. Stem time-outs verstandig af – Stel waarden in op basis van gegevens over het laden van pagina's uit de echte wereld om vertragingen te optimaliseren.

  4. Standaardiseer wachttijden – Hergebruik consistente patronen in uw codebase.

  5. Voeg veerkracht toe – Implementeer nieuwe pogingen en afhandeling van fouten om rekening te houden met dynamische pagina's.

Wachten kan in eerste instantie vervelend lijken. Maar investeren in robuuste wachtlogica zal u belonen met betrouwbare, veerkrachtige schrapers die zijn voorbereid op het moderne internet.

Hopelijk zullen deze patronen en tips, gedestilleerd uit mijn jaren als professionele webscraping-specialist, je helpen succesvol te wachten. Schrap maar!

Doe mee aan het gesprek

Uw e-mailadres wordt niet gepubliceerd. Verplichte velden zijn gemarkeerd *