Bagaimana menemukan elemen HTML yang ada dengan python-selenium di halaman jupyterhub?

Dec 10 2020

Saya memiliki konstruksi berikut di halaman HTML dan saya ingin memilih lielemen (dengan 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>

Saya menggunakan xpath berikut:

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

Namun unsur tersebut sepertinya tidak ditemukan.

Lengkap, kode contoh kerja minimal:

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()

Lingkungan Hidup:

  • MacOS Mojave (10.14.6)
  • python 3.8.6
  • selenium 3.8.0
  • geckodriver 0.26.0

Tambahan

Saya telah mencoba merekam langkah-langkah dengan add-on Firefox "Selenium IDE" yang memberikan langkah-langkah berikut untuk 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()

yang, tentu saja, juga tidak berfungsi. Dengan baris kode itu saya mendapatkan kesalahan

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

Jawaban

4 DebanjanB Dec 18 2020 at 02:37

Anda cukup dekat. Faktanya, seluruh program Anda hanya memiliki satu masalah sebagai berikut:

  • The xpath_runall = "//li[@data-command='notebook:run-all-below']"tidak mengidentifikasi elemen yang terlihat dengan teks sebagai Jalankan Sel yang Dipilih dan Semua di Bawah secara unik karena elemen pertama yang cocok adalah elemen tersembunyi .

Pertimbangan tambahan

Beberapa pengoptimalan lagi:

  • Unsur diidentifikasi sebagai xpath = "//button[@title='Save the notebook contents and create checkpoint']"adalah diklik elemen. Jadi bukan EC seperti presence_of_element_located()yang bisa Anda gunakanelement_to_be_clickable()

  • Setelah elemen dikembalikan melalui EC karena element_to_be_clickable()Anda dapat memanggil click()di baris yang sama.

  • The XPath untuk mengidentifikasi elemen dengan teks sebagai Run Dipilih Sel dan Semua Di bawah akan menjadi:

    //li[@data-command='notebook:run-all-below']//div[@class='lm-Menu-itemLabel p-Menu-itemLabel' and text()='Run Selected Cell and All Below']
    
  • Karena aplikasi ini dibangun melalui JavaScript, Anda perlu menggunakan ActionChains .


Larutan

Solusi Anda yang dioptimalkan adalah:

  • Blok Kode:

    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")
    
  • Keluaran Konsol:

    Page loaded
    Clicked on Run
    Clicked on Run Selected Cell and All Below
    
3 Booboo Dec 17 2020 at 23:21

Ini berhasil untuk saya. Saya menemukan item menu tingkat atas menggunakan xpath penuh dan kemudian mengkliknya. Saya menunggu sedikit waktu untuk memastikan bahwa menu popup telah muncul dan kemudian menggunakan offset dari item menu asli yang telah saya tentukan sebelumnya, saya memindahkan mouse ke offset tersebut dan mengklik apa yang saya tahu sebagai sub- item menu. Pada kode di bawah ini, pertama-tama saya memberi diri saya kesempatan untuk memilih sel:

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()

Pembaruan: Solusi yang Lebih Optimal

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()
1 KunduK Dec 10 2020 at 19:58

Sepertinya ada dua elemen li dengan atribut yang mirip. Anda perlu mengidentifikasi elemen yang benar untuk diklik.Gunakan yang berikut ini xpathuntuk mengklik elemen yang benar.

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))

Keluaran konsol:

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