3

I want to ask my python to click a link from a web page and I have tried below 3 ways to specify an Xpath to a Span element in my python code:

driver.find_element_by_xpath("//*[@id='ChartUnitsHistory_ranges']/span[text()='1y']").click()
driver.find_element_by_xpath("//div[@class='graphControls']/span/1y")
driver.find_element_by_xpath("//a[@class='graphControls']/span[text()='1y']").click()

but all of these failed with the same error message:

selenium.common.exceptions.InvalidSelectorException: Message: The specified selector is invalid.

Updated Error message:

Traceback (most recent call last):   File "02042020.py", line 31, in <module>
    driver.find_element_by_xpath("//span[@id='ChartUnitsHistory_ranges']/a[text()='1y']").click() File "C:\Users\username\PycharmProjects\Web_Scraping\venv\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 394, in find_element_by_xpath
    return self.find_element(by=By.XPATH, value=xpath)   File "C:\Users\username\PycharmProjects\Web_Scraping\venv\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 976, in find_element
    return self.execute(Command.FIND_ELEMENT, {   File "C:\Users\username\PycharmProjects\Web_Scraping\venv\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)   File "C:\Users\username\PycharmProjects\Web_Scraping\venv\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace) selenium.common.exceptions.InvalidSelectorException: Message: The specified selector is invalid.

I need help on coming up with the correct Xpath for the '1y' option.

HTML Source Code:

<div class="graphControls">
            <a href="javascript:jsChartUnitsHistory.getOptions().shiftRange(100, true)">&lt;&lt;</a>&nbsp;
            <a href="javascript:jsChartUnitsHistory.getOptions().shiftRange(33, true)">&lt;</a>
            &nbsp;&nbsp;
            <a href="javascript:jsChartUnitsHistory.getOptions().shiftRange(33, false)">&gt;</a>&nbsp;
            <a href="javascript:jsChartUnitsHistory.getOptions().shiftRange(100, false)">&gt;&gt;</a>&nbsp;
            <a href="javascript:jsChartUnitsHistory.getOptions().updateRange(0,'now')">&gt;|</a>
            &nbsp;&nbsp;&nbsp;&nbsp;
            <a href="javascript:jsChartUnitsHistory.getOptions().zoom(50);">[ + ]</a>
            <a href="javascript:jsChartUnitsHistory.getOptions().zoom(200);">[ - ]</a>
            &nbsp;&nbsp;
        <span id="ChartUnitsHistory_ranges" style="">
                    <a href="javascript:jsChartUnitsHistory.getOptions().updateRange(1,'year')">1y</a>
            <a href="javascript:jsChartUnitsHistory.getOptions().updateRange(3,'month')">3m</a>
            <a href="javascript:jsChartUnitsHistory.getOptions().updateRange(1,'month')">1m</a>
            <a href="javascript:jsChartUnitsHistory.getOptions().updateRange(2,'week')">2w</a>
            <a href="javascript:jsChartUnitsHistory.getOptions().updateRange(1,'week')">1w</a>
            <a href="javascript:jsChartUnitsHistory.getOptions().updateRange(3,'day')">3d</a>
            &nbsp;&nbsp;&nbsp;
        </span>
            <a href="#" id="ChartUnitsHistory_embiggen" onclick="EnlargeFlotChart( 'ChartUnitsHistory', jsChartUnitsHistory, 1100, 312 ); return false">enhance</a>
            <a href="#" id="ChartUnitsHistory_restore" style="display:none;" onclick="RestoreFlotChart( 'ChartUnitsHistory', jsChartUnitsHistory, 700, 160 );;return false">unenhance</a>
            <div style="clear: both;"></div>
</div>

The layout of these elements looks like this on the web page:

<<  <    >  >>  >|      [ + ] [ - ]    1y 3m 1m 2w 1w 3d     enhance unenhance

Please also see the attached screenshot of the web page: Screenshot of the webpage

Please let me know if information provided is enough or not. Thank you in advance!

Guy
  • 46,488
  • 10
  • 44
  • 88
an1que
  • 405
  • 3
  • 14

3 Answers3

1

The text "1y" is in <a> tag, the parent element with id='ChartUnitsHistory_ranges' is <span>

driver.find_element_by_xpath("//span[@id='ChartUnitsHistory_ranges']/a[text()='1y']").click()

"//div[@class='graphControls']/span/1y" didn't work because "1y" is treated like a tag here.

"//a[@class='graphControls']/span[text()='1y']" didn't work because class='graphControls' is in <div> tag and the element is no a direct child, / is for direct child, // for any descendant.

You can also use css_selector for that

driver.find_element_by_css_selector('#ChartUnitsHistory_ranges > [href$="(1,\'year\')"]').click()
Guy
  • 46,488
  • 10
  • 44
  • 88
  • I used the xpath you provided but the same error still occurred. I also added driver.implicitly_wait(5) before the xpath line of code. Any suggestions? Thanks :) – an1que Feb 06 '20 at 19:11
  • @an1que You are getting `Invalid selector exception`? this is kind of wired. Actually all the `xpath` you used are valid, just didn't match. Can you post the full error message? – Guy Feb 06 '20 at 19:14
  • driver.find_element_by_xpath("//span[@id='ChartUnitsHistory_ranges']/a[text()='1y']").click() File "", line 394, in find_element_by_xpath return self.find_element(by=By.XPATH, value=xpath) File ", line 976, in find_element return self.execute(Command.FIND_ELEMENT, { File "", line 321, in execute self.error_handler.check_response(response) File "", line 242, in check_response raise exception_class(message, screen, stacktrace) selenium.common.exceptions.InvalidSelectorException: Message: The specified selector is invalid. – an1que Feb 06 '20 at 19:19
  • Please see the updated error message from the original post for better viewing. – an1que Feb 06 '20 at 19:24
  • @an1que Can you share the link? – Guy Feb 06 '20 at 19:26
  • You won't be able to access cause it's an internally used web page :( – an1que Feb 06 '20 at 19:28
  • @an1que Are you able to use the xpath in dev tools? – Guy Feb 06 '20 at 19:29
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/207378/discussion-between-guy-and-an1que). – Guy Feb 06 '20 at 19:31
1

The desired element is an JavaScript enabled element, so to click on the element ideally 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, "1y"))).click()
    
  • Using CSS_SELECTOR:

    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div.graphControls span#ChartUnitsHistory_ranges a[href*='year']"))).click()
    
  • Using XPATH:

    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[@class='graphControls']//span[@id='ChartUnitsHistory_ranges']//a[contains(@href, 'year')]"))).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
    
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
1

If you're using Chrome, you can click F12 to switch to the Developer Mode and locate the HTML element. Then right click the element to copy:

  • css selector
  • Xpath or full Xpath
  • JS path
  • Styles

In your case, you need to copy the Xpath. This would be a quick way to get the Xpath.

fqsrhy
  • 31
  • 4
  • 1
    "Then right-click the element to copy:" Can be written as "you can follow these tips to navigate to the Xpath". – Umar Hayat Feb 27 '20 at 07:42