Attendez que page soit chargée avec Selenium WebDriver pour Python

je veux gratter toutes les données d'une page implémentée par un scroll infini. Le code python suivant fonctionne.

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

cela signifie que chaque fois que je fais défiler vers le bas, j'ai besoin d'attendre 5 secondes, ce qui est généralement suffisant pour que la page termine de charger le contenu nouvellement généré. Mais, cela peut ne pas être efficace du temps. La page peut finir de charger le nouveau contenu en 5 secondes. Comment puis-je détecter si la page a fini de charger les nouveaux contenus chaque fois que je scroll vers le bas? Si je peux détecter cela, je peux faire défiler vers le bas à nouveau pour voir plus de contenus une fois que je sais que la page a terminé le chargement. C'est plus efficace.

84
demandé sur A-B-B 2014-10-26 00:14:30

8 réponses

le webdriver attendra qu'une page se charge par défaut via la méthode .get() .

comme vous pourriez être à la recherche d'un élément spécifique comme @user227215 l'a dit, vous devriez utiliser WebDriverWait pour attendre un élément situé dans votre page:

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

Je l'ai utilisé pour vérifier les alertes. Vous pouvez utiliser n'importe quelle autre méthode de type pour trouver le Localisateur.

EDIT 1:

I devrait mentionner que le webdriver attendra qu'une page se charge par défaut. Il n'attend pas le chargement à l'intérieur des cadres ni les requêtes ajax. Cela signifie que lorsque vous utilisez .get('url') , votre navigateur attendra jusqu'à ce que la page soit complètement chargée, puis passer à la commande suivante dans le code. Mais lorsque vous postez une demande ajax, webdriver n'attend pas et il est de votre responsabilité d'attendre un temps approprié pour que la page ou une partie de la page se charge; il y a donc un module nommé expected_conditions .

115
répondu Zeinab Abbasimazar 2017-07-17 02:21:44

en essayant de passer find_element_by_id au constructeur pour presence_of_element_located (comme montré dans la réponse acceptée ) causé NoSuchElementException pour être augmenté. J'ai dû utiliser la syntaxe dans fragles ' commentaire :

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"

ceci correspond à l'exemple dans la documentation . Voici un lien vers la documentation pour par .

45
répondu David Cullen 2017-05-23 12:10:08

trouver ci-dessous 3 méthodes:

readyState

page de contrôle readyState (pas fiable):

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'

la fonction d'aide wait_for est bonne, mais malheureusement click_through_to_new_page est ouvert à la condition de course où nous parvenons à exécuter le script dans l'ancienne page, avant que le navigateur a commencé à traiter le clic, et page_has_loaded retourne juste vrai tout de suite.

id

Comparant nouvelle page de codes avec l'ancien:

def page_has_loaded_id(self):
    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

il est possible que la comparaison des ids ne soit pas aussi efficace que d'attendre des exceptions de référence périmées.

staleness_of

utilisant staleness_of méthode:

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

pour plus de détails, consultez Harry's blog .

22
répondu kenorb 2018-04-03 09:40:01

de 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"))
14
répondu Carl 2017-06-12 13:43:23

comme mentionné dans la réponse de David Cullen , j'ai vu toujours recommandé d'utiliser une ligne comme la suivante:

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

il m'a été difficile de trouver n'importe où tous les locateurs possibles qui peuvent être utilisés avec la syntaxe By , donc j'ai pensé qu'il serait utile de fournir ici la liste. Selon grattage Web avec Python par Ryan Mitchell:

ID

utilisé dans l'exemple; trouve des éléments par leur attribut HTML id

CLASS_NAME

utilisé pour trouver des éléments par leur attribut de classe HTML. Pourquoi est-ce fonction CLASS_NAME pas simplement CLASS ? En utilisant le formulaire object.CLASS serait de créer problèmes pour la bibliothèque Java de Selenium, où .class est réservé méthode. Afin de garder la syntaxe du sélénium cohérente entre différentes langues, CLASS_NAME a été utilisé à la place.

CSS_SELECTOR

trouver les éléments par leur classe, id, ou nom d'étiquette, en utilisant le #idName , .className , tagName de la convention.

LINK_TEXT

trouve les balises HTML par le texte qu'elles contiennent. Par exemple, un lien qui dit " suivant "peut être sélectionné en utilisant (By.LINK_TEXT, "Next") .

PARTIAL_LINK_TEXT

Semblables LINK_TEXT , mais correspond à une chaîne partielle.

NAME

trouve les balises HTML par leur attribut name. C'est pratique pour les formulaires HTML.

TAG_NAME

trouve les balises HTML par leur nom de balise.

XPATH

utilise une expression XPath ... pour sélectionner les éléments correspondants.

11
répondu J0ANMM 2017-05-23 12:10:08

sur une note latérale, au lieu de défiler 100 fois vers le bas, vous pouvez vérifier s'il n'y a plus de modifications au DOM (nous sommes dans le cas du bas de la page étant AJAX paresseux-chargé)

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
4
répondu raffamaiden 2017-07-09 16:18:52

Que Diriez-vous de mettre WebDriverWait tout en boucle et d'attraper les exceptions.

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"
2
répondu Rao 2017-05-08 06:44:07

avez-vous essayé driver.implicitly_wait . C'est comme un paramètre pour le pilote, donc vous ne l'appelez qu'une fois dans la session et il indique au pilote d'attendre le temps donné jusqu'à ce que chaque commande puisse être exécutée.

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

Donc, si vous définissez un temps d'attente de 10 secondes, il exécutera la commande dès que possible, attendre 10 secondes avant d'abandonner. J'ai utilisé ça dans des scénarios similaires, donc je ne vois pas pourquoi ça ne marcherait pas dans votre cas. Espérer ceci est utile :)

0
répondu seeiespi 2018-05-13 04:36:48