. как Selenium ожидает завершения загрузки страницы?
В предыдущей статье я объяснил, что означает с технической точки зрения “завершение загрузки страницы”.
Повторю вкратце основную мысль: Selenium (и, я думаю, большинство других инструментов тоже) использует свойство document.readyState для определения момента окончания загрузки страницы.
А вот как именно он это делает – сейчас расскажу. Точнее говоря, не “как”, а “когда”.
На первый взгляд всё кажется просто.
Свойство document.readyState может принимать всего три различных значения:
- loading означает, что страница находится в процессе загрузки
- interactive означает, что основная часть страницы загрузилась, пользователь может с ней взаимодействовать, но ещё продолжается загрузка дополнительных ресурсов (например, картинок)
- complete означает, что страница “полностью загружена”
Выполняем команду get, чтобы открыть в браузере страницу с указанным адресом, после чего ждём, когда свойство document.readyState приобретёт значение complete.
Если немного подумать – вспоминается команда click, выполняющая проход по ссылке. После неё тоже надо ожидать завершения загрузки страницы.
Но и этим дело не ограничивается.
Новая страница может начать загружаться после любого действия пользователя.
На любое действие пользователя (не только на клик, но и, например, нажатие клавиши или наведение мыши на какой-то элемент) может быть зарегистрирован JavaScript-обработчик, который может активировать загрузку новой страницы. Выходит, что после любой команды нужно пытаться подождать, не загрузится ли новая страница? Выходит, так.
Иногда страница перезагружается сама собой, по таймауту, даже если не выполнялось никаких действий.
Как Selenium может отследить это? Нереально…
Разрабочики Selenium нашли остроумное решение.
Да, для выполнения какого-либо действия на странице нужно, чтобы она была загружена, то есть свойство document.readyState приобрело нужное значение. Но вовсе необязательно ждать этого после выполнения команды. Гораздо лучше ждать перед выполнением команды.
Перед выполнением каждой команды Selenium ждёт, пока свойство document.readyState примет нужное значение.
Например, выполняется команда get, начинает загружаться страница. Но Selenium не ждёт завершения загрузки, вместо этого он достаточно быстро сообщает, что команда get успешно выполнена.
После этого мы пытаемся найти на странице какой-нибудь элемент, чтобы совершить с ним какое-нибудь действие, то есть выполняем команду findElement. В этот момент Selenium замечает, что свойство document.readyState ещё не приобрело значение complete, то есть страница всё ещё загружается. И тогда он приостанавливает выполнение команды поиска элемента и ждёт, пока страница догрузится.
Таким образом, выполняя ту или иную команду, Selenium вообще не заботится о том, будет после этого загружаться новая страница или нет. Если будет – разберёмся с этим перед выполнением следующей команды.
P.S. А зачем котик на картинке, которая ссылается на эту статью? Ну вот такие у меня ассоциации 🙂
Selenium, как кот в засаде, ждёт, когда браузер “расслабится” – и в этот момент набрасывается на него и выполняет команду!
Автор: Алексей Баранцев
Если вам понравилась эта статья, вы можете поделиться ею в социальных сетях (кнопочки ниже), а потом вернуться на главную страницу блога и почитать другие мои статьи.
Ну а если вы не согласны с чем-то или хотите что-нибудь дополнить – оставьте комментарий ниже, может быть это послужит поводом для написания новой интересной статьи.
Как дождаться с помощью Python полной загрузки страницы?
Есть список урлов, которые нужно обойти и собрать нужные элементы. Но иногда при проходе по этим урлам, некоторые из них как-будто не загружаются полностью и программа переходит к следующему урлу. Соответственно, нужные элементы не находятся, но они точно есть. Программа всегда по разному выполняется, иногда все ссылки открываются как надо, иногда рандомно с какой-то из них происходит такая ситуация. Подскажите, пожалуйста, как добавить обязательное условие, проверяющее загрузилась ли полностью страница перед поиском нужного элемента? Например код:
from selenium import webdriver from bs4 import BeautifulSoup driver = webdriver.Chrome(executable_path='C:\\..\chromedriver.exe') url = ['http://www.yandex.ru/', 'https://www.google.com/'] for i in url: driver.get(i) time.sleep(2) html = driver.find_element_by_xpath("//div[@id='content-all']").get_attribute("innerHTML")
1 ответ 1
1. Задавайте явное ожидание элемента, например:
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.Chrome(executable_path='C:\\..\chromedriver.exe') driver.get('https://ya.ru') timeout = 5 try: # Вместо body возможно нужен другой тег указывать, характерный для сайта element_present = EC.presence_of_element_located((By.TAG_NAME, 'body')) WebDriverWait(driver, timeout).until(element_present) except TimeoutException: print("Timed out waiting for page to load")
Можно разные условия ожидания элемента ставить (например, visibility_of_element_located ), смотрите в expected_conditions as EC и разные способы поиска, например по атрибуту id , смотрите в By :
element_present = EC.presence_of_element_located((By.ID, 'element_id'))
2. Можно задавать через set_page_load_timeout
from selenium import webdriver driver = webdriver.Chrome(executable_path='C:\\..\chromedriver.exe') driver.set_page_load_timeout(0.1) # 100 ms явно не хватит :) driver.get("https://www.google.com/maps")
3. Специфичный способ через проверку в js (источник) проверяя значение в document.readyState.
Но это сгодится когда используется подзагрузка данных, например через ajax.
from selenium import webdriver from selenium.common.exceptions import TimeoutException from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.common.keys import Keys driver = webdriver.Chrome(executable_path='C:\\..\chromedriver.exe') driver.get(. ) timeout = 5 # Выполнение действий, затрагивающих подзагрузку, которая поменяет структуру страницы # Например, пролистывание страницы # driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") try: WebDriverWait(driver, timeout).until( lambda driver: driver.execute_script('return document.readyState') == 'complete' ) except TimeoutException: print("Timed out waiting for page to load")