How to do xpath "and" / condition query correctly? - python

I am trying to set a checkmark on the prime option on amazon.fr.
Here is the link (the prime option is on the left): https://www.amazon.fr/gp/browse.html?node=2036893031.
Here is an image that shows the field I want to checkmark: https://ibb.co/ZY3mK3Z
I have it almost working. But it does not work for all amazon-categories, that's why I added the "and"-operator. Here is my xpath-query:
driver = webdriver.Chrome()
driver.get(category_url)
driver.find_element_by_xpath('//*[#id="leftNav"]/h4[text()="%s"]/following-sibling::ul//input[contains(#name, "s-ref-checkbox-")] and //*[#id="leftNav"]/h4[text()="%s"]/following-sibling::ul//input[contains(#name, "s-ref-checkbox-")]//i[contains(#class, "icon-prime")]' % ("Option d'expédition", "Option d'expédition"))
driver.click()
How can I format my query correctly? Is the and operator even necessary? I get the following error message:
TypeError: Failed to execute 'evaluate' on 'Document': The result is
not a node set, and therefore cannot be converted to the desired type.

I think you are trying to click without passing the WebElement.
You can find the checkbox based on the position of Prime label next to it.
Try the following xpath,
myprime = driver.find_element_by_xpath("//*[contains(#class,'icon-prime a-icon-small s-ref-text-link')]/parent::span/parent::label/input")
myprime.click();

I just tried the XPath below and it uniquely located the element
//label[.//i[contains(#class, 'a-icon-prime')]]/input
^ find a LABEL tag
^ that has a child I tag
^ that contains the class 'a-icon-prime' (indicating the prime logo)
^ then find an INPUT under the LABEL

The minimal working XPath locator would be:
//i[contains(#class,'small') and contains(#class,'prime')]
For better robustness and reliability I would recommend wrapping it into an Explicit Wait like:
prime = WebDriverWait(driver, 10).until(
expected_conditions.presence_of_element_located((By.XPATH, "//i[contains(#class,'small') and contains(#class,'prime')]")))
prime.click()
More information: How to use Selenium to test web applications using AJAX technology

Related

Selenium CSS Selector get only first element while the inspector show 4

I'm trying to automize Linguee dictionary using Selenium.
For instance, in the image I would like to get an array with [de, para, por, con]. In order to get this, I wrote the following code
from selenium import webdriver
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.common.by import By
s=Service(r"C:\Users\Usuario\python\geckodriver.exe")
driver = webdriver.Firefox(service=s)
driver.get('https://www.linguee.fr/frances-espanol/')
consent_button=driver.find_element(By.CSS_SELECTOR, "div[class='sn-b-def sn-blue']")
consent_button.click()
search_input=driver.find_element(By.CSS_SELECTOR, "input[id='queryinput']")
search_input.send_keys("de")
buttonSearch=driver.find_element(By.CSS_SELECTOR, "button[alt='Rechercher dans le dictionnaire français-espagnol']")
buttonSearch.click()
wortType=driver.find_element(By.CSS_SELECTOR, "span[class='tag_wordtype']").text
print( wortType)
translations=driver.find_element(By.CSS_SELECTOR, "div[class='isMainTerm'] a[class='dictLink']").text
print(translations)
The code work correctly, but it returns only the first translations ("en" in the image) while in the browser console I get 4. So, I have been reading and trying differents ways of fix the problem, like change the CSS_SELECTOR
translations=driver.find_element(By.CSS_SELECTOR,"div[class='isMainTerm'] div[class='exact'] div[class='lemma featured'] div[class='lemma_content'] div[gid=0] div[class='translation_lines'] div[class='translation sortablemg featured'] a")
translations=driver.find_element(By.CSS_SELECTOR, "div[class='isMainTerm'] a[class='dictLink']").text
translations=driver.find_element(By.CSS_SELECTOR, "div[class='isMainTerm'] a[class='dictLink']")
translations=driver.find_element(By.CSS_SELECTOR,"div.isMainTerm div.lemma_content a.dictLink").text
translations=driver.find_element(By.CSS_SELECTOR, "div[class='isMainTerm']> a[class='dictLink featured']").text
I have also used XPath but also returns only one.
I have read differents post in stackoverflow with similar problems like this enter link description here, but the problem persists.
Sorry this is long-winded, but can someone guide me to why it's doing this and what my options are?
This is how find_element method works.
It returns the first matching element on the DOM it finds.
So in case you are passing it locator matching several elements on the page the first matching element on the DOM will be returned.
In case you want to get all the elements matching that locator find_elements method should be used instead.
So, in case you want to get all the texts in elements matching div[class='isMainTerm'] a[class='dictLink'] CSS Selector instead of
translations=driver.find_element(By.CSS_SELECTOR, "div[class='isMainTerm'] a[class='dictLink']").text
print(translations)
You can use
translations = driver.find_elements(By.CSS_SELECTOR, "div[class='isMainTerm'] a[class='dictLink']")
for translation in translations:
print(translation.text)
BTW, it is only this CSS Selector locator div[class='isMainTerm'] a[class='dictLink featured'] is matching 4 elements as you mentioned and it looks like this locator you should use here while div[class='isMainTerm'] a[class='dictLink'] locator is matching 81 elements! on that page.

