Зачекайте, поки сторінка буде завантажена Selenium WebDriver для Python


181

Я хочу скребти всі дані сторінки, реалізованої нескінченним прокруткою. Наступний код python працює.

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

Це означає, що кожного разу, коли я прокручую донизу, мені потрібно почекати 5 секунд, що, як правило, достатньо, щоб сторінка закінчила завантаження новоствореного вмісту. Але це може бути не ефективно. Сторінка може закінчити завантаження нового вмісту протягом 5 секунд. Як я можу виявити, що сторінка закінчувала завантаження нового вмісту кожного разу, коли я прокручую вниз? Якщо я можу це виявити, я можу прокрутити вниз знову, щоб побачити більше вмісту, як тільки я знаю, що сторінка закінчується завантаженням. Це більш ефективно в часі.


1
Це може допомогти дізнатися трохи більше про сторінку. Є елементи послідовними чи передбачуваними? Можна дочекатися завантаження елементів, перевіривши видимість за допомогою id чи xpath
user2272115



Чи відповідає це на ваше запитання? Чекайте завантаження сторінки в Селен
Matej J

Відповіді:


234

webdriverЧекатиме завантаження сторінки за замовчуванням з допомогою .get()методу.

Оскільки ви, можливо, шукаєте якийсь конкретний елемент, як сказав @ user227215, вам слід WebDriverWaitзачекати на елементі, який знаходиться на вашій сторінці:

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

Я використовував це для перевірки оповіщень. Для пошуку локатора можна використовувати будь-які інші методи.

EDIT 1:

Слід зазначити, що webdriverза замовчуванням сторінка буде чекати завантаження сторінки. Він не чекає завантаження всередині кадрів або запитів ajax. Це означає, що коли ви користуєтесь .get('url'), ваш браузер зачекає, поки сторінка повністю не завантажиться, а потім перейде до наступної команди в коді. Але коли ви публікуєте запит на ajax, webdriverне чекайте, і ви зобов’язані чекати відповідну кількість часу для завантаження сторінки або частини сторінки; тому існує модуль з іменем expected_conditions.


3
Я отримую "find_element () аргумент після * повинен бути послідовністю, що не WebElement" змінений на "WebDriverWait (браузер, затримка) .until (EC.presence_of_element_located ((By.ID," IdOfMyElement ")))" наведено в керівництві selenium- python.readthedocs.org/en/latest/waits.html
fragles

2
Коментар @fragles та відповідь Девіда Каллена - це те, що працювало для мене. Можливо, цю прийняту відповідь можна було б відповідно оновити?
Майкл Ольрогге

6
Проходження browser.find_element_by_id('IdOfMyElement')викликає NoSuchElementExceptionпідняття a . Документація говорить передати кортеж , який виглядає наступним чином : (By.ID, 'IdOfMyElement'). Дивіться мою відповідь
Девід Каллен

2
Сподіваємось, це допомагає комусь іншому, тому що мені спочатку було незрозуміло: WebDriverWait фактично поверне веб-об’єкт, над яким потім можна виконати дію (наприклад click()), прочитати текст тощо. Я був під помилковим враженням, що це просто викликала очікування, після якого вам все-таки довелося знайти елемент. Якщо ви зачекаєте, тоді елемент пошуку після цього помилиться, але селен помилиться, оскільки він намагається знайти елемент, поки старе очікування ще обробляється (сподіваємось, це має сенс). Суть полягає в тому, що вам не потрібно знаходити елемент після використання WebDriverWait - це вже об’єкт.
Бен Вілсон

1
@Gopgop Нічого цього некрасивого, це не конструктивний коментар. Що в цьому негарного? Як це можна було зробити кращим?
Modus Tollens

72

Спроба перейти find_element_by_idдо конструктора для presence_of_element_located(як це показано у прийнятій відповіді ) викликала NoSuchElementExceptionпідвищення. Я повинен був використовувати синтаксис fragles ' коментар :

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"

Це відповідає прикладу в документації . Ось посилання на документацію для By .


2
Дякую! так, це було потрібно і для мене. Ідентифікатор - не єдиний атрибут, який можна використовувати, щоб отримати повний список, скористайтеся довідкою (По). Наприклад, я використовувавEC.presence_of_element_located((By.XPATH, "//*[@title='Check All Q1']"))
Майкл Олрогге

Ось так це працює і для мене! Я написав додаткову відповідь, розширюючи різні локатори, доступні разом із Byоб'єктом.
J0ANMM

Я відправив питання подальшу справу з очікуваннями , де різні сторінки можуть бути завантажені, і не завжди на тій же сторінці: stackoverflow.com/questions/51641546 / ...
Liquidgenius

48

Знайдіть нижче 3 способи:

readyState

Перевірка сторінки "Готова держава" (не надійно):

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'

wait_forДопоміжна функція хороша, але , до жаль , click_through_to_new_pageвідкритий до стану гонки , де нам вдається виконати сценарій в старій сторінці, перед тим, як браузер почав обробку клацання, і page_has_loadedпросто повертає істину відразу.

