Wie finde ich ein vorhandenes HTML-Element mit Python-Selen in einer Jupyterhub-Seite?
Ich habe das folgende Konstrukt in einer HTML-Seite und möchte das li
Element (mit Python-Selen) auswählen :
<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>
Ich benutze den folgenden xpath:
//li[@data-command='notebook:run-all-below']
Aber das Element scheint nicht gefunden zu sein.
Vollständiger, minimal funktionierender Beispielcode:
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()
Umgebung:
- MacOS Mojave (10.14.6)
- Python 3.8.6
- Selen 3.8.0
- Geckodriver 0.26.0
Nachtrag
Ich habe versucht, die Schritte mit dem Firefox-Add-On "Selenium IDE" aufzuzeichnen, das die folgenden Schritte für Python enthält:
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()
was natürlich auch nicht funktioniert. Mit diesen Codezeilen bekomme ich eine Fehlermeldung
selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: .lm-mod-active > .lm-MenuBar-itemLabel
Antworten
Du warst nah genug. Tatsächlich hatte Ihr gesamtes Programm nur ein einziges Problem:
- Das
xpath_runall = "//li[@data-command='notebook:run-all-below']"
sichtbare Element mit Text wird nicht eindeutig als Ausgewählte Zelle ausführen und Alle unten identifiziert, da das erste übereinstimmende Element ein verstecktes Element ist.
Weitere Überlegungen
Weitere Optimierungen:
Das als identifizierbare Element
xpath = "//button[@title='Save the notebook contents and create checkpoint']"
ist ein anklickbares Element. Also anstelle von EC, wiepresence_of_element_located()
Sie verwenden könnenelement_to_be_clickable()
Sobald das Element durch zurückgegeben EC als
element_to_be_clickable()
Sie können die aufrufenclick()
auf der gleichen Linie.Der x-Pfad zum Identifizieren des Elements mit Text als " Ausgewählte Zelle ausführen" und "Alle unten" lautet :
//li[@data-command='notebook:run-all-below']//div[@class='lm-Menu-itemLabel p-Menu-itemLabel' and text()='Run Selected Cell and All Below']
Da die Anwendung über JavaScript erstellt wird , müssen Sie ActionChains verwenden .
Lösung
Ihre optimierte Lösung lautet:
Codeblock:
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")
Konsolenausgabe:
Page loaded Clicked on Run Clicked on Run Selected Cell and All Below
Das hat bei mir funktioniert. Ich finde den Menüpunkt der obersten Ebene mit vollem xpath und klicke dann darauf. Ich warte eine Weile, um sicherzustellen, dass das Popup-Menü angezeigt wird, und bewege dann mit einem Versatz zum ursprünglichen Menüpunkt, den ich zuvor festgelegt habe, die Maus auf diesen Versatz und klicke auf das, was ich als das richtige Untermenü kenne Menüpunkt. Im folgenden Code gebe ich mir zuerst die Möglichkeit, eine Zelle auszuwählen:
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()
Update: Eine optimalere Lösung
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()
Es scheint, dass es zwei li-Elemente mit ähnlichen Attributen gibt. Sie müssen das richtige Element zum Klicken identifizieren. Verwenden Sie Folgendes, xpath
um auf das richtige Element zu klicken.
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))
Konsolenausgabe:
Page loaded
Clicked on 'Run'
Found element 'Run Selected Cell and All Below'
Clicked on 'Run Selected Cell and All Below'