Cannot click a button because Selenium is unable to locate an element - python

I have tried to make my script click the Purchase/Buy Family button on the Spotify Checkout Page. No matter what class, CSS, XPath, ID, or whatever I put in, it's just saying it could not find the object.
This is the button. It's not in an iframe:
<div class="sc-fzXfOu cvoJMt">
<button id="checkout_submit" class="Button-oyfj48-0 kaOWUo sc-fzXfOv tSdMK">
Buy Premium Family
</button>
</div>
My code:
time.sleep(3)
buy = driver.find_element_by_xpath("/html/body/div[3]/div/div/div/div/div/div/div[3]/div/div/div[2]/form/div[2]/button").click()

I am able to click the button,by a different xpath
driver.findElement(By.xpath("//button[#id='checkout_submit']")).click();
Edit -
Your xpath also works for me only when i load the page initially and there is no change in the dom - /html/body/div[3]/div/div/div/div/div/div/div[3]/div/div/div[2]/form/div[2]/button
It doesn't work when some new event or error is displayed and the dom structure changes.
Why use such relative XPath when the element has distinguishable attributes?

The problem here is the form is not static you have to wait loading of all elements.
And page loads with pane to accept cookies who can mask elements you want to run action on.
The best way in that case is first accept cookies and then run all actions you need.
Try to adapt this code for your needs, that run with success on my test.
In case you run this code you have to brake execution to login first, when the driver get the page.
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.remote.webelement import WebElement
# change this line
path_driver = "your_path_to_chrome_driver"
by, buy_selector, cookies_selector = By.CSS_SELECTOR, 'button#checkout_submit', "button#onetrust-accept-btn-handler"
driver = webdriver.Chrome(path_driver)
driver.maximize_window()
actions = ActionChains(driver)
driver.get("https://www.spotify.com/us/purchase/offer/premium-family/?country=US")
# wait for loading buy button
sls = wait.until(EC.presence_of_all_elements_located((by, buy_selector)))
if sls:
# get accept cookies button element and click
cookies_accept = driver.find_element_by_css_selector(cookies_selector)
if isinstance(cookies_accept, WebElement):
cookies_accept.click()
# get buy button element, move to element and click
buy = driver.find_element_by_css_selector(buy_selector)
if isinstance(buy, WebElement) and buy.is_displayed() and buy.is_enabled():
actions.move_to_element(buy).click(buy).perform()

Related

Cannot locate form-control object to send_keys using python Selenium

I am trying to navigate a scheduling website to eventually auto populate a schedule using the following script:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
# Create a Chrome webdriver
driver = webdriver.Chrome(r'C:\Users\chromedriver_win32\chromedriver.exe')
# Navigate to https://www.qgenda.com/
driver.get('https://www.qgenda.com/')
# Wait for the page to load
driver.implicitly_wait(5) # 5 seconds
# You can now interact with the page using the webdriver
# Locate the sign in button
sign_in_button = driver.find_element(By.XPATH,'/html/body/div[1]/div/header[3]/div/div[3]/div/div/div/div/a')
# Click the sign in button
sign_in_button.click()
# Find the input element
input_email = driver.find_element(By.XPATH,'//*[#id="Input_Email"]')
# Send text
input_email.send_keys('Josh')
However, I cannot seem to find the Input_Email object. I've tried all the Xpaths and Id's that make sense and also tried waiting until the object is clickable with no luck. Would really appreciate some guidance on this.
I was expecting Selenium to find the html object form box and pass in text but instead I get an error:
NoSuchElementException: no such element: Unable to locate element: {"method":"xpath","selector":"//*[#id="Input_Email"]"}
even though the Xpath definitely exists.
The XPath seems fine. I am guessing you need to do some explicit wait or implicit wait to ensure the page is fully loaded before allocating the element.
Another thing I would like to point out is that given the login URL is available. Locating the sign in button seems to be redundant. You can access it directly via driver.get('https://login.qgenda.com/')
For instance,
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver.get('https://login.qgenda.com/')
input_email = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, '//*[#id="Input_Email"]'))
)
input_email.send_keys('Josh')
You can read more about it here.

