Как найти существующий элемент HTML с python-selenium на странице jupyterhub?

У меня есть следующая конструкция на HTML-странице, и я хочу выбрать элемент li (с python-selenium):

<li class = "p-Menu-item p-mod-disabled" data-type = "command" data-command = "notebook:run-all-below">
    <div class = "p-Menu-itemIcon"></div>
    <div class = "p-Menu-itemLabel" style = "">Run Selected Cell and All Below</div>
    <div class = "p-Menu-itemShortcut" style = ""></div>
    <div class = "p-Menu-itemSubmenuIcon"></div>
</li>

Я использую следующий xpath:

//li[@data-command='notebook:run-all-below']

Но элемент, похоже, не найден.

Полный, минимальный рабочий пример кода:

import time
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()
driver.get("https://mybinder.org/v2/gh/jupyterlab/jupyterlab-demo/master?urlpath=lab/tree/demo")

# Wait for the page to be loaded
xpath = "//button[@title='Save the notebook contents and create checkpoint']"
element = WebDriverWait(driver, 600).until(
    EC.presence_of_element_located((By.XPATH, xpath))
)
time.sleep(10)
print("Page loaded")

# Find and click on menu "Run"
xpath_run = "//div[text()='Run']"
element = WebDriverWait(driver, 60).until(
    EC.element_to_be_clickable((By.XPATH, xpath_run))
)
element.click()
print("Clicked on 'Run'")

# Find and click on menu entry "Run Selected Cell and All Below"
xpath_runall = "//li[@data-command='notebook:run-all-below']"
element = WebDriverWait(driver, 600).until(
    EC.element_to_be_clickable((By.XPATH, xpath_runall))
)
print("Found element 'Run Selected Cell and All Below'")
element.click()
print("Clicked on 'Run Selected Cell and All Below'")

driver.close()

Среда:

  • MacOS Мохаве (10.14.6)
  • питон 3.8.6
  • селен 3.8.0
  • геккодивер 0.26.0

Приложение

Я пытался записать шаги с надстройкой Firefox «Selenium IDE», которая дает следующие шаги для python:

sdriver.get("https://hub.gke2.mybinder.org/user/jupyterlab-jupyterlab-demo-y0bp97e4/lab/tree/demo")
driver.set_window_size(1650, 916)
driver.execute_script("window.scrollTo(0,0)")
driver.find_element(By.CSS_SELECTOR, ".lm-mod-active > .lm-MenuBar-itemLabel").click()

что, естественно, тоже не работает. С этими строками кода я получаю сообщение об ошибке

selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: .lm-mod-active > .lm-MenuBar-itemLabel

Сможете ли вы вообще найти этот элемент с помощью find_element_by_xpath? Возможно, это не тот элемент, на который нужно щелкнуть, и вам нужен какой-то родительский элемент.

Mykola Zotko 10.12.2020 11:56

Я могу найти его с find_element_by_xpath кажется. Но почему я не могу найти элемент с более безопасным кодом, как в примере?

Alex 10.12.2020 12:05

<li> содержит класс p-mod-disabled и элемент отключен. Возможно, вы не сможете нажать.

undetected Selenium 10.12.2020 13:00

Но я могу нажать на этот элемент. Просто запустите пример кода и откройте страницу в инспекторе.

Alex 10.12.2020 13:22
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
6
4
270
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Кажется, есть два элемента li с похожими атрибутами. Вам нужно указать правильный элемент, чтобы щелкнуть его. Используйте следующие xpath, чтобы щелкнуть правильный элемент.

xpath_runall = "//ul[@class='lm-Menu-content p-Menu-content']//li[@data-command='notebook:run-all-below']"
element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.XPATH, xpath_runall))
)
elementText=element.text
print("Found element '{}'".format(elementText))
element.click()
print("Clicked on '{}'".format(elementText))

Вывод консоли:

Page loaded
Clicked on 'Run'
Found element 'Run Selected Cell and All Below'
Clicked on 'Run Selected Cell and All Below'

Извините, но ваше решение, похоже, не работает. Я получаю сообщение об ошибке selenium.common.exceptions.ElementNotInteractableException: Message: Element <li class = "lm-Menu-item p-Menu-item lm-mod-disabled p-mod-disabled"> could not be scrolled into view.

Alex 17.12.2020 14:58

Это сработало для меня. Я нахожу пункт меню верхнего уровня, используя полный xpath, а затем нажимаю на него. Я жду некоторое время, чтобы убедиться, что всплывающее меню появилось, а затем, используя смещение от исходного пункта меню, которое я заранее определил, я перемещаю мышь на это смещение и нажимаю на то, что, как я знаю, является правильным подпунктом. пункт меню. В приведенном ниже коде я сначала даю себе возможность выбрать ячейку:

