2014-10-25 2 views
61

Voglio raschiare tutti i dati di una pagina implementata da uno scroll infinito. Il seguente codice Python funziona.Come aspettare che la pagina venga caricata con Selenium per Python?

for i=1:100 
    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") 
    time.sleep(5) 

Questo significa che ogni volta che ho scorrere fino in fondo, ho bisogno di aspettare 5 secondi, che è in genere sufficiente per la pagina per completare il caricamento dei contenuti appena generato. Ma questo potrebbe non essere efficiente nel tempo. La pagina potrebbe terminare il caricamento dei nuovi contenuti entro 5 secondi. Come posso rilevare se la pagina ha finito di caricare i nuovi contenuti ogni volta che si scorre verso il basso? Se riesco a rilevare questo, posso scorrere di nuovo verso il basso per vedere più contenuti una volta che so che la pagina ha terminato il caricamento. Questo è più efficiente nel tempo.

+0

Potrebbe essere utile conoscere un po 'di più sulla pagina. Gli elementi sono sequenziali o prevedibili? Puoi aspettare che gli elementi vengano caricati controllando visiblity usando id o xpath – user2272115

+0

Sto analizzando la seguente pagina: http://www.pinterest.com/cremedelacrumb/yum/ – apogne

+0

possibile duplicato di [Rileva attendibilmente il carico o il timeout della pagina, selenio 2] (http://stackoverflow.com/questions/18729483/reliably-detect-page-load-or-time-out-selenium-2) – kenorb

risposta

82

webdriver attenderà il caricamento di una pagina per impostazione predefinita tramite il metodo .get().

Come si può essere alla ricerca di qualche elemento specifico come @ user227215 Detto questo, è necessario utilizzare WebDriverWait attendere per un elemento che si trova nella pagina:

from selenium import webdriver 
from selenium.webdriver.support.ui import WebDriverWait 
from selenium.webdriver.support import expected_conditions as EC 
from selenium.webdriver.common.by import By 
from selenium.common.exceptions import TimeoutException 

browser = webdriver.Firefox() 
browser.get("url") 
delay = 3 # seconds 
try: 
    myElem = WebDriverWait(browser, delay).until(EC.presence_of_element_located((By.ID, 'IdOfMyElement'))) 
    print "Page is ready!" 
except TimeoutException: 
    print "Loading took too much time!" 

L'ho usato per controllare gli avvisi. È possibile utilizzare qualsiasi altro metodo di tipo per trovare il localizzatore.

EDIT 1:

Devo dire che il webdriver attenderà per una pagina per caricare per impostazione predefinita. Non aspetta il caricamento all'interno di frame o per richieste Ajax. Significa che quando usi .get('url'), il tuo browser attenderà che la pagina sia completamente caricata e poi passerà al comando successivo nel codice. Ma quando stai postando una richiesta Ajax, webdriver non aspetta ed è tua responsabilità attendere una quantità di tempo appropriata per caricare la pagina o una parte della pagina; quindi c'è un modulo chiamato expected_conditions.

+0

Che cos'è "IdOfMyElement"? È qualcosa che dovrei prevedere come l'indice di qualcosa verrà caricato di recente? Ad esempio, desidero eseguire la scansione della seguente pagina: http://www.pinterest.com/cremedelacrumb/yum/ – apogne

+0

Dovresti trovare un elemento nella tua pagina che sei sicuro che esista sempre nella pagina. "IdOfMyElement" si riferisce all'ID di un elemento nella pagina; se non possiede un ID, puoi usare qualsiasi altro tipo di localizzatore, come 'xpath'. –

+0

Penso che non dovrebbe essere qualcosa sempre esistente. Dovrebbe essere qualcosa che verrà caricato di nuovo una volta scorrendo verso il basso. Ho ragione? Ad esempio, puoi dirmi qual è questo elemento della pagina che ho detto prima? – apogne

18

seguito troverete 3 metodi:

Verifica dei readyState (non attendibile):

def page_has_loaded(self): 
    self.log.info("Checking if {} page is loaded.".format(self.driver.current_url)) 
    page_state = self.driver.execute_script('return document.readyState;') 
    return page_state == 'complete' 

Confrontando nuovi ID di pagina con la precedente:

def page_has_loaded2(): 
    self.log.info("Checking if {} page is loaded.".format(self.driver.current_url)) 
    try: 
     new_page = browser.find_element_by_tag_name('html') 
     return new_page.id != old_page.id 
    except NoSuchElementException: 
     return False 

Utilizzando staleness_of metodo:

@contextlib.contextmanager 
def wait_for_page_load(self, timeout=10): 
    self.log.debug("Waiting for page to load at {}.".format(self.driver.current_url)) 
    old_page = self.find_element_by_tag_name('html') 
    yield 
    WebDriverWait(self, timeout).until(staleness_of(old_page)) 

Per ulteriori dettagli, selezionare Harry's blog.

36

Cercando di passare find_element_by_id al costruttore per presence_of_element_located (come mostrato nello accepted answer), è necessario generare NoSuchElementException. Ho dovuto usare la sintassi in fragles 'comment:

from selenium import webdriver 
from selenium.common.exceptions import TimeoutException 
from selenium.webdriver.support.ui import WebDriverWait 
from selenium.webdriver.support import expected_conditions as EC 
from selenium.webdriver.common.by import By 

driver = webdriver.Firefox() 
driver.get('url') 
timeout = 5 
try: 
    element_present = EC.presence_of_element_located((By.ID, 'element_id')) 
    WebDriverWait(driver, timeout).until(element_present) 
except TimeoutException: 
    print "Timed out waiting for page to load" 

Questo corrisponde al example in the documentation. Ecco un collegamento allo documentation for By.

+1

Grazie! sì, questo era necessario anche per me. L'ID non è l'unico attributo che può essere utilizzato, per ottenere l'elenco completo, usa l'aiuto (By). Per esempio. Ho usato 'EC.presence_of_element_located ((By.XPATH," // * [@ title = 'Controlla tutto Q1'] "))' –

+0

Questo è il modo in cui funziona anche per me! Ho scritto un ulteriore [risposta] (http://stackoverflow.com/a/40037216/5802289) espandendo i vari locatori disponibili con l'oggetto 'By'. – J0ANMM

8

Come accennato nella answer from David Cullen, ho visto sempre consigliabile utilizzare una linea come la seguente:

element_present = EC.presence_of_element_located((By.ID, 'element_id')) 
    WebDriverWait(driver, timeout).until(element_present) 

E 'stato difficile per me trovare ovunque tutti i possibili indicatori di posizione che possono essere utilizzati con la sintassi By , quindi ho pensato che sarebbe stato utile fornire qui la lista. Secondo Web Scraping with Python da Ryan Mitchell:

ID

utilizzati nell'esempio; trova gli elementi per la loro id attributo HTML

CLASS_NAME

Utilizzato per trovare gli elementi per la loro attributo di classe HTML. Perché questa funzione CLASS_NAME non è semplicemente CLASS? L'utilizzo del modulo object.CLASS creerebbe problemi per la libreria Java di Selenium, dove .class è un metodo riservato . Al fine di mantenere costante la sintassi del selenio tra diverse lingue, è stato invece utilizzato CLASS_NAME.

CSS_SELECTOR

Trova elementi per la loro classe, id, o il nome di tag, utilizzando la convenzione #idName, .className, tagName.

LINK_TEXT

Finds tag HTML dal testo che contengono. Ad esempio, un collegamento che dice "Successivo" può essere selezionato utilizzando (By.LINK_TEXT, "Next").

PARTIAL_LINK_TEXT

Simile a LINK_TEXT, ma corrisponde a una stringa parziale.

NAME

Finds tag HTML per il loro attributo nome. Questo è utile per i moduli HTML.

TAG_NAME

Pinne tag HTML con il loro nome tag.

XPATH

utilizza un'espressione XPath ... per selezionare gli elementi corrispondenti.

+3

La [documentazione per By] (http://selenium-python.readthedocs.io/api.html?highlight=#module-selenium.webdriver.common.by) elenca gli attributi che possono essere usati come localizzatori. –

+0

Era quello che stavo cercando! Grazie! Bene, ora dovrebbe essere più facile trovare come google mi stava mandando a questa domanda, ma non alla documentazione ufficiale. – J0ANMM

7

Da selenium/webdriver/support/wait.py

driver = ... 
from selenium.webdriver.support.wait import WebDriverWait 
element = WebDriverWait(driver, 10).until(
    lambda x: x.find_element_by_id("someId")) 
1

ne dici di mettere in WebDriverWait While e cattura le eccezioni.

from selenium import webdriver 
from selenium.webdriver.support.ui import WebDriverWait 
from selenium.webdriver.support import expected_conditions as EC 
from selenium.common.exceptions import TimeoutException 

browser = webdriver.Firefox() 
browser.get("url") 
delay = 3 # seconds 
while True: 
    try: 
     WebDriverWait(browser, delay).until(EC.presence_of_element_located(browser.find_element_by_id('IdOfMyElement'))) 
     print "Page is ready!" 
     break # it will break from the loop once the specific element will be present. 
    except TimeoutException: 
     print "Loading took too much time!-Try again" 
1

Una nota a parte, invece di scorrere verso il basso 100 volte, è possibile controllare se non ci sono più modifiche al DOM (siamo nel caso della parte inferiore della pagina essendo AJAX pigro-caricato)

def scrollDown(driver, value): 
    driver.execute_script("window.scrollBy(0,"+str(value)+")") 

# Scroll down the page 
def scrollDownAllTheWay(driver): 
    old_page = driver.page_source 
    while True: 
     logging.debug("Scrolling loop") 
     for i in range(2): 
      scrollDown(driver, 500) 
      time.sleep(2) 
     new_page = driver.page_source 
     if new_page != old_page: 
      old_page = new_page 
     else: 
      break 
    return True 
+0

Questo è utile. Tuttavia cosa rappresenta il 500? È abbastanza grande per arrivare alla fine della pagina? – Moondra

+0

L'importo della pagina dovrebbe scorrere ... dovresti impostarlo il più in alto possibile. Ho appena scoperto che questo numero era abbastanza per me, dal momento che fa scorrere la pagina fino in fondo fino a quando gli elementi AJAX non sono caricati a fatica, stimolando la necessità di ricaricare nuovamente la pagina – raffamaiden