id

Порівняння ідентифікаторів нової сторінки зі старою:

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

Можливо, порівняння ідентифікаторів не є настільки ефективним, як очікування вичерпних винятків посилань.

staleness_of

Використовуючи 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))

Для отримання більш детальної інформації перегляньте блог Гаррі .


Чому ти кажеш, що self.driver.execute_script('return document.readyState;')не надійно? Здається, він ідеально підходить для мого використання, який чекає, коли статичний файл завантажиться на нову вкладку (яка відкривається через javascript на іншій вкладці замість .get ()).
Артур Геберт

1
@ArthurHebert Неможливо бути надійним через стан гонки, я додав відповідне цитування.
kenorb

23

Як згадується у відповіді Девіда Каллена , я завжди бачив рекомендації використовувати такий рядок:

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

Мені було важко знайти десь усі можливі локатори, якими можна скористатися By, тому я подумав, що було б корисно надати тут список. За даними веб-скрапінгу з Python Райана Мітчелла:

ID

Використовується в прикладі; знаходить елементи за їх атрибутом HTML id

CLASS_NAME

Використовується для пошуку елементів за атрибутом класу HTML. Чому ця функція CLASS_NAMEне просто CLASS? Використання форми object.CLASS створило б проблеми для бібліотеки Java Selenium, де .classце зарезервований метод. Для того, щоб синтаксис Selenium був узгодженим між різними мовами, CLASS_NAMEнатомість був використаний.

CSS_SELECTOR

Знаходить елементи їх класу, ідентифікатор або ім'я тега, використовуючи #idName, .className,tagName конвенцію.

LINK_TEXT

Знаходить теги HTML за текстом, який вони містять. Наприклад, посилання з написом "Далі" можна вибрати за допомогою (By.LINK_TEXT, "Next").

PARTIAL_LINK_TEXT

Схожий на LINK_TEXT, але відповідає на частковій струні.

NAME

Знаходить теги HTML за атрибутом імені. Це зручно для форм HTML.

TAG_NAME

Знаходить HTML-теги за назвою тегів.

XPATH

Використовує вираз XPath ... для вибору відповідних елементів.


5
Документація By перераховані атрибути , які можуть бути використані в якості локаторів.
Девід Каллен

1
Це я шукав! Дякую! Що ж, тепер слід простіше знайти, як Google надсилав мені це питання, але не до офіційної документації.
J0ANMM

Дякую за цитування з книги. Це набагато чіткіше документації.
ZygD


11

Зі сторони замість прокручування вниз 100 разів, ви можете перевірити, чи немає більше змін до DOM (у випадку, якщо внизу сторінки лежачи завантажені AJAX)

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

Це корисно. Однак що представляє 500? Чи достатньо великий, щоб дістатися до кінця сторінки?
Мондра

Це сума, яку повинна прокручувати сторінка ... ви повинні встановити її якомога вище. Щойно я дізнався, що цього номера мені вистачало, оскільки він змушує прокручувати сторінку донизу, поки елементи AJAX не ліниво завантажуються, що викликає необхідність знову завантажити сторінку
raffaem

Це допомагає при спробі забезпечити повне завантаження всіх коментарів до проблеми в gitlab.
bgStack15

7

Ви пробували driver.implicitly_wait. Це як налаштування для драйвера, тому ви викликаєте його лише один раз на сеансі, і це, в основному, каже драйверу почекати задану кількість часу, поки кожна команда може бути виконана.

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

Тож якщо ви встановите час очікування 10 секунд, воно виконає команду якомога швидше, чекаючи 10 секунд, перш ніж вона здасться. Я використовував це в подібних сценаріях прокрутки, тому не бачу, чому це не спрацює у вашому випадку. Сподіваюся, що це корисно.

Щоб мати можливість виправити цю відповідь, я повинен додати новий текст. Обов’язково використовуйте малі букви 'w' implicitly_wait.


Яка різниця між неявним очікуванням та webdriverwait?
song0089

4

Як щодо введення WebDriverWait у циклі "Хоча" та пошуку винятків.

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"

вам не потрібна петля?
Корі Голдберг

4

Тут я це зробив, використовуючи досить просту форму:

from selenium import webdriver
browser = webdriver.Firefox()
browser.get("url")
searchTxt=''
while not searchTxt:
    try:    
      searchTxt=browser.find_element_by_name('NAME OF ELEMENT')
      searchTxt.send_keys("USERNAME")
    except:continue

1

Ви можете зробити це дуже просто за допомогою цієї функції:

def page_is_loading(driver):
    while True:
        x = driver.execute_script("return document.readyState")
        if x == "complete":
            return True
        else:
            yield False

і коли ви хочете щось зробити після завершення завантаження сторінки, ви можете використовувати:

Driver = webdriver.Firefox(options=Options, executable_path='geckodriver.exe')
Driver.get("https://www.google.com/")

while not page_is_loading(Driver):
    continue

Driver.execute_script("alert('page is loaded')")
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.