3

Using Selenium WebDriver with Python 3.4.

I'm writing a scraper, and locating elements using XPaths relative to some non-root ancestor element, such as below:

ancestor_element = driver.find_element_by_xpath(ancestor_xpath)
child_element = ancestor_element.find_element_by_xpath(child_xpath)

This works as expected. However, I am unsure how to do this relative location with an explicit wait call, as the examples I have seen use this syntax:

child_element =  WebDriverWait(driver,10).until(
    EC.presence_of_element_located((By.XPATH, child_xpath))
)

which appears to evaluate the XPath against the page root, and throws an error complaining about the ".//" beginning of the XPath string.

Any advice on this?

5
  • 1
    The wait documentation includes an example that appears to use a parent element to find its child as opposed to searching from page root. element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id(“someId”)). I have not tried it, but merely knew it existed.You should be able to change the query to search by xpath. Commented Oct 26, 2017 at 20:57
  • 1
    why not combine xpaths and search for child element right away? E.g. if ancestor xpath is //a/b and child xpath is .//c/d, then it will be same as //a/b//c/d Commented Oct 26, 2017 at 21:53
  • Another option is to write a custom wait condition: class child_is_present def __init__(self, ancestor_xpath, child_xpath)... def __call__(self, driver): ancestor_element = driver.find_element_by_xpath(*self.ancestor_xpath) child_element = ancestor_element.find_element_by_xpath(*self.child_xpath) if(child_element) return true Commented Oct 26, 2017 at 21:58
  • @RonNorris - The lambda solution is exactly what I was looking for, thanks! Commented Oct 27, 2017 at 13:52
  • @KirilS. - Combining the xpaths would definitely be a good solution, but there are many instances of the ancestor element in the page, and I'm passing them in one at a time to this helper method. Meaning combining the xpaths would return the child of every ancestor on the page when I just want the child of one of those ancestors. (e.g. I want only guy #1's name, but I'm getting the names of every guy on the page). I could append a [position()] to the parent XPath to solve this, but the lambda worked out. Anyway, thanks to both of you! I would upvote but seems I'm still too low reputation Commented Oct 27, 2017 at 14:05

1 Answer 1

2

Had this same question and follow the lambda example from @Ron Norris's comment. However, the documentation isn't clear about how you can find the child_element relative to the ancestor_element, even when using lambda.

You can actually replace driver in the WebDriverWait call with your ancestor_element, to explicitly only search things "under" the ancestor_element (even though the docstring states that it has to be an instance of WebDriver, I found that a WebElement also works).

So I wound up with something like:

child_element =  WebDriverWait(ancestor_element,10).until(
    EC.presence_of_element_located((By.XPATH, child_xpath))
)

If you want to use a lambda, you could then do:

child_element =  WebDriverWait(ancestor_element,10).until(
    lambda x: x.find_element_by_xpath(child_xpath)
)
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! WebDriverWait(child_element, 10)... is really works whats surprised me

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.