I'm playing around with booking.com as I teach myself selenium / python. I'm trying to select a currency based on a known string e.g. 'USD'.
I'm able to click the currency icon to open the currency selection modal but then when I try to say find element where text = 'USD', I get:
selenium.common.exceptions.InvalidSelectorException: Message: invalid selector: An invalid or illegal selector was specified
1. Here is the list item for 'USD':
<li class="ccff2b4c43 ea27cffb06"><button data-testid="selection-item" type="button" class="fc63351294 ea925ef36a bf97d4018a ae8177da1f cddb75f1fd"><div class="a1b3f50dcd a1f3ecff04 b2fe1a41c3 db7f07f643 d19ba76520"><div class="b1e6dd8416 aacd9d0b0a"><span class="cf67405157">U.S. Dollar<div class=" ea1163d21f">USD</div></span></div><div class=""></div></div></button></li>
2. Here is my known string I'm trying to search for:
currency = 'USD'
3. Here is my attempt at trying to find (then select) this string in the HTML:
selected_currency_element = self.find_element(By.CSS_SELECTOR, f'//span[text()={currency}]')
selected_currency_element.click()
I assume I don't need to read this modal's entire unordered list into an array then loop through it to find the string? Or do I?
Couple of things to rectify.
The used syntax is for XPATH and you have used CSS_SELECTOR need to change to XPATH
selected_currency_element = self.find_element(By.XPATH, f'//span[text()={currency}]')
If you see the node USD is inside a DIV tag of parent SPAN
So your xpath should be like
selected_currency_element = self.find_element(By.XPATH, f'//span[.//div[text()="{currency}"]]')
Instead of using the abbreviation, I used the long name. This works pretty good. By the way, I use Firefox as my browser
from selenium.webdriver.common.by import By
from selenium import webdriver
def change_currenecy(country):
dvr = webdriver.Firefox()
dvr.get("https://www.booking.com/")
dvr.find_elements(By.XPATH, "//span[contains(text(),'USD')]")[0].click()
dvr.find_elements(By.XPATH, f"//span[contains(text(),'{country}')]")[0].click()
change_currenecy("Thai Baht")
You need to take care of a couple of things here:
Though you mentioed By.CSS_SELECTOR but the value is of a xpath. So you need to change the locator strategy as:
selected_currency_element = self.find_element(By.XPATH, f'xpath_value')
The desired text USD is within the descendant <div> of it's parent <span>, so you need to go a step deeper.
Solution
Your effective line of code will be:
currency = 'USD'
selected_currency_element = self.find_element(By.XPATH, f'//span//div[text()={currency}]')
selected_currency_element.click()
Related
I am trying to get the text that is located outside of this label and inside the div
<div style="color:red;">
<label> Total Amount Due:</label>
$0.00
</div>
Here all I am trying to extract is the $0.00
I have tried using the xpath of the div to get the number but it still includes the text of the label saying Total Amount Due: $0.00
amount_finder =driver.find_elements_by_xpath("//body[1]/div[4]/div[1]/div[2]/div[2]/div[3]/div[1]/div[1]/div[1]/font[1]/div[1]/table[2]/tbody[1]/tr[1]/td[1]/div[10]")
for amount in amount_finder:
print(amount.text)
I can't seem to figure out a way to just get the $0.00. I am new to to this and having trouble search previous posts on here and applying it to my situation. I see various recommendations of using following-sibling but can't get that to work.
You can use text() node of XPath:
//div/text()[2]
but, since it is not an element but a text node, selenium cannot process it. So the solution is to run XPath by Javascript:
node_text = driver.execute_script("return document.evaluate('//div//text()[2]', document).iterateNext().textContent;")
It is because you are looping through all the elements in amount_finder
You just need to get the text from the div element itself.
amount = driver.find_element(By.XPATH, '//body[1]/div[4]/div[1]/div[2]/div[2]/div[3]/div[1]/div[1]/div[1]/font[1]/div[1]/table[2]/tbody[1]/tr[1]/td[1]/div[10]').text()
print(amount)
You can also use simply use Xpath or Query Selector instead of Full Xpath.
Use from selenium.webdriver.common.by import By, if you are running this code. Using By is much easier compared to old find_elements_by_xpath
I am trying to get Selenium to do a Youtube search for me and I got to the final step which is actually entering the text and I'm stuck. searchElem uses the id that allows me to use .click(), but I cannot use send_keys() with that id so I tried inputElem and that doesn't work either.
There isn't a more specific id or class to use for the search input so I'm not sure what to do.
Below is the error I get once I try to use send_keys() with searchElem and inputElem.
selenium.common.exceptions.ElementNotInteractableException: Message: Element <div id="search-input" class="ytd-searchbox-spt"> is not reachable by keyboard
This is my code for the WebDriver and elements within the source code.
driver = webdriver.Firefox()
driver.get('https://www.youtube.com')
searchElem = driver.find_element_by_id('search-input')
inputElem = driver.find_element_by_id('search')
searchElem.click()
searchElem.send_keys('election')
It's possible that 'search' is the ID of a different element that is being picked up, so try using the xpath instead.
inputElem = browser.find_element_by_xpath('/html/body/ytd-app/div/div/ytd-masthead/div[3]/div[2]/ytd-searchbox/form/div/div[1]/input')
inputElem.send_keys('election')
You can create the locator based on search field placeholder (this would make it unique)
For example, for english, the placeholder would be "Search" and your locator would look like:
//input[#placeholder='Search']
I sadly couldn't find any resources online for my problem. I'm trying to store elements found by XPath in a list and then loop over the XPath elements in a list to search in that object. But instead of searching in that given object, it seems that selenium is always again looking in the whole site.
Anyone with good knowledge about this? I've seen that:
// Selects nodes in the document from the current node that matches the selection no matter where they are
But I've also tried "/" and it didn't work either.
Instead of giving me the text for each div, it gives me the text from all divs.
My Code:
from selenium import webdriver
driver = webdriver.Chrome()
result_text = []
# I'm looking for all divs with a specific class and store them in a list
divs_found = driver.find_elements_by_xpath("//div[#class='a-fixed-right-grid-col a-col-left']")
# Here seems to be the problem as it seems like instead of "divs_found[1]" it behaves like "driver" an looking on the whole site
hrefs_matching_in_div = divs_found[1].find_elements_by_xpath("//a[contains(#href, '/gp/product/')]")
# Now I'm looking in the found href matches to store the text from it
for href in hrefs_matching_in_div:
result_text.append(href.text)
print(result_text)
You need to add . for immediate child.Try now.
hrefs_matching_in_div = divs_found[1].find_elements_by_xpath(".//a[contains(#href, '/gp/product/')]")
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")
I am trying to extract NBA players stats using selenium web driver in python and here is my attempt:
from selenium import webdriver
from selenium.webdriver.support.ui import Select
browser = webdriver.Chrome()
browser.get('https://www.basketball-reference.com')
xp_1 = "//select[#id='selector_0' and #name='team_val']"
team = Select(browser.find_element_by_xpath(xp_1))
team.select_by_visible_text('Golden State Warriors')
xp_2 = "//select[#id='selector_0' and #name='1']"
player = Select(browser.find_element_by_xpath(xp_2))
player.select_by_visible_text('Jordan Bell')
The problem I have is there are 4 "Go" buttons in this page and all have the same input features. In other words, the following xpath returns 4 buttons:
//input[#type='submit'and #name="go_button" and #id="go_button" and #value="Go!"]
I unsuccessfully tried adding ancestor as below but it does not return an xpath:
//input[#type='submit' and #name="go_button" and #id="go_button" and #value="Go!"]/ancestor::/form[#id='player_roster']
I appreciate any insight!
Try below XPAth to select required Go button:
"//input[#value='Go!' and ancestor::form[#id='player_roster']]"
or
"//form[#id='player_roster']//input[#value='Go!']"
Note that you shouldn't mix single and double quotes in a XPath expression and correct usage of ancestor axis is
//descendant_node/ancestor::ancestor_node
You could also switch to CSS selectors and use a descendant combination where you use an parent element to restrict to the appropriate form with Go button
#player_roster #go_button
That is
browser.find_element_by_css_selector("#player_roster #go_button")
The # is an id selector.
CSS selectors are generally faster than XPath, except in cases of older IE versions. More info.