driver.implicitly_wait(300) # wait up to 300 seconds before calls to find elements time out
driver.get('https://mybinder.org/v2/gh/jupyterlab/jupyterlab-demo/master?urlpath=lab/tree/demo')
driver.execute_script("scroll(0, 0);")
elem = driver.find_element_by_xpath('//div[text() = "Run"]')
elem.click() # click on top-level menu item
time.sleep(.2) # wait for sub-menu to appear
action = webdriver.common.action_chains.ActionChains(driver)
action.move_to_element_with_offset(elem, 224, 182)
# click on sub-menu item:
action.click()
action.perform()

Обновление: более оптимальное решение

driver.implicitly_wait(300) # wait up to 300 seconds before calls to find elements time out
driver.get('https://mybinder.org/v2/gh/jupyterlab/jupyterlab-demo/master?urlpath=lab/tree/demo')
driver.execute_script("scroll(0, 0);")
elem = driver.find_element_by_xpath('//div[text() = "Run"]')
elem.click()
driver.implicitly_wait(.2)
elem2 = driver.find_element_by_xpath('//*[contains(text(),"Run Selected Cell and All Below")]')
driver.execute_script("arguments[0].click();", elem2) # sub-menu, however, stays open
# to close the sub-menu menu:
elem.click()

Извините, но скрипт должен работать автоматически. Если бы требовалось взаимодействие с пользователем, мне вообще не нужно было бы использовать селен!

Alex 17.12.2020 17:50

@ Алекс, я думаю, ты неправильно понял. Проблема, насколько я понимаю, заключалась в том, как выбрать пункт меню «Выполнить выбранную ячейку и все ниже», который я очень старался решить для вас. «Ручной» код, который я включил, был предназначен для моих целей тестирования, чтобы сначала выбрать конкретную ячейку кода, чтобы при окончательном щелчке элемента меню была выбранная ячейка для фактического запуска. Я не думал, что первый выбор ячейки для запуска был частью описания вашей проблемы. Я удалил утверждение input, так как оно вас смутило.

Booboo 17.12.2020 17:58

Благодарю за разъяснение. Ваше решение, кажется, работает сейчас, но использование координат для поиска элемента неоптимально. Небольшое изменение пользовательского интерфейса или CSS может непреднамеренно изменить эти координаты.

Alex 18.12.2020 07:28

Я добавил еще одно решение, которое, надеюсь, вам больше понравится.

Booboo 18.12.2020 13:33
Ответ принят как подходящий

Вы были достаточно близко. На самом деле вся ваша программа имела только одну проблему:

  • xpath_runall = "//li[@data-command='notebook:run-all-below']" не идентифицирует видимый элемент с текстом как «Выполнить выбранную ячейку» и «Все ниже» однозначно, поскольку первый совпадающий элемент является скрытым элементом.

Дополнительные соображения

Еще немного оптимизации:

  • Элемент, обозначенный как xpath = "//button[@title='Save the notebook contents and create checkpoint']", является интерактивным элементом. Так что вместо EC как presence_of_element_located() вы можете использовать element_to_be_clickable()

  • Как только элемент возвращается через EC как element_to_be_clickable(), вы можете вызвать click() в той же строке.

  • xpath для идентификации элемента с текстом «Выполнить выбранную ячейку» и «Все ниже» будет выглядеть так:

    //li[@data-command='notebook:run-all-below']//div[@class='lm-Menu-itemLabel p-Menu-itemLabel' and text()='Run Selected Cell and All Below']
    
  • Поскольку приложение построено с помощью JavaScript , вам необходимо использовать ActionChains.


Решение

Ваше оптимизированное решение будет:

  • Блок кода:

    from selenium import webdriver
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.common.action_chains import ActionChains
    
    driver = webdriver.Firefox(executable_path=r'C:\WebDrivers\geckodriver.exe')
    driver.get("https://mybinder.org/v2/gh/jupyterlab/jupyterlab-demo/master?urlpath=lab/tree/demo")
    WebDriverWait(driver, 60).until(EC.element_to_be_clickable((By.XPATH, "//button[@title='Save the notebook contents and create checkpoint']")))
    print("Page loaded")
    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[text()='Run']"))).click()
    print("Clicked on Run")
    element = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//li[@data-command='notebook:run-all-below']//div[@class='lm-Menu-itemLabel p-Menu-itemLabel' and text()='Run Selected Cell and All Below']")))
    ActionChains(driver).move_to_element(element).click(element).perform()
    print("Clicked on Run Selected Cell and All Below")
    
  • Выход консоли:

    Page loaded
    Clicked on Run
    Clicked on Run Selected Cell and All Below
    

Я не верю, что вам нужно ActionChains. Как только вы сделаете element интерактивным, вы можете просто сделать element.click().

Booboo 19.12.2020 14:37

Другие вопросы по теме