Python Selenium: Selecting from a list by text

I have spent the past few days trying to solve this problem. I store a string in a variable and i am trying to automate using selenium to select the item whose text()='string stored in the variable'.
I noticed i could not select some of the items in the list even though the text was exactly the same as the string i stored in the variable. The i realized today that some of the texts have a space before the next item. For example;
instead of (Architecture)
it says (Architecture )
This is the code i was running;
fc = 'COMMUNICATION'
faculty = WebDriverWait(driver, 50).until(EC.element_to_be_clickable((By.XPATH, '//*[#id="agentApplicationForm'
'"]/div/div[9]/div['
'1]/div/span/span/span[1]')))
print(faculty.is_displayed())
faculty.click()
sleep(5)
faculty_select = driver.find_element_by_xpath(f'//*[#id="Program1FacultyInstitute_listbox"]/li[text().StartsWith("{fc}")]')
faculty_select.click()
sleep(2)
I am now trying to use text().startsWith({fc}) but i don't think i am using a correct syntax.
This is the error i get;
selenium.common.exceptions.InvalidSelectorException: Message: invalid selector: Unable to locate an element with the xpath expression //*[#id="Program1FacultyInstitute_listbox"]/li[text().startsWith("COMMUNICATION")] because of the following error:
SyntaxError: Failed to execute 'evaluate' on 'Document': The string '//*[#id="Program1FacultyInstitute_listbox"]/li[text().startsWith("COMMUNICATION")]' is not a valid XPath expression.
(Session info: chrome=88.0.4324.96)
Can someone help me please.
Thank you very much.
Try this to search text starting with:-
//*[#id="Program1FacultyInstitute_listbox"]/li[starts-with(text(),'fc')]
Or maybe try using contains to do substring match like below. But you will need to pass more accurate text to search in order to avoid multiple matches.
//*[#id="Program1FacultyInstitute_listbox"]/li[contains(text(),'fc')]
if you want to select only (one) element based on (text)
you can use
faculty.click()
faculty.send_keys("value you want to select")
notice that this method works only if you want to select only one value based on string , also it works in droplist.

How can make an Xpath into a loop in python?

