Distinguishing between the same web elements with Python Selenium based on relates - python

After third attempt of solving this problem, I am unable to finalize this on my own. I would appreciate, if someone would like to share their thoughts on the following issue.
Let us assume, we have such kind of HTML structure:
<div class="panel"></div>
<div class="title"></div>
<h3 class="title">HEADER NUMBER ONE<h3>
<div class="area"></div>
<div class="something">IO field</div>
<input class="input"></input>
<div class="panel"></div>
<div class="title"></div>
<h3 class="title">HEADER NUMBER TWO<h3>
<div class="area"></div>
<div class="something">IO field</div>
<input class="input"></input>
My intention is to identify an input element that belongs to the second panel.
Based on reliability check, when I have hardcoded XPATH gathered directly from the browser, sometimes wrong element is being identified (I assume that there are many scripts running when the page is being loaded, which impacts the reliability and stability). Therefore I would like to distinguish between elements based on the h3, which are the one and only difference between objects.
How can I do it?
When identifying elements one by one (so first the title, then its parent, and then moving down to the input), I receive an "element not interactable" exception which is not dependent from the time.
I am thinking of something like:
find //input[#class='input'] where one of ancestors contains /div/h3 which contains(text(), 'HEADER NUMBER TWO')
Obviously, I did not found any working solution for that, despite I spent more than a week with that.
Is it doable at all? If so, could you suggest me something, please? The structure in real is a little bit more complex, but I need just a pattern, hint, or clue.
Greetings!

You can locate the parent panel element based on it's child h3 with the desired title and then to locate the input element inside it.
The XPath to do so can look like the following:
"//div[#class='panel' and(.//h3[contains(.,'HEADER NUMBER ONE')])]//input"
Or even
"//div[#class='panel' and(contains(.,'HEADER NUMBER ONE'))]//input"
The selenium command using that XPath can look like:
driver.find_element(By.XPATH, "//div[#class='panel' and(contains(.,'HEADER NUMBER ONE'))]//input")
More explanations
The XPath
"//div[#class='panel' and(.//h3[contains(.,'HEADER NUMBER ONE')])]//input"
literally means:
Find element with div tag and class attribute value panel and having some child element inside it (this is .// comes for) containing HEADER NUMBER ONE text content.
Inside the above div element find input child element.

In Selenium you can find a set of elements:
inputs = driver.find_elements(By.CSS, 'input[class="input"]')
input_2 = inputs[1]
The input you need is the 2nd element in the list

Related

How to fix a changing xPath in search box (doesn't contain text)

Basically I want to input invoices in Xero software for my job. The process is very simple, I have some values that I need to input in some slots. I have a big problem however. The xpath is dynamic (changes every time you refresh).
Basically it changes from something like this:
//*[#id="PaidToName_12ddc347c7bc4f5aa84c452f55660690_value"]
To something like this:
//*[#id="PaidToName_4fea44e4f8a844b4b630b4bf149490d8_value"]
So the numbers keep on changing.
I have tried a starts-with function however I am pretty sure that there are two XPATHs that starts with PaidToName or end with value, therefore this doesn't seem like a solution as I get this error message:
selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable
The other thing to note is that I see many elements that have the "input type hidden" in the HTML code which I am pretty sure play a role with that. Please let me know if there is anything I can do to help.
This is the code I have tried that doesn't work.
button = driver.find_element_by_xpath("//*[starts-with(#id,'PaidToName')]")
button.send_keys('lol')
This is the HTML code I am trying to retrieve
<input type="text" size="24" autocomplete="off" id="PaidToName_4fea44e4f8a844b4b630b4bf149490d8_value" name="PaidToName_4fea44e4f8a844b4b630b4bf149490d8_value" class="x-form-text x-form-field autocompleter x-form-focus" tabindex="10" style="width: 129px;">
You can use xpath with id and class combination, try this :
button = driver.find_element_by_xpath("//*[contains(#id,'PaidToName') and contains(#class,'x-form-text')]")
button.send_keys('lol')
Try below given locator.
driver.find_element_by_xpath("//input[contains(#id,'PaidToName') AND contains(#class,'x-form-text')]")

what to do for dynamically changing xpaths in python using selenium?

I have a xpath as:
//*[#id="jobs-search-box-keyword-id-ember968"]
The number 968 constantly keeps on changing after every reload.
Rest of the string remains constant.
How to I find the constantly changing xpath?
You can use partial id with contains()
//*[contains(#id, "jobs-search-box-keyword-id-ember")]
You can try using starts-with below,
//*[starts-with(#id,'jobs-search-box-keyword-id-ember')]
The details provided is insufficient to to provide the accurate result. Still you can follow the below code references
In //*[#id="jobs-search-box-keyword-id-ember968"] the last number 968 keeps changing. but if you make this like //*[starts-with(#id,'jobs-search-box-keyword-id-ember')] then there might be possibility that you can have more then one element with the same partial is i.e. jobs-search-box-keyword-id-ember in this case it will locate on 1st matching element. that may not be your expected one
Use the tag name lets say element is an input tag whose id is jobs-search-box-keyword-id-ember968
Xpath - //input[starts-with(#id,'jobs-search-box-keyword-id-ember')]
CSS - input[id^='jobs-search-box-keyword-id-ember']
Use the relevant parent element to make this more specific. e.g the element is in parent tag <div class="container">
Xpath- //div[#class='container']//input[starts-with(#id,'jobs-search-box-keyword-id-ember')]
CSS - div.container input[id^='jobs-search-box-keyword-id-ember']
This worked for me:
Locator:
JOBS_SEARCH_BOX_XPATH = "//*[contains(#id,'jobs-search-box-keyword-id-ember')]"
Code:
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, JOBS_SEARCH_BOX_XPATH)))
.send_keys("SDET")

