Как найти существующий элемент 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 Mojave (10.14.6)
- Python 3.8.6
- селен 3.8.0
- geckodriver 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
Ответы
Вы были достаточно близко. Фактически вся ваша программа имела только одну проблему:
xpath_runall = "//li[@data-command='notebook:run-all-below']"
Не определяет видимый элемент с текстом , как Run Selected Cell и все Ниже однозначно , как первый совпавший элементом является скрытым элементом.
Дополнительные соображения
Еще несколько оптимизаций:
Элемент идентифицирован как
xpath = "//button[@title='Save the notebook contents and create checkpoint']"
это интерактивный элемент. Итак, вместо EC, какpresence_of_element_located()
вы можете использоватьelement_to_be_clickable()
После того , как элемент возвращается через ЕС , как
element_to_be_clickable()
вы можете вызватьclick()
на той же строке.XPath для идентификации элемента с текстом , как Run Selected Cell и все Ниже будет:
//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
Это сработало для меня. Я нахожу пункт меню верхнего уровня, используя полный 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()
Кажется, есть два элемента 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'