I have the following code:
button = driver.find_element_by_xpath("/html/body/div[3]/div/div[1]/div[2]/div[2]/div[2]/div[3]/div[1]")
ActionChains(driver).move_to_element(button).click(button).perform()
the next time i wanna click on this button using selenium the x path changes to
button = driver.find_element_by_xpath("/html/body/div[3]/div/div[1]/div[2]/div[2]/div[2]/div[3]/div[2]")
ActionChains(driver).move_to_element(button).click(button).perform()
I wanna convert this into a for look in the range(1,5). I used the below code:
l=[1,2,3,4,5]
string=str(l)
for i in string:
button = driver.find_element_by_xpath("/html/body/div[3]/div/div[1]/div[2]/div[2]/div[2]/div[3]/div"+i)
ActionChains(driver).move_to_element(button).click(button).perform()
time.sleep(5)
but I am getting the following error:
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.InvalidSelectorException: Message: invalid selector: Unable to locate an element with the xpath expression /html/body/div[3]/div/div[1]/div[2]/div[2]/div[2]/div[3]/div[ because of the following error:
SyntaxError: Failed to execute 'evaluate' on 'Document': The string '/html/body/div[3]/div/div[1]/div[2]/div[2]/div[2]/div[3]/div[' is not a valid XPath expression.
(Session info: chrome=83.0.4103.61)
Having large xpaths like that are fragile and difficult to navigate. You would probably have more success with a smaller, more accurate xpath.
Addressing your issue:
If you look at your error SyntaxError: Failed to execute 'evaluate' on 'Document': The string '/html/body/div[3]/div/div[1]/div[2]/div[2]/div[2]/div[3]/div[' is not a valid XPath expression.
This is not truncated. This is the xpath you're looking for - You're ending it with an open square bracket so it's not valid.
Have a look at making this xpath string valid before you feed it into find_by.
[edit - URL provided in the comments]
Looking at the page provided, try:
button = driver.find_element_by_xpath("//input[#class='amscroll-load-button']")
ActionChains(driver).move_to_element(button).click(button).perform()
It's a smaller xpath that goes right to the object without navigating the entire DOM.
When I run that xpath it always identifies just 1 object. Even if you click it and the page moves, wherever it moves still it still only identifies one.
If you want to use this, be aware there's an asynchronous page load when you click it. You'll also need a dynamic wait to let the data/page to load before you press it another time.
If you want to just see if it works, the lazy approach is to enter a few seconds of sleep/wait between the click commands.
If you want to know more about finding xpaths using devtools - have a look at this resource. Few videos at 10 minutes each can save a lot of headache on how to get the right objects.

Looping through webelements with selenium Python

I am currently trying to automate a process using Selenium with python, but I have hit a roadblock with it. The list is part of a list which is under a tree. I have identified the base of the tree with the following xpath
item = driver.find_element_by_xpath("//*[#id='filter']/ul/li[1]//ul//li")
items = item.find_elements_by_tag_name("li")
I am trying to Loop through the "items" section but need and click on anything with an "input" tag
for k in items:
WebDriverWait(driver, 10).until(EC.element_to_be_clickable((k.find_element(By.TAG_NAME, "input")))).click()
When execute the above I get the following error:
"TypeError: find_element() argument after * must be an iterable, not WebElement"
For some reason .click() will not work if I use something like the below.
k.find_element_by_tag_name("input").click()
it only works if i use the webdriverwait. I have had to use the web driver wait method anytime i needed to click something on the page.
My question is:
What is the syntax to replicate items = item.find_elements_by_tag_name("li")
for WebDriverWait(driver, 10).until(EC.element_to_be_clickable((k.find_element(By.TAG_NAME, "input")))).click()
i.e how do I use a base path and append to the using the private methods find_elements(By.TAG_NAME)
Thanks in advance
I have managed to find a work around and get Selenium to do what i need.
I had to call the javascript execution, so instead of trying to get
WebDriverWait(driver, 10).until(EC.element_to_be_clickable((k.find_element(By.TAG_NAME, "input")))).click() to work, i just used
driver.execute_script("arguments[0].click();", k.find_element_by_tag_name("input"))
Its doing exactly what I needed it to do.

selenium get element by css selector

I am trying to get user details from each block as given
driver.get("https://www.facebook.com/public/karim-pathan")
wait = WebDriverWait(driver, 10)
li_link = []
for s in driver.find_elements_by_class_name('clearfix'):
print s
print s.find_element_by_css_selector('_8o._8r.lfloat._ohe').get_attribute('href')
print s.find_element_by_tag_name('img').get_attribute('src')
it says:
unable to find element with css selector
any hint appreciable.
Just a mere guess based on assumption that you are not logged in. You are getting exception cause for all class clearfix, element with ._8o._8r.lfloat._ohe does not exists. So your code isn't reaching the required elements. Anyhow, if you are trying to fetch href and img source of results, you need not iterate over all clearfix cause as suggested by #leo.fcx, your css is incorrect, trying the css provided by leo, you can achieve the desired result as:
driver.get("https://www.facebook.com/public/karim-pathan")
for s in driver.find_elements_by_css_selector('._8o._8r.lfloat._ohe'): // there didn't seemed to iterate over each class of clearfix
print s.get_attribute('href')
print s.find_element_by_tag_name('img').get_attribute('src')
P.S. sorry for any syntax, never explored python binding :)
Since you are using all class-names that the element applies, adding a . to the beginning of your CSS selector should fix it.
Try this:
s.find_element_by_css_selector('._8o._8r.lfloat._ohe')
instead of:
s.find_element_by_css_selector('_8o._8r.lfloat._ohe')
Adding to what #leo.fcx pointed about the selector, wait for search results to become visible:
wait.until(EC.visibility_of_element_located((By.ID, "all_search_results")))

Categories

Resources