Selecting elements using xpath - selenium

So very new here to Selenium but I'm having trouble selecting the element I want from this website. In this case, I got the x_path using Chrome's 'copy XPath tool.' Basically, I'm looking to extract the CID text (in this case 4004) from the website, but my code seems to be unable to do this. Any help would be appreciated!
I have also tried using the CSS selector method as well but it returns the same error.
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.binary_location = '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'
driver= webdriver.Chrome()
chem_name = "D008294"
url = "https://pubchem.ncbi.nlm.nih.gov/#query=" + chem_name
driver.get(url)
elements = driver.find_elements_by_xpath('//*[#id="collection-results-container"]/div/div/div[2]/ul/li/div/div/div/div[2]/div[2]/div[2]/span/a/span/span')
driver.close()
print(elements.text)
As of now, this is the error I receive: 'list' object has no attribute 'text'

Here is the xpath that you can use.
//span[.='Compound CID']//following-sibling::a/descendant::span[2]
Why your script did not worked: I 2 issues in your code.
elements = driver.find_elements_by_xpath('//*[#id="collection-results-container"]/div/div/div[2]/ul/li/div/div/div/div[2]/div[2]/div[2]/span/a/span/span')
driver.close() # <== don't close the browser until you are done with all your steps on the browser or elements
print(elements.text) # <== you can not get text from list (python will through error here
How to fix it:
CID = driver.find_element_by_xpath("//span[.='Compound CID']//following-sibling::a/descendant::span[2]").text # <== returning the text using find_element (not find_elements)
driver.close()
print(CID) # <== now you can print `CID` though browser closed as the value already stored in variable.

Function driver.find_elements_by_xpath return list of Element. You should loop to get text of each element,
Like this:
for ele in print(elements.text):
print(ele.text)
Or if you want to match first Element, use driver.find_element_by_xpath function instead.

Using xpath provided chrome is always does not work as expected. First you have to know how to write xpath and verify it chrome console.
see these links, which helps you to know about xpaths
https://www.guru99.com/xpath-selenium.html
https://www.w3schools.com/xml/xpath_syntax.asp
In this case, first find the span contains text Compound CID and move to parent span the down to child a/span/span. something like //span[contains(text(),'Compound CID']/parent::span/a/span/span.
And also you need to findelement which return single element and get text from it. If you use findelements then it will return list of elements, so you need to loop and get text from those elements.

xpath: //a[contains(#href, 'compound')]/span[#class='breakword']/span
you can use the "href" as your attribute reference since I noticed that it has unique value for each component.
Example:
href="https://pubchem.ncbi.nlm.nih.gov/substance/53790330"
href="https://pubchem.ncbi.nlm.nih.gov/compound/4004"

Related

How to find the previous element of an element found in Appium Xpath?

PROBLEM:
I am having an issue to identify an element in Appium.
As I can't identify the element, I managed to identify by xpath the next element. Let's call it "FOUND" element.
SO now I try to get the previous element from this element "FOUND".
DETAILS:
In this screenshot above, you can see the elements I am speaking about.
To find the "FOUND" element, I am looping in all the element with the class "android.widget.TextView", I extract the attribute 'text' and compate it to the string 'Website'.
Then from the element FOUND, I try to find the element I need. I tried so many various expression, but I didn't succeed to get it. I use a "try, except" to try to cath it, but without success.
here is the code:
elements_of_profile_detail_page = driver.find_elements_by_class_name("android.widget.TextView")
list_xpath=[
"preceding-sibling::android.widget.TextView[1]",
"preceding-sibling::android.widget.TextView[1]",
"(/preceding-sibling::android.widget.TextView)[1]",
"/*preceding-sibling::android.widget.TextView[1]",
"(/*preceding-sibling::android.widget.TextView)[1]",
"(preceding-sibling::android.widget.TextView)[1]",
"../android.widget.TextView[0]",
"preceding-sibling::*[1]",
"/preceding-sibling::android.widget.TextView",
"preceding-sibling::android.widget.TextView",
"(preceding-sibling::android.widget.TextView)[1]"
]
i=0
while i<len(list_xpath):
try:
website = element_of_profile_detail_page.find_element_by_xpath(list_xpath[i]).get_attribute('text')
print(f"website : {website}")
print(f"xpath : {xpath}")
break
except:
print("It didn't work!")
i+=1
And here is another screenshot with more details of the element I need:
I am using Appium 1.15.1 and Python 3.7. I don't think it is important as it is a matter of Xpath.
I hope I gave enough details to find the solution. I am working on it since very early this morning.
You can directly find the element by using its text in the xpath like:
element = driver.find_element_by_xpath("//*[contains(#text,'AlfangeAcademy')]")

