У меня есть следующая конструкция на 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()
Среда:
Приложение
Я пытался записать шаги с надстройкой 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
кажется. Но почему я не могу найти элемент с более безопасным кодом, как в примере?
<li>
содержит класс p-mod-disabled
и элемент отключен. Возможно, вы не сможете нажать.
Но я могу нажать на этот элемент. Просто запустите пример кода и откройте страницу в инспекторе.
Кажется, есть два элемента 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
.
Это сработало для меня. Я нахожу пункт меню верхнего уровня, используя полный 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()
Извините, но скрипт должен работать автоматически. Если бы требовалось взаимодействие с пользователем, мне вообще не нужно было бы использовать селен!
@ Алекс, я думаю, ты неправильно понял. Проблема, насколько я понимаю, заключалась в том, как выбрать пункт меню «Выполнить выбранную ячейку и все ниже», который я очень старался решить для вас. «Ручной» код, который я включил, был предназначен для моих целей тестирования, чтобы сначала выбрать конкретную ячейку кода, чтобы при окончательном щелчке элемента меню была выбранная ячейка для фактического запуска. Я не думал, что первый выбор ячейки для запуска был частью описания вашей проблемы. Я удалил утверждение input
, так как оно вас смутило.
Благодарю за разъяснение. Ваше решение, кажется, работает сейчас, но использование координат для поиска элемента неоптимально. Небольшое изменение пользовательского интерфейса или CSS может непреднамеренно изменить эти координаты.
Я добавил еще одно решение, которое, надеюсь, вам больше понравится.
Вы были достаточно близко. На самом деле вся ваша программа имела только одну проблему:
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()
.
Сможете ли вы вообще найти этот элемент с помощью
find_element_by_xpath
? Возможно, это не тот элемент, на который нужно щелкнуть, и вам нужен какой-то родительский элемент.