Подождите, пока страница загрузится с Selenium WebDriver для Python
Я хочу очистить все данные страницы, реализованной бесконечной прокруткой. Следующий код Python работает.
for i in range(100):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(5)
Это означает, что каждый раз, когда я прокручиваю вниз, мне нужно ждать 5 секунд, чего обычно достаточно, чтобы страница загрузила только что сгенерированное содержимое. Но, это не может быть эффективным по времени. Страница может завершить загрузку нового содержимого в течение 5 секунд. Как я могу определить, закончилась ли загрузка страницы новым содержимым при каждой прокрутке вниз? Если я могу обнаружить это, я могу прокрутить вниз снова, чтобы увидеть больше содержимого, как только я знаю, что страница закончила загрузку. Это более эффективно по времени.
17 ответов
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
,
Пытаясь пройти 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.
Найдите ниже 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
просто возвращает истину сразу.
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))
Для получения более подробной информации, проверьте блог Гарри.
Как уже упоминалось в ответе Дэвида Каллена, я всегда рекомендовал использовать строку, подобную следующей:
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... для выбора подходящих элементов.
Из селена / 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"))
Кроме того, вместо 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
Ты пытался driver.implicitly_wait
, Это похоже на настройку для драйвера, поэтому вы вызываете его только один раз в сеансе, и он в основном говорит драйверу подождать определенное количество времени, пока каждая команда не может быть выполнена.
driver = webdriver.Chrome()
driver.implicitlyWait(10)
Таким образом, если вы установите время ожидания 10 секунд, оно выполнит команду как можно скорее, ожидая 10 секунд, прежде чем она сдастся. Я использовал это в похожих сценариях прокрутки вниз, поэтому я не понимаю, почему это не сработает в вашем случае. Надеюсь, что это полезно:)
Здесь я сделал это, используя довольно простую форму:
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
Решение для страниц Ajax, которые постоянно загружают данные. Указанные методы предварительного просмотра не работают. Вместо этого мы можем взять dom страницы, хэшировать ее и сравнить старые и новые значения хеша вместе за разное время.
import time
from selenium import webdriver
def page_has_loaded(driver, sleep_time = 2):
'''
Waits for page to completely load by comparing current page hash values.
'''
def get_page_hash(driver):
'''
Returns html dom hash
'''
# can find element by either 'html' tag or by the html 'root' id
dom = driver.find_element_by_tag_name('html').get_attribute('innerHTML')
# dom = driver.find_element_by_id('root').get_attribute('innerHTML')
dom_hash = hash(dom.encode('utf-8'))
return dom_hash
page_hash = 'empty'
page_hash_new = ''
# comparing old and new page DOM hash together to verify the page is fully loaded
while page_hash != page_hash_new:
page_hash = get_page_hash(driver)
time.sleep(sleep_time)
page_hash_new = get_page_hash(driver)
print('<page_has_loaded> - page not loaded')
print('<page_has_loaded> - page loaded: {}'.format(driver.current_url))
Как насчет того, чтобы поместить 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"
Вы можете сделать это очень просто с помощью этой функции:
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')")
selenium не может определить, полностью загружена страница или нет, но javascript может. Я предлагаю вам попробовать это.
from selenium.webdriver.support.ui import WebDriverWait
WebDriverWait(driver, 100).until(lambda driver: driver.execute_script('return document.readyState') == 'complete')
это выполнит код javascript вместо использования python, поскольку javascript может определить, когда страница полностью загружена, он покажет «завершено». Этот код означает, что через 100 секунд продолжайте пытаться использовать document.readyState до тех пор, пока не будет показано завершение.
Используйте это в коде:
from selenium import webdriver
driver = webdriver.Firefox() # or Chrome()
driver.implicitly_wait(10) # seconds
driver.get("http://www.......")
или вы можете использовать этот код, если ищете конкретный тег:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Firefox() #or Chrome()
driver.get("http://www.......")
try:
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "tag_id"))
)
finally:
driver.quit()
Если вы пытаетесь прокрутить и найти все элементы на странице. Вы можете рассмотреть возможность использования следующего. Это комбинация нескольких методов, упомянутых здесь другими. И это сделало работу для меня:
while True:
try:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
driver.implicitly_wait(30)
time.sleep(4)
elem1 = WebDriverWait(driver, 30).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "element-name")))
len_elem_1 = len(elem1)
print(f"A list Length {len_elem_1}")
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
driver.implicitly_wait(30)
time.sleep(4)
elem2 = WebDriverWait(driver, 30).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "element-name")))
len_elem_2 = len(elem2)
print(f"B list Length {len_elem_2}")
if len_elem_1 == len_elem_2:
print(f"final length = {len_elem_1}")
break
except TimeoutException:
print("Loading took too much time!")
Здесь очень хорошие ответы. Быстрый пример ожидания
XPATH
.
# wait for sizes to load - 2s timeout
try:
WebDriverWait(driver, 2).until(expected_conditions.presence_of_element_located(
(By.XPATH, "//div[@id='stockSizes']//a")))
except TimeoutException:
pass
Я немного изо всех сил пытался заставить это работать, так как это не сработало для меня, как ожидалось. любой, кто все еще пытается заставить это работать, может проверить это.
Я хочу дождаться появления элемента на веб-странице, прежде чем продолжить свои манипуляции.
мы можем использовать WebDriverWait(driver, 10, 1).until(), но загвоздка в том, что until() ожидает функцию , которую она может выполнять в течение заданного периода времени ожидания (в нашем случае это 10) за каждую 1 секунду. поэтому сохранение его, как показано ниже, сработало для меня.
element_found = wait_for_element.until(lambda x: x.find_element_by_class_name("MY_ELEMENT_CLASS_NAME").is_displayed())
вот что до() делает за кулисами
def until(self, method, message=''):
"""Calls the method provided with the driver as an argument until the \
return value is not False."""
screen = None
stacktrace = None
end_time = time.time() + self._timeout
while True:
try:
value = method(self._driver)
if value:
return value
except self._ignored_exceptions as exc:
screen = getattr(exc, 'screen', None)
stacktrace = getattr(exc, 'stacktrace', None)
time.sleep(self._poll)
if time.time() > end_time:
break
raise TimeoutException(message, screen, stacktrace)
nono = driver.current_url
driver.find_element(By.XPATH,"//button[@value='Send']").click()
while driver.current_url == nono:
pass
print("page loaded.")