0

How do I write an xpath so that I can click on the settings button with selenium in python for the following html. Clicking the element is made harder by the fact that the settings element doesn't have an own id.

<ul class="nav nav-tabs">
    <li class="active">
        <a data-toggle="tab" href="#editor">Structure</a>
    </li>
    <li>
        <a data-toggle="tab" href="#feedback">Feedback</a>
    </li>
    <li>
        <a data-toggle="tab" href="#settings">General settings</a>
    </li>

I have tried multiple commands, but I don't seem to get it right.

I am using elements (plural) so that I can see if it matches any xpaths. Figured it would be the cleanest way to test out different xpaths. If I get the xpath correctly I will use element (singular) and add .click() to the end. I have tried at least these following codes.

  1. driver.find_elements_by_xpath("//*[@class='nav nav-tabs']//*[href='#settings']") --> return [] (xpath doesn't match?)

  2. driver.find_elements_by_xpath("//ul[contains(@class, 'nav-tabs')]") --> returns []

  3. driver.find_elements_by_xpath(".//a[@href='#settings']") --> return []

When I 'click' the element, it usually gives the following error message:

NoSuchElementException: no such element: Unable to locate element: {"method":"xpath","selector":".//a[@href='#settings']"}
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352

3 Answers3

2

@MosheSlavin was close. However, to click() on the element you have to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following Locator Strategies:

  • Using LINK_TEXT:

    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.LINK_TEXT, "General settings"))).click()
    
  • Using CSS_SELECTOR:

    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "ul.nav.nav-tabs li>a[href$='settings']"))).click()
    
  • Using XPATH:

    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//ul[@class='nav nav-tabs']//li/a[text()='General settings']"))).click()
    
  • Note : You have to add the following imports :

    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    

Here you can find a detailed discussion on Selenium “selenium.common.exceptions.NoSuchElementException” when using Chrome

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • The problem turned out to be, not that the element hadn't loaded, but that selenium was looking for the element in the wrong frame. The link you provided helped me find the information needed to solve the problem. So thank you very much for that! So the solution was to find the right iframe id and insert it in this function _driver.switch_to.frame("iframe_name")_ . After that I ran driver.find_element_by_xpath(".//a[@href='#settings']").click() and it worked. :) – Tomas Storås Aug 19 '19 at 20:54
  • 1
    @TomasStorås It's not only you, everyone starting with _Selenium_ have to cross these phases, so while answering I always point the _New contributors_ to the detailed answer which I have published long back. Glad to be able to help you out. – undetected Selenium Aug 19 '19 at 21:03
  • 2
    This is an example of why it's a good idea to show the full HTML/URL in the question. – Moshe Slavin Aug 20 '19 at 08:18
1

The element might not be loaded yet, so use WebDriverWait to wait for the element:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

WebDriverWait(self.driver,10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "a[href='#settings']")))

Or

WebDriverWait(self.driver,10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "a[href='#settings']")))
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
Moshe Slavin
  • 5,127
  • 5
  • 23
  • 38
  • Thanks for the response. I haven't defined a class, so I suppose I should not include self in the code(?). Well I ran it like this: _WebDriverWait(driver,10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "a[href='#settings']")))_ And it returned: _raise TimeoutException(message, screen, stacktrace) TimeoutException_ I don't believe the problem to be that the element has not loaded. Since I have the webpage open and keep running different commands at it, so the element should definitely have loaded. Could it be that it is invisible (!?) somehow. – Tomas Storås Aug 19 '19 at 19:25
-1

The solution was that selenium was looking for the element in the wrong frame. I inspected the html code and found two iframe id's. I inserted one of the iframe id's in this function

driver.switch_to.frame("iframe_name") 

and then

driver.find_element_by_xpath(".//a[@href='#settings']").click()