Python Selenium. Why does the button press send the bot to the home page instead of the intended checkout page?

I'm making a buying bot with python selenium. on this website, when you press Köp (Buy in swedish) you can see that the product list is loading the product you just put in the cart. The Bot is pressing the buy button, then it is supposed to press the Till Kassan (To checkout) button and then register information. The problem I'm having is that the bot presses the checkout button quicker than the product itself loads in the cart, so instead of going to checkout, it sends the bot to the home page.
There is no error being shown (besids the bluetooth default adapter failed, which I don't think affects anything), and I have tried to add an wd.implicitly_wait(10) before the button click, which does nothing.
Here is the code:
from selenium import webdriver as wd
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import chromedriver_binary
# Open the browser and the website
options = wd.ChromeOptions()
options.add_argument("start-maximized")
options.add_argument("disable-infobars")
options.add_argument("--disable-extensions")
wd = wd.Chrome(options=options)
wd.get("https://www.inet.se/produkt/5410779/lian-li-grafikkortshallare")
wd.implicitly_wait(10)
# Click the BUY button
wd.find_element_by_xpath('//*[#id="react-root"]/div[3]/div/div[2]/div[2]/article/div[2]/section[2]/div/button').click()
# Click the GO TO CHECKOUT button when it loads in a popup
WebDriverWait(wd, 10).until(EC.element_to_be_clickable((By.XPATH, '//*[#id="react-root"]/div[2]/nav/div/div/div[2]/div/div/div/a'))).click()
It does seem working fine when you press the checkout button with your mouse, but I assume that the bot does it too quickly and breaks it. There must be a way to make a timer somehow or wait untill the product loads, but I don't know how to do that.
As I was writing this question I found this answer, and I thought why I don't answer it.
On this website about how to wait on elements in selenium python there are a lot of different types of waits. In this specific case you need to use the explicit wait, meaning that the bot will wait for the product list to load first and then press the checkout button.
Here is the code:
from selenium import webdriver as wd
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import chromedriver_binary
# Open the browser and the website
options = wd.ChromeOptions()
options.add_argument("start-maximized")
options.add_argument("disable-infobars")
options.add_argument("--disable-extensions")
wd = wd.Chrome(options=options)
wd.get("https://www.inet.se/produkt/5410779/lian-li-grafikkortshallare")
wd.implicitly_wait(10)
wd.find_element_by_xpath('//*[#id="react-root"]/div[3]/div/div[2]/div[2]/article/div[2]/section[2]/div/button').click()
# This will wait untill an element inside the products element in the ul list loads, ensuring that the item has loaded completely.
WebDriverWait(wd, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[#id="react-root"]/div[2]/nav/div/div/div[2]/div/div/div/div[1]/ul/li/div/article/div[4]/button')))
wd.find_element_by_xpath('//*[#id="react-root"]/div[2]/nav/div/div/div[2]/div/div/div/a').click()
Another important thing to note, is that you can't wait in this case for the li element to load, because in some cases the li element of the product loads before the rest of the information, still sending the bot to the home page. I fixed that by using the XPath of an element inside of that particular li element.

Getting next page data through selenium and request

I'm looking to click on the next page to get the listed product data: https://www.riteaid.com/shop/grocery
When I look at the HTML element, the Next button is actually not a button element, but a span tag. Looking at the developer tools, I noticed that there is a tag that dynamically changes when I click on the next button without moving to a different url.
<div data-pager="p:pg1" data-tp="89" data-cp="2">
How would one go about getting the next page data or send the request to get the next page data? I thought I'd need selenium since it is interactive, but I got the error "selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable".
Thanks!
You can invoke click onto the element.
driver.get("https://www.riteaid.com/shop/grocery")
wait = WebDriverWait(driver, 10)
elem = wait.until(EC.presence_of_element_located((By.XPATH, "//span[text()='Next']/parent::div")))
driver.execute_script("arguments[0].click();", elem)
Import
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
I checked the site and you can navigate to next page like this:
next_page_xpath = "//div[#class='rfk_next']"
driver.find_element_by_xpath(next_page_xpath).click()
Do remember to do while and put above code in try/except, so that it breaks from while loop once there will be no next page.

How to select an option from dropdown

I have a problem navigating a website using selenium. This is my code:
from selenium import webdriver
driver = webdriver.Firefox()
driver.get('https://webtrader.binary.com/v2.2.8/main.html#')
resources = driver.find_element_by_id('ui-id-1')
resources.click()
However, I get the exception:
selenium.common.exceptions.ElementNotInteractableException: Message: Element <ul id="ui-id-1" class="ui-menu ui-widget ui-widget-content ui-menu-icons"> could not be scrolled into view
I don't understand where I went wrong. I am trying to access 'Historical data' from the dropdown menu labeled "Resources". Could someone please help me access it. Maybe I got the id for Resources wrong. You could also check that out.
The element you want to click to open the dropdown is the previous sibling of the element resources
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.select import By
from selenium.webdriver.support.ui import WebDriverWait
wait = WebDriverWait(driver, 10)
driver.find_element_by_class_name('resources').click()
historical_data = wait.until(ec.visibility_of_element_located((By.ID, 'ui-id-4')))
There are many things happening here. First of all, your code is lacking a wait. Without it will always fail, as the page is dynamically loaded. Read about Waits.
Secondly, here resources = driver.find_element_by_id('ui-id-1') you are finding the element from the dropdown menu, and then you are trying to click it. But the dropdown menu is not opened. You should click on it, then wait for the option to appear, only then click on the 'Historical data'.

Selenium with Python : how to click dojo combobox values

I'm navigating a JS-heavy webpage with Selenium and I need to be able to interact with a dojo component on the page. The page I'm looking at has a dojo dijit form with a combobox that has subject names for my university. I want to expose and iteratively click on every item in the list in order to scrape the course names for that subject when it redirects. The list items are exposed when the dropdown arrow button is clicked.
the url I'm automating: http://sis.rutgers.edu/soc/#subjects?semester=12020&campus=NB,NK,CM&level=U,G
I'm inspecting the element for the dropdown button and copying the XPath.
dropdownButton = driver.find_element_by_xpath('//*[#id="widget_dijit_form_FilteringSelect_0"]/div[1]/input')
Running this yields:
NoSuchElementException: Message: no such element: Unable to locate
element:
{"method":"xpath","selector":"//*[#id="widget_dijit_form_FilteringSelect_0"]/div[1]/input"}
EDIT: I've made some progress, turn out the element wasn't rendered by the time find_by_xpath was called. I added a wait in my program, and now Selenium is able to locate and click the drowdown button.
Use WebDriverWait to wait require element conditions. Dropdown disappears on any action on the page, that's why to get option locator you can do one of the following:
all options loaded after first expand, that's why you can search option element by text in chrome dev tools and get locator
pause and inspect the element.
You can google best practice for locators,here and here.
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
wait = WebDriverWait(driver, 10)
with driver:
driver.get("http://sis.rutgers.edu/soc/#subjects?semester=12020&campus=NB,NK,CM&level=U,G")
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "#filteringSelectDiv .dijitArrowButtonInner"))).click()
options = driver.execute_script('return [...arguments[0]].map(e=>e.textContent)',
wait.until(EC.presence_of_all_elements_located(
(By.CSS_SELECTOR, ".dijitComboBoxMenuPopup .dijitMenuItem[item]"))))
for option in options:
driver.find_element_by_css_selector(".dijitInputInner").clear()
driver.find_element_by_css_selector(".dijitInputInner").send_keys(option, Keys.TAB)
wait.until(lambda d: d.execute_script("return document.readyState === 'complete'"))
# collect data

Categories