Finding an element by partial href (Python Selenium)

I'm trying to access text from elements that have different xpaths but very predictable href schemes across multiple pages in a web database. Here are some examples:
<a href="/mathscinet/search/mscdoc.html?code=65J22,(35R30,47A52,65J20,65R30,90C30)">
65J22 (35R30 47A52 65J20 65R30 90C30) </a>
In this example I would want to extract "65J22 (35R30 47A52 65J20 65R30 90C30)"
<a href="/mathscinet/search/mscdoc.html?code=05C80,(05C15)">
05C80 (05C15) </a>
In this example I would want to extract "05C80 (05C15)". My web scraper would not be able to search by xpath directly due to the xpaths of my desired elements changing between pages, so I am looking for a more roundabout approach.
My main idea is to use the fact that every href contains "/mathscinet/search/mscdoc.html?code=". Selenium can't directly search for hrefs, but I was thinking of doing something similar to this C# implementation:
Driver.Instance.FindElement(By.XPath("//a[contains(#href, 'long')]"))
To port this over to python, the only analogous method I could think of would be to use the in operator, but I am not sure how the syntax will work when everything is nested in a find_element_by_xpath. How would I bring all of these ideas together to obtain my desired text?
driver.find_element_by_xpath("//a['/mathscinet/search/mscdoc.html?code=' in #href]").text
If I right understand you want to locate all elements, that have same partial href. You can use this:
elements = driver.find_elements_by_xpath("//a[contains(#href, '/mathscinet/search/mscdoc.html')]")
for element in elements:
print(element.text)
or if you want to locate one element:
driver.find_element_by_xpath("//a[contains(#href, '/mathscinet/search/mscdoc.html')]").text
This will give a list of all elements located.
As per the HTML you have shared #AndreiSuvorkov's answer would possibly cater to your current requirement. Perhaps you can get much more granular and construct an optimized xpath by:
Instead of using contains using starts-with
Include the ?code= part of the #href attribute
Your effective code block will be:
all_elements = driver.find_elements_by_xpath("//a[starts-with(#href,'/mathscinet/search/mscdoc.html?code=')]")
for elem in all_elements:
print(elem.get_attribute("innerHTML"))

In Angular website, get exact text inside <div> tag with Selenium & Python?

I would like to get exact text inside tag with selenium and python.
When I inspect the element, I can see the html below on the browser.
<div class="value ng-binding" ng-bind="currentEarning">£8.8</div> == $0
I have written the python code with selenium in order to get text as follows.
currentEaring = Ladbrokes.find_element_by_xpath('//div[#ng-bind="currentEarning"]').text
When I run this script several times, I occasionally get the result as 0 - this is not true.
Rarely I can get £8.8 - this is ture.
I guess I occasionally get 0 because of the == $0 but not sure.
How can I get the text as £8.8? - using regex? If then, how?
it's happening may be because, it takes some time to populate the text after page has loaded, and it seems like you are not waiting enough.
you can use explicit wait to wait until element contains certain text.
For your case, following example might work.
wait = WebDriverWait(driver, 30)
wait.until(EC.text_to_be_present_in_element((By.XPATH, "//div[#ng-bind='currentEarning']"), "£"))
Here is the Answer to your Question:
One of the reason to get improper results may be due to asynchronous rendering of the HTML DOM due to presence of JavaScript and AJAX calls. As you have taken help of the ng-bind attribute only so our intended node may not be the unique/first match in the HTML DOM. Hence we will refine our xpath to be more granular & unique by adding the class attribute along with ng-bind attribute and take help of get_attribute method to get the text £8.8 as follows:
currentEaring = Ladbrokes.find_element_by_xpath('//div[#class="value ng-binding" and #ng-bind="currentEarning"]').get_attribute("innerHTML")
Let me know if this Answers your Question.

Selenium Find Sub-Child href Element

I'm trying to click the following link using selenium.
<div id="RECORD_2" class="search-results-item">
<a hasautosubmit="true" oncontextmenu="javascript:return IsAllowedRightClick(this);" class="smallV110" href="#;cacheurlFromRightClick=no"></a>
</div>
Which record to click is not known before the code is executed. Record_2 has multiple children, and the one included is the one I want to click. The link is edited for the sake of privacy. I tried to do something like that where name is the record variable, however it doesn't work.
driver.find_element_by_css_selector("css=div#"RECORD_%s" % (name).smallV110")
I'm a complete newbie to selenium so I couldn't figure out a way to sort this out. I would appreciate any help. Thanks!
Note that this is not Selenium IDE and you don't need the css= at the beginning of a selector.
There are multiple ways to locate the link element, e.g.:
driver.find_element_by_css_selector(".search-results-item a.smallV110")
driver.find_element_by_css_selector("[id^=RECORD] a.smallV110") # id starts with "RECORD"
If you know the id value beforehand:
id_i_know = 2
driver.find_element_by_css_selector("[id=RECORD_%d] a.smallV110" % id_i_know)
You don't have to have that smallV110 class attribute check - I've added it to increase chances of not matching other a elements inside the div (not sure what they are, you have not posted the entire HTML).

Categories