Poczekaj, aż strona zostanie załadowana za pomocą Selenium WebDriver for Python

Chcę zeskrobać wszystkie dane strony zaimplementowanej przez nieskończony zwój. Działa następujący kod Pythona.

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

Oznacza to, że za każdym razem, gdy przewijam w dół do dołu, muszę odczekać 5 sekund, co generalnie wystarczy, aby Strona zakończyła Ładowanie nowo Wygenerowanej zawartości. Ale to może nie być efektywne czasowo. Strona może zakończyć ładowanie nowej zawartości w ciągu 5 sekund. Jak mogę sprawdzić, czy strona zakończyła Ładowanie nowej zawartości za każdym razem, gdy przewijam w dół? If I mogę to wykryć, mogę ponownie przewijać w dół, aby zobaczyć więcej treści, gdy tylko dowiem się, że strona została załadowana. To jest bardziej efektywne czasowo.

Author: A-B-B, 2014-10-26

8 answers

webdriver będzie czekać na domyślnie załadowaną stronę za pomocą metody .get().

Ponieważ być może szukasz jakiegoś konkretnego elementu, jak powiedział @user227215, powinieneś użyć WebDriverWait, aby poczekać na element znajdujący się na twojej stronie:

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

Użyłem go do sprawdzania alertów. Możesz użyć innych metod typu, aby znaleźć lokalizator.

Edytuj 1:

Powinienem wspomnieć, że webdriver będzie domyślnie czekać na załadowanie strony. Nie czeka na załadowanie wewnątrz ramek ani na żądania ajax. Oznacza to, że gdy używasz .get('url'), twoja przeglądarka będzie czekać, aż strona zostanie całkowicie załadowana, a następnie przejść do następnego polecenia w kodzie. Ale kiedy publikujesz żądanie ajax, webdriver nie czeka i Twoim obowiązkiem jest poczekać odpowiednią ilość czasu na załadowanie strony lub części strony; więc istnieje moduł o nazwie expected_conditions.

 115
Author: Zeinab Abbasimazar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-17 02:21:44

Próba przekazania find_element_by_id konstruktorowi dla presence_of_element_located (Jak pokazano wzaakceptowanej odpowiedzi ) spowodowała podniesienie NoSuchElementException. Musiałem użyć składni w fragles' komentarz :

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"

Pasuje do przykładu w dokumentacji . Oto link do dokumentacji Dla By.

 45
Author: David Cullen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:08

Znajdź poniżej 3 metody:

ReadyState

Sprawdzanie czytelności strony (nie wiarygodne):

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'

Funkcja helpera wait_for jest dobra, ale niestety click_through_to_new_page jest otwarta do stanu race, w którym udaje nam się wykonać skrypt na starej stronie, zanim przeglądarka zacznie przetwarzać kliknięcie i page_has_loaded od razu zwraca true.

id

Porównywanie nowych identyfikatorów stron ze starymi:

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

It ' s possible porównywanie identyfikatorów nie jest tak skuteczne, jak oczekiwanie na przestarzałe wyjątki referencyjne.

staleness_of

Stosując metodę staleness_of:

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

[[9]}aby uzyskać więcej informacji, sprawdź Harry ' s blog .
 22
Author: kenorb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-03 09:40:01

Z 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
Author: Carl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-12 13:43:23

Jak wspomniano w odpowiedź od Davida Cullena , widziałem zawsze zalecane użycie linii jak następujący:

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

Trudno mi było znaleźć wszędzie wszystkie możliwe lokalizatory, które mogą być używane ze składnią By, więc pomyślałem, że przydałoby się podać tutaj listę. Według Scrapowanie stron z Pythonem autor: Ryan Mitchell:

ID

Użyte w przykładzie; wyszukuje elementy przez atrybut HTML id

CLASS_NAME

Używane do znajdowania elementów za pomocą atrybutu klasy HTML. Dlaczego to Funkcja CLASS_NAME nie po prostu CLASS? Korzystanie z formularza object.CLASS tworzy problemy dla Biblioteki Javy Selenium, gdzie .class jest metoda zarezerwowana. W celu zachowania spójności składni selenu pomiędzy różnymi językami zamiast tego użyto CLASS_NAME.

CSS_SELECTOR

Znajdź elementy według ich klasy, id lub nazwa znacznika, używając #idName, .className, tagName zjazd.

LINK_TEXT

Znajduje znaczniki HTML po tekście, który zawierają. Na przykład link, który mówi "Next" można wybrać za pomocą (By.LINK_TEXT, "Next").

PARTIAL_LINK_TEXT

Podobny do LINK_TEXT, ale pasuje do częściowego ciągu.

NAME

Znajduje znaczniki HTML według ich atrybutu name. Jest to przydatne dla HTML formularze.

TAG_NAME

Fins tagów HTML według ich nazwy tagu.

XPATH

Używa wyrażenia XPath ... aby wybrać pasujące elementy.

 11
Author: J0ANMM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:08

Na marginesie, zamiast przewijać 100 razy w dół, Możesz sprawdzić, czy nie ma już żadnych modyfikacji w DOM (jesteśmy w przypadku, gdy dół strony jest AJAX Lazy-loaded)

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
Author: raffamaiden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-09 16:18:52

Jak o umieszczenie WebDriverWait w pętli While I przechwytywanie WYJĄTKÓW.

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
Author: Rao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-08 06:44:07

Czy próbowałeś driver.implicitly_wait. Jest to jak ustawienie dla sterownika, więc wywołujesz go tylko raz w sesji i w zasadzie mówi sterownikowi, aby poczekał określoną ilość czasu, aż każde polecenie może być wykonane.

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

Więc jeśli ustawisz czas oczekiwania na 10 sekund, wykonasz polecenie tak szybko, jak to możliwe, czekając 10 sekund, zanim się podda. Używałem tego w podobnych scenariuszach przewijania w dół, więc nie rozumiem, dlaczego miałoby to nie działać w Twoim przypadku. Mam nadzieję, że to pomoże:)

 0
Author: seeiespi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-13 04:36:48