Подождите, пока страница загрузится с Selenium WebDriver для Python
Я хочу очистить все данные страницы, реализованной бесконечной прокруткой. Следующий код Python работает.
for i in range(100):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(5)
Это означает, что каждый раз, когда я прокручиваю вниз, мне нужно ждать 5 секунд, чего обычно достаточно, чтобы страница закончила загрузку вновь сгенерированного содержимого. Но, это не может быть эффективным по времени. Страница может завершить загрузку нового содержимого в течение 5 секунд. Как я могу определить, заканчивает ли страница загрузку нового содержимого каждый раз, когда я прокручиваю вниз? Если я могу обнаружить это, я могу прокрутить вниз снова, чтобы увидеть больше содержимого, как только я знаю, что страница закончила загрузку. Это более эффективно по времени.
Ответы
Ответ 1
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!"
Я использовал его для проверки предупреждений. Вы можете использовать любые другие методы, чтобы найти локатор.
РЕДАКТИРОВАТЬ 1:
Я должен отметить, что webdriver
будет ожидать загрузки страницы по умолчанию. Он не дожидается загрузки внутри фреймов или запросов ajax. Это означает, что когда вы используете .get('url')
, ваш браузер будет ждать, пока страница будет полностью загружена, а затем перейдите к следующей команде в коде. Но когда вы отправляете запрос ajax, webdriver
не ждет, и ваша ответственность - ждать соответствующего количества времени для загрузки страницы или части страницы; поэтому существует модуль с именем expected_conditions
.
Ответ 2
Попытка передать find_element_by_id
конструктору для presence_of_element_located
(как показано в принятом ответе), вызвала NoSuchElementException
. Мне пришлось использовать синтаксис в 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"
Это соответствует примеру в документации. Ниже приведена ссылка на документацию для.
Ответ 3
Найдите ниже 3 метода:
readyState
Проверка страницы 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
просто сразу возвращает true.
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))
Для более подробной информации, проверьте Гарри блог.
Ответ 4
Из селена /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"))
Ответ 5
Как уже упоминалось в ответе Дэвида Каллена, я всегда рекомендовал использовать строку, подобную следующей:
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
создаст проблемы для библиотеки Selenium Java, где .class
является зарезервированным методом. Чтобы сохранить синтаксис Selenium в разных языках, вместо этого использовалось CLASS_NAME
.
CSS_SELECTOR
Найдите элементы по их классу, идентификатору или имени тега, используя #idName
, .className
, tagName
.
LINK_TEXT
Находит теги HTML по тексту, который они содержат. Например, ссылка "Next" может быть выбрана с помощью (By.LINK_TEXT, "Next")
.
PARTIAL_LINK_TEXT
Похож на LINK_TEXT
, но соответствует частичной строке.
NAME
Находит теги HTML по их атрибуту имени. Это удобно для HTML-форм.
TAG_NAME
Подбирает теги HTML по имени тега.
XPATH
Использует выражение XPath... для выбора подходящих элементов.
Ответ 6
На боковой ноте, вместо прокрутки вниз 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
Ответ 7
Как поместить WebDriverWait в цикл While и перехватить исключения.
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"
Ответ 8
Здесь я сделал это, используя довольно простую форму:
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
Ответ 9
Вы пробовали driver.implicitly_wait
. Это похоже на настройку для драйвера, поэтому вы вызываете его только один раз в сеансе, и он в основном говорит драйверу подождать определенное количество времени, пока каждая команда не может быть выполнена.
driver = webdriver.Chrome()
driver.implicitly_Wait(10)
Таким образом, если вы установите время ожидания 10 секунд, оно выполнит команду как можно скорее, ожидая 10 секунд, прежде чем она сдастся. Я использовал это в похожих сценариях прокрутки вниз, поэтому я не понимаю, почему это не сработает в вашем случае. Надеюсь, это полезно.