Selenium xpath failing to find element (other xpath tools prove it's there)

Selenium FindElement:
driver.FindElement(By.XPath($"//*[contains(text(), '{text}')]"));
Throws:
no such element: Unable to locate element:
{
"method":"xpath",
"selector":"//*[contains(text(), '269424ae-4d74-4a68-91e0-1603f2d674a0')]"
}
(Session info: chrome=74.0.3729.169)
(Driver info:
chromedriver=74.0.3729.6 (255758eccf3d244491b8a1317aa76e1ce10d57e9-refs/branch-heads/3729#{#29}),
platform=Linux 4.18.0-20-generic x86_64)
But it's definitely there and the xpath is valid because I can use AngleSharp to parse the driver's page source with the same xpath expression:
new HtmlParser()
.ParseDocument(driver.PageSource)
.SelectSingleNode($"//*[contains(text(), '{text}')]");
The target element is a div containing a guid:
<div class="home-project-title-text"> 269424ae-4d74-4a68-91e0-1603f2d674a0 </div>
This is with
dotnet core 2.2
chrome webdriver
Chrome 74
Ubuntu 18.04
EDIT1
Interestingly the document.evaluate in the browser console also fails with this xpath expression. I use this as a helper function for running xpath:
selectSingle = xpath => document.evaluate(xpath, document).iterateNext()
and then find that this returns null:
> selectSingle("//*[contains(text(), '269424ae-4d74-4a68-91e0-1603f2d674a0')]")
> null
but it's definitely there and has the expected text, e.g. I can use a different xpath expression to manually locate and check it's text content:
> selectSingle("//*[#id='app']/div/div[1]/div[3]/div/div[1]/div/div[1]/div")
.textContent
.trim()
== "269424ae-4d74-4a68-91e0-1603f2d674a0"
> true
EDIT2
So the cause was that the div was being created in react like this:
React.createElement(
"div",
{className = "home-project-title-text"},
" ",
"269424ae-4d74-4a68-91e0-1603f2d674a0",
" ");
I think this roughly means that the div has three textnodes as children (is that valid?). The result looks 100% normal - it renders perfectly and inspecting the element with devtools looks like a single text node and .textContent returns the concatenated string.
Now that you gave some more information (how this element is created):
Yes, it is possible that an XML element has as its children several separate text nodes. However, this is usually not the case if the text nodes are adjacent to each other, instead of separated by child elements.
If '269424ae-4d74-4a68-91e0-1603f2d674a0' is indeed the second text node, then
//*[contains(text(), '269424ae-4d74-4a68-91e0-1603f2d674a0')]
will indeed not return this div element. You should not think of this as "breaking XPath", it is just that the precise semantics of the expression are:
Find an element with any name whose first text node contains '269424ae-4d74-4a68-91e0-1603f2d674a0'.
text() actually selects all text nodes of an element, but XPath functions such as contains() silenty select only the first one.
What you actually would like to select is
an element with any name where any text node contains '269424ae-4d74-4a68-91e0-1603f2d674a0'
And an expression to achieve exactly that is:
//*[text()[contains(.,'269424ae-4d74-4a68-91e0-1603f2d674a0')]]
You can test those expressions with a small toy document such as:
<div className="home-project-title-text">
<other/>
269424ae-4d74-4a68-91e0-1603f2d674a0
<other/>
</div>
Where other elements are forcing the div element to contain three separate text nodes, two of them containing whitespace only.
Finally, if you already know that the element you are looking for is a div, then you should look specifically for that:
//div[text()[contains(.,'269424ae-4d74-4a68-91e0-1603f2d674a0')]]
It might be the case the element lives in an iframe, if this is the case - you will have to use IWebDriver.SwitchTo() function in order to switch to the required iframe prior to attempting locating the element.
It might be the case the element is not immediately available, i.e. it's being loaded by an AJAX request, in that case you will need to use WebDriverWait class so Selenium could wait till the element appears in DOM prior to interacting with it.
Try the following xpath.See if you get any luck.
//div[#class='home-project-title-text'][contains(.,'269424ae-4d74-4a68-91e0-1603f2d674a0')]
EDIT
//div[contains(.,'269424ae-4d74-4a68-91e0-1603f2d674a0')]

How to select one from duplicate tag in page in java in selenium webdriver

I am using Selenium WebDriver and I have number of items on a page and each item on page is a separate form type.
I have saved all of these form elements in a list and I am iterating over every item in an attempt to get the name of the element by using the "alt" attribute.
However when I try to get the "name" attribute from the input element it is always returning the first input tag found on that page, not the name attribute of the element I have currently selected.
The syntax I am using is:
((Webdriver imgtags.get(i)).findelement(By.xpath("//input[#name='qty']")).sendKeys ("100");
I have also tried to get the id from the tag by using:
((Webdriver imgtags.get(i)).getAttribute("id");
It's returning a blank value, but it should return the value of the id attribute in that input tag.
I also tried to get the id by using .bytagname but as id is an attribute it is not accessible
Try:
(driver) findElement(By.xpath("//*[contains(local-name(), 'input') and contains(#name, 'qty')]")).sendKeys("100");
To answer the comment by #rrd: to be honest, I have no idea why OP uses ((Webdriver imgtags.get(i)). I don't know what that is. Normally, I just use driver.findElement[...]
Hoping that he knows what works in his framework :D
Selenium Xpath handling is not fully compliant and it does not always treat // as a synonym of descendant-or-self.
Instead try tweaking your code to use the following Xpath:
((Webdriver imgtags.get(i)).findElement(By.xpath("./descendant-or-self::input[#name='qty']")).sendKeys("100");
This will base your search off the currently selected WebElement and then look for any descendants that have a name attribute with a value of "qty".
I would also suggest storing your imgtags array as an array of WebElement e.g.
List<WebElement> imgtags = new ArrayList<>();
This is a much better idea than casting to WebDriver to be able to use .findElement(). This will cause you problems at some point in the future.

Issue with send_keys in Selenium

I am trying to fill up textbox using Selenium.
description=driver.find_element_by_xpath("//*[contains(text(), 'add
description')]")
description.click()
description = driver.find_element_by_xpath("//*[#type='text']")
description.send_keys(""+str(description))
For some reason the variable value of description isnt passed to the textbox but webelement id and session id which is :
<selenium.webdriver.remote.webelement.WebElement (session="f2943bc99f2fbbec90c3fa9c0cb68e20", element="0.3535547756904527-5")>
Can anyone help me on this?
try with this code :
description.send_keys("""Just a testing """)
OR
description.send_keys(str(description.text))
If I've understood your code correctly 'description' contains a web element. So your send_keys is essentially typing the web element in string form.
From the (unofficial) documentation:
find_element_by_xpath(xpath)
Finds an element by xpath.
Args: xpath - The xpath locator of the element to find
Returns: WebElement - the element if it was found
Raises: NoSuchElementException - if the element wasn’t found
Usage: element = driver.find_element_by_xpath(‘//div/td[1]’)
http://selenium-python.readthedocs.io/api.html

How to locate an element using selenium webdriver which doesn't have unique identifier like Name, Id, title etc?

I am new to Selenium. Not sure how to handle this scenario. I am working on a website which has several buttons with following code,
<a class="Some big class name" datacommunication="SelectItem" token="some token number" model-id="Id1" element="button">
<i class="classname">Book Ticket</i>
</a>
<a class="Some big class name" datacommunication="SelectItem" token="some token number" model-id="Id2" element="button">
<i class="classname">Book Ticket</i>
</a>
I tried to click on it using following commands,
ele = driver.FindElement(By.ClassName("Some big class name")); but it fails with following message, Compound class names are not supported. Consider searching for one class name and filtering the results.
ele = driver.FindElement(By.CssSelector("a[model-id='Id1']")); fails with 'Test method TestBot.HomeTest.bookTicket threw exception:
OpenQA.Selenium.WebDriverTimeoutException: Timed out after 10 seconds'
Tried using XPATH,
ele = driver.FindElement(By.XPath("\\\a[#model-id='Id1']")); doesn't work either.
I have no control on html. Can't change it.
Please let me know how to identify elements in such scenarios.
You can't have spaces in class names. Those are actually multiple classes separated by a space. You can find the above elements using a css selector
var ele = driver.FindElements(By.CssSelector(".Some.big.class.name"))
Of course, this will find both elements. To find just the first, you could use
var ele = driver.FindElement(By.CssSelector("a[model-id='Id1']"))
You can find help on css selectors here: http://www.w3schools.com/cssref/css_selectors.asp
Update:
I just noticed your XPath appears to have the slashes the wrong way around. If you wish to use XPath, try
//a[#model-id='Id1']
Note, however, that css selectors perform better than XPath.
There are multiple number of ways to locate your WebElement in Selenium WebDriver.
But always remember all are based on you attribute or combination of HTML tags so case could be any of them
1- First way is using id
2- 2nd one is Name
3- Class Name
4- Some time you can used Tagname
5- Some time linkText
6- Some time partial link text
7- Using xpath
8- Using css selector
So in you case we need to take help of Xpath and Css Selector
So xpath of you elements
Syntax : //[#attribute ='value of selected tag']
Example
id1: //a[#model-id='Id1']
id2: //a[#model-id='Id2']
For both element following are the css Selector
Syntax [attribute ='value']
id1:
a[model-id='Id1']
id2:
a[model-id='Id2']
http://www.slideshare.net/weekendtesting/css-selector-29312437
http://www.slideshare.net/weekendtesting/locators-in-selenium-bnt-09
Thanks a lot for help. I have used following code to overcome above mentioned issue,
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(ExpectedConditions.ElementIsVisible(By.CssSelector("a[data-model-id='c5']"))).Click();
With above code, I am able to click on the button.
Thanks again for your help and knowledge sharing.
Amit
You can locate by using xpath.
WebElement ele = driver.findElement(By.xpath("//*[#class='Some big class name']"));
there is difference between findElements and findElement.
FindElement: findElement returns a single element.
FindElements : returns a list of same element.As in this example there are multiple classes with same class name , so use driver.findElements method .
driver.findElements will return a list of all elements with that class name .
Now , you have list of all elements but you want only one of the element.
So iterate over list to get a single element out of a list.
List<WebElement> elementList= driver.FindElement(By.ClassName("Some.big.class.name"));
Iterator itr = elementList.iterator();
while(itr.hasNext())
{
WebElement element = itr.next();
if(element.getAttribute("model-id").equals("Id1")){
element.click();
break;
}//if block ends here
}//while loop ends here
You can also use XPATH , if nothing works
To identify the elements in selenium there are multiple ways.
To see the details please refer BY Class.
Try to find the way which can identify the element uniquely. Start with id if available and if nothing works go for XPATH. XPATH is slower than id and CSS selector.