Подождите, пока страница загрузится с Selenium WebDriver для Python

182

Я хочу очистить все данные страницы, реализованной бесконечной прокруткой. Следующий код Python работает.

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

Это означает, что каждый раз, когда я прокручиваю вниз, мне нужно ждать 5 секунд, чего обычно достаточно, чтобы страница закончила загрузку вновь сгенерированного содержимого. Но это не может быть эффективным по времени. Страница может завершить загрузку нового содержимого в течение 5 секунд. Как я могу определить, заканчивает ли страница загрузку нового содержимого каждый раз, когда я прокручиваю вниз? Если я могу обнаружить это, я могу прокрутить вниз снова, чтобы увидеть больше содержимого, как только я знаю, что страница закончила загрузку. Это более эффективно по времени.

apogne
источник
1
Это может помочь узнать немного больше о странице. Являются ли элементы последовательными или предсказуемыми? Вы можете подождать загрузки элементов, проверив видимость с помощью id или xpath
user2272115
Я сканирую
apogne
Отвечает ли это на ваш вопрос? Ждите загрузки страницы в Selenium
Matej J

Ответы:

235

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.

Зейнаб Аббасимазар
источник
3
Я получаю «аргумент find_element () после того, как * должен быть последовательностью, а не WebElement» изменен на «WebDriverWait (браузер, задержка) .until (EC.presence_of_element_located ((By.ID,« IdOfMyElement »)))» см. Ручной selenium- python.readthedocs.org/en/latest/waits.html
фрагменты
2
Комментарий @fragles и ответ Дэвида Каллена были тем, что сработало для меня. Возможно, этот принятый ответ может быть обновлен соответствующим образом?
Майкл Олрогге
6
Прохождение browser.find_element_by_id('IdOfMyElement')заставляет NoSuchElementExceptionбыть поднятым. Документация говорит передать кортеж , который выглядит следующим образом : (By.ID, 'IdOfMyElement'). Смотрите мой ответ
Дэвид Каллен
2
Надеюсь, это поможет кому-то другому, потому что изначально мне это было непонятно: WebDriverWait на самом деле вернет веб-объект, с которым вы затем можете выполнить действие (например click()), прочитать текст и т. Д. У меня было ошибочное впечатление, что это просто вызвал ожидание, после которого вам все равно пришлось найти элемент. Если вы выполняете ожидание, то после поиска элемента, селен выдает ошибку, потому что он пытается найти элемент, пока старое ожидание еще обрабатывается (надеюсь, это имеет смысл). Суть в том, что вам не нужно искать элемент после использования WebDriverWait - это уже объект.
Бен Уилсон
1
@ Gopgop Wow это так уродливо не конструктивный комментарий. Что в этом страшного? Как это можно сделать лучше?
Модус Толленс
73

Попытка передать find_element_by_idконструктору for 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
Спасибо! да, это было необходимо и для меня. Идентификатор не единственный атрибут, который можно использовать, чтобы получить полный список, используйте справку (By). Например, я использовалEC.presence_of_element_located((By.XPATH, "//*[@title='Check All Q1']"))
Майкл Олрогге
Вот так это работает и для меня! Я написал дополнительный ответ, расширяющий различные локаторы, которые доступны с Byобъектом.
J0ANMM
Я опубликовал дополнительный вопрос, касающийся ожиданий, когда могут загружаться разные страницы, и не всегда
одна
48

Найдите ниже 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))

Для более подробной информации, проверьте блог Гарри .

kenorb
источник
Почему вы говорите, что это self.driver.execute_script('return document.readyState;')не надежно? Кажется, он отлично работает для моего варианта использования, который ожидает загрузки статического файла в новую вкладку (которая открывается через javascript на другой вкладке вместо .get ()).
Артур Хеберт
1
@ArthurHebert Может быть ненадежным из-за состояния гонки, я добавил соответствующие ссылки.
Кенорб
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это зарезервированный метод. CLASS_NAMEВместо этого использовался синтаксис Selenium для разных языков .

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 ... для выбора подходящих элементов.

J0ANMM
источник
5
В документации по By перечислены атрибуты, которые можно использовать в качестве локаторов.
Дэвид Каллен
1
Это было то, что я искал! Спасибо! Ну, теперь должно быть легче найти, поскольку гугл отправлял меня на этот вопрос, но не на официальную документацию.
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
raffaem
источник
Это полезно Однако что представляет собой 500? Это достаточно большой, чтобы добраться до конца страницы?
Moondra
Это количество, которое страница должна прокручивать ... вы должны установить его как можно выше. Я только что узнал, что этого числа мне достаточно, так как он заставляет страницу прокручиваться до самого дна, пока элементы AJAX не загружаются лениво, что стимулирует необходимость повторной загрузки страницы
raffaem
Это помогает при попытке полностью загрузить все комментарии по проблеме в gitlab.
bgStack15
7

Вы пробовали driver.implicitly_wait. Это похоже на настройку для драйвера, поэтому вы вызываете его только один раз в сеансе, и он в основном говорит драйверу подождать определенное количество времени, пока каждая команда не может быть выполнена.

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

Таким образом, если вы установите время ожидания 10 секунд, оно выполнит команду как можно скорее, ожидая 10 секунд, прежде чем она сдастся. Я использовал это в подобных сценариях прокрутки вниз, поэтому я не понимаю, почему это не сработает в вашем случае. Надеюсь, это полезно.

Чтобы исправить этот ответ, я должен добавить новый текст. Обязательно используйте строчную букву 'w' в implicitly_wait.

seeiespi
источник
В чем разница между неявным ожиданием и webdriverwait?
song0089
4

Как насчет того, чтобы поместить 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"
Рао
источник
тебе не нужна петля?
Кори Голдберг
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')")
NaabNuts
источник