Unable to let my script slide a button to the right - python

I've written a script in python in combination with selenium to log in to a website. The thing is my script sometimes successfully gets logged in but most of the times it comes across a slider which is meant to press and slide to the right.
Website link
Image of that slider:
How can I let my script slide that button to the right?
I've tried with:
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def sign_in():
driver.get("https://login.aliexpress.com/")
wait.until(EC.frame_to_be_available_and_switch_to_it(wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#alibaba-login-box")))))
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "input#fm-login-id"))).send_keys("someEmail")
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "input#fm-login-password"))).send_keys("somePassword")
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "button[class$='password-login']"))).click()
#the following line is for handling the slider but it doesn't do anything
item = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".nc_wrapper .btn_slide")))
ActionChains(driver).move_to_element(item).perform()
if __name__ == '__main__':
driver = webdriver.Chrome()
wait = WebDriverWait(driver,10)
sign_in()
Html elements connected to that slider:
<div id="nc_1_n1t" class="nc_scale">
<div id="nc_1__bg" class="nc_bg" style="width: 0px;"></div>
<span id="nc_1_n1z" class="nc_iconfont btn_slide" data-spm-anchor-id="0.0.0.i1.3f9579f4qCwuHp" style="left: 0px;"></span>
<div id="nc_1__scale_text" class="scale_text slidetounlock"><span class="nc-lang-cnt" data-nc-lang="_startTEXT">Please slide to verify</span></div>
<div id="nc_1_clickCaptcha" class="clickCaptcha" style="top: -118px; height: 235px;">
<div class="clickCaptcha_text">
<b id="nc_1__captcha_text" class="nc_captch_text"></b>
<i id="nc_1__btn_2" class="nc_iconfont nc_btn_2 btn_refresh"></i>
</div>
<div class="clickCaptcha_img"></div>
<div class="clickCaptcha_btn"></div>
</div>
<div id="nc_1_imgCaptcha" class="imgCaptcha" style="top: -118px; min-height: 290px; height: 189px;">
<div class="imgCaptcha_text"><input id="nc_1_captcha_input" maxlength="6" type="text" style="ime-mode:disabled"></div>
<div class="imgCaptcha_img" id="nc_1__imgCaptcha_img"></div>
<i id="nc_1__btn_1" class="nc_iconfont nc_btn_1 btn_refresh" onclick="document.getElementById('nc_1__imgCaptcha_img').children[0].click()"></i>
<div class="imgCaptcha_btn">
<div id="nc_1__captcha_img_text" class="nc_captcha_img_text"></div>
<div id="nc_1_scale_submit" class="nc_scale_submit"></div>
</div>
</div>
<div id="nc_1_cc" class="nc-cc"></div>
<i id="nc_1__voicebtn" tabindex="0" role="button" class="nc_voicebtn nc_iconfont" style="display:none"></i>
<b id="nc_1__helpbtn" class="nc_helpbtn"><span class="nc-lang-cnt" data-nc-lang="_learning">help</span></b>
</div>

I'm unable to get the slider to display on the website linked in the question, so I have provided a solution using another site that has a slider element (I'm assuming the functionality is similar to the one on the aliexpress website).
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
# Instantiate objects
driver = webdriver.Chrome()
actions = ActionChains(driver)
# Load page and fill in input elements
driver.get("http://kthornbloom.com/slidetosubmit/")
driver.find_element(By.NAME, "name").send_keys("Fred")
driver.find_element(By.NAME, "email").send_keys("fred#example.com")
# Find slider elements
slider_container = driver.find_element(By.CSS_SELECTOR, ".slide-submit")
slider = driver.find_element(By.CSS_SELECTOR, ".slide-submit-thumb")
# Perform sliding action
actions.move_to_element(slider).click_and_hold().move_by_offset(slider_container.size['width'], 0).release().perform()
# Browser intentionally left open so that you can see what happened when the test was run!
The key part here, is the Perform sliding action line. We have to identify two specific elements:
The element that we are going to slide
The container that holds this element
Once we have found both of these elements, we use the actions class to click and hold the element we need to slide and then we slide it the width of the containing element along the x axis (as shown using slider_container.size['width']), without changing the y axis.
This should fix your sliding problem, however you will have another problem, you need to work out if the slider was displayed, or you were logged into the website. To do that you are going to need an Expected condition that checks for the existence of 2 elements:
The slider.
An element you would expect to see when you have successfully logged in.
If the element you would expect to see when you log in is shown, you don't need to do anything. If the slider is shown, you will need to perform the above logic to slide the bar across.
*Edit*
To improve on this a bit more you can put the sliding code into an ExpectedCondition like so:
class wait_for_element_while_verifying_slider(object):
def __init__(self, locator, slider_container_locator, slider_locator):
self.locator = locator
self.slider_container_locator = slider_container_locator
self.slider_locator = slider_locator
def __call__(self, _driver):
try:
return _driver.find_element(*self.locator)
except (NoSuchElementException, StaleElementReferenceException):
container = _driver.find_elements(*self.slider_container_locator)
slider = _driver.find_elements(*self.slider_locator)
if len(container) > 0 and len(slider) > 0:
actions = ActionChains(_driver)
actions.move_to_element(slider[0]).click_and_hold().move_by_offset(container[0].size['width'], 0).release().perform()
return False
This will search for an element that you expect to be shown when you log in. If the element that you would expect to see when you have successfully logged in is not shown, it will then try and locate the slider elements and interact with them to process the slide verification. Note that once it tries to perform the slide verification the Expected Condition will return False which will force it to check to see if the expected logged in element is displayed again.
You can use it in your code like this:
# Instantiate objects
driver = webdriver.Chrome()
wait = WebDriverWait(driver, 10)
# Load page and fill in input elements
driver.get("http://kthornbloom.com/slidetosubmit/")
driver.find_element(By.NAME, "name").send_keys("Fred")
driver.find_element(By.NAME, "email").send_keys("fred#example.com")
# Define slider elements and element that will be shown when you successfully log in
SLIDER_CONTAINER = (By.CSS_SELECTOR, ".slide-submit")
SLIDER = (By.CSS_SELECTOR, ".slide-submit-thumb")
ELEMENT_TO_FIND = (By.XPATH, "//div[.=\"Looks Like You're Human!\"]")
# Invoke the explicit wait that will deal with the slider if it is displayed
wait.until(wait_for_element_while_verifying_slider(ELEMENT_TO_FIND, SLIDER_CONTAINER, SLIDER))
The example above uses the same example website as above. To make it timeout you can change ELEMENT_TO_FIND to something that doesn't exist. To make it pass without sliding you can modify ELEMENT_TO_FIND to be:
ELEMENT_TO_FIND = (By.XPATH, "//div[.=\"Slide To Submit\"]")
Using the code you have provided in your comment I would expect the following to work on the aliexpress website:
# Define slider elements and element that will be shown when you successfully log in
SLIDER_CONTAINER = (By.CSS_SELECTOR, ".nc-lang-cnt")
SLIDER = (By.CSS_SELECTOR, ".nc_wrapper .btn_slide")
ELEMENT_TO_FIND = (By.ID, "search-key")
# Invoke the explicit wait that will deal with the slider if it is displayed
wait.until(wait_for_element_while_verifying_slider(ELEMENT_TO_FIND, SLIDER_CONTAINER, SLIDER))

Try using Actions Below is c# code for reference
//following code will click and hold the slider
string Xpath=""; //set xpath for desired element to be click and hold
Actions clickHold = new Actions(driver);
IWebElement element = driver.FindElement(By.XPath(Xpath));
clickHold.ClickAndHold(element).Perform();
once you have held on slider try to move it to the desired offset value
int x = 100;
int y = 100;
Actions moveOffset = new Actions(driver);
moveOffset.MoveByOffset(x,y).Perform(); //set your suitable (x,y) offset value

The sample code (in java) for slider operation from left to right is below.
Actions slider=new Actions(driver);
slider.clickAndHold("xpath of slider");
slider.movebyoffset(x,y).build.perform();
in place of 'x' &'y' you can give actual offset value as per your application.

Related

How do you click an expander button in Python with Selenium?

I am working on a script for selenium to open a webpage, and click a series of buttons. The first button is an Expander button, shown in the below image
The HTML code from inspecting the button is below, but the button itself is "class="dojoxExpandoIcon dojoxExpandoIconLeft qa-button-toc"
<div class="dojoExpandoPaneInner">
<div data-dojo-attach-point="titleWrapper" class="dojoxExpandoTitle">
<div class="dojoxExpandoContainer">
<div class="dojoxExpandoIcon dojoxExpandoIconLeft qa-button-toc" data-dojo-attach-point="iconNode" data-dojo-attach-event="ondijitclick:_onIconNodeClick" tabindex="0" aria-controls="slideout"><span class="a11yNode">X</span></div>
</div>
The xpath as well, is as follows:
/html[#class='dj_webkit dj_chrome dj_contentbox has-webkit has-no-quirks svg']/body[#class='claro original']/div[#id='border']/div[#id='slideout']/div[#class='dojoExpandoPaneInner']/div[#class='dojoxExpandoTitle']/div[#class='dojoxExpandoContainer']/div[#class='dojoxExpandoIcon dojoxExpandoIconLeft qa-button-toc']
To find this button, I tried the following code one at a time.
driver.find_element_by_css_selector("div[class^='dojoxExpandoIcon']") # find expander by class name
# driver.find_element_by_css_selector("div[class^='dojoxExpandoIcon dojoxExpandoIconLeft qa-button-toc']") # find expander by class name
# driver.find_element_by_css_selector("div.dojoxExpandoContainer")
However, none of them have worked at all, and result in errors such as
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"div.dojoxExpandoIcon.dojoxExpandoIconLeft.qa-button-toc"}
Is there anything I'm doing wrong here?
Try waiting until the locator is clickable:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 15)
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".dojoxExpandoIcon.dojoxExpandoIconLeft.qa-button-toc")))
button = driver.find_element_by_css_selector(".dojoxExpandoIcon.dojoxExpandoIconLeft.qa-button-toc").click()
If the number of classes is not stable, remove the ones that are changing and leave only stable ones, for example:
.dojoxExpandoIconLeft.qa-button-toc
Here I just removed one of classes.
Read more about waits here https://selenium-python.readthedocs.io/waits.html.

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

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()

Click on dropdown list in AngularJS for elements other than select using Python and Selenium

I'm using Selenium to scrape this web page. In the page I can click on a button and a dropdown list is displayed. I want to be able to select different values from the list in my program.
After a lot of research I found out that normally there is a select element in the html, but this is not the case. I'm certain enough that the element that should be clicked is the following one:
<div class="ui-select-container ui-select-bootstrap dropdown ng-valid ng-touched" ng-class="{open: $select.open}" ng-disabled="form.disabledSemanal" ng-change="getSemanaByYear(form.agno.code, form.fechaTermino)" tagging="" tagging-label="('new')" ng-model="form.agno" style="width: 70px" theme="bootstrap" title="Seleccionar año">
Another suspect I have is:
<i class="caret pull-right" ng-click="$select.toggle($event)"></i>
Which is the little arrow of the button.
I can find both of these elements with the following code:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
url = "https://www.odepa.gob.cl/precios/precios-al-consumidor-en-linea"
driver = webdriver.Firefox()
driver.get(url)
# I have to go through an iframe first
frame_src = "'//aplicativos.odepa.gob.cl/precio-consumidor/serie-precio'"
frame = driver.find_element_by_xpath('//iframe[#src='+frame_src+']')
driver.switch_to.frame(frame)
# Now I find both elements
element1 = driver.find_element_by_xpath("//div[#class='ui-select-container ui-select-bootstrap dropdown ng-valid']")
element2 = driver.find_element_by_xpath("//i[#class='caret pull-right']")
As these elements are not select elements, I cannot use the handy:
from selenium.webdriver.support.ui import Select
select = Select(element1)
select.select_by_visible_text("Some_value")
One solution out there is to use element1.click(), but no dropdown list is displayed.
Another solution is to wait to click as in:
element = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[#class='form-control ui-select-search ng-pristine ng-untouched ng-valid ng-hide']")))
element.click()
But I don't get the list displayed.
Am I using the correct elements? How can I achieve this task?
I dont think you can use the Select class as the elements you are looking for are not of type Select.
Try this approach, it worked on my end. You have to click the overlay element to expose the input element.
If you go with this approval, replace the horrible time.sleep with webdriver wait and replace the Keys with a better approach
from selenium.webdriver import Firefox
from selenium.webdriver.common.keys import Keys
import time
driver = Firefox()
driver.get('https://www.odepa.gob.cl/precios/precios-al-consumidor-en-linea')
time.sleep(5)
# Switch
frame = driver.find_element_by_css_selector(
'iframe[src="//aplicativos.odepa.gob.cl/precio-consumidor/serie-precio"]'
)
driver.switch_to.frame(frame)
time.sleep(2)
tipo_de_serie = driver.find_element_by_css_selector('span[aria-label="Seleccionar
tipo de serie activate"]'
)
driver.execute_script('arguments[0].click();', tipo_de_serie)
time.sleep(2)
seleccionar_tipo_de_serie = driver.find_element_by_css_selector('input[aria-
label="Seleccionar tipo de serie"]'
)
seleccionar_tipo_de_serie.send_keys('Anual')
seleccionar_tipo_de_serie.send_keys(Keys.ARROW_DOWN)
seleccionar_tipo_de_serie.send_keys(Keys.ENTER)

Python selenium "Not Clickable at point error."

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
import os
Game_Pin = input('Enter your PIN: ')
NickNAME = input('Enter your nickname: ')
driver = webdriver.Chrome(executable_path=r"C:\WebDriver\bin\chromedriver.exe")
def Enter_Press(driver):
driver.find_element_by_xpath("//*[contains(text(), 'Enter')]").click()
def OK_GO(driver):
driver.find_element_by_xpath("//*[#class='btn btn-greyscale join ng-binding']").click()
def Kahoot_Spammer(Game_Pin, NickNAME, driver):
driver.get('https://kahoot.it/')
driver.maximize_window() #For maximizing window
driver.implicitly_wait(2) #gives an implicit wait for 2 seconds
game_pin = driver.find_element_by_xpath("//*[#id='inputSession']")
game_pin.send_keys(Game_Pin)
Enter_Press(driver)
driver.implicitly_wait(2)
Name = driver.find_element_by_xpath("//*[#id='username']")
Name.send_keys(NickNAME)
OK_GO(driver)
Kahoot_Spammer(Game_Pin, NickNAME, driver)
The program works fine until you get to a certain point. Then selenium cannot click a button. It gives me this error.
selenium.common.exceptions.WebDriverException: Message: unknown error: Element <button type="submit" class="btn btn-greyscale join ng-binding" blocking="" data-functional-selector="join-button-username">...</button> is not clickable at point (1279, 741). Other element would receive the click: <div id="waitOverlay" class="alert-fullscreen valignwrapper" data-functional-selector="wait-overlay" style="opacity: 0.7;">...</div>
Here is the code for the button I am trying to click.
<button class="btn btn-greyscale join ng-binding" type="submit" data-functional-selector="join-button-username" blocking=""> OK, go! </button>
You can use action class to resolve this exception,
action=ActionChains(driver)
action.move_to_element("Your element").click().build().perform()
You use implicitly_wait as a wrong way. In selenium implicitly wait is for setting the find duration of all selenium find API, like find_element_by_xxx. Rather than used for waiting page refresh/load complete.
From the error message we can know, when you click on the Go button, but another element (A layer display loading which at top of the GO button ) received the click event.
So you need to wait the loading layer disappear before click anything covered by it.
Enter_Press(driver)
// driver.implicitly_wait(2)
// remove above 'implicitly_wait' line,
// you set the find duration to 2 seconds, the default value is 10 seconds.
// we don't recommend to change this default value, unless you are doing
// performance testing to expect the element comes out on page in short time.
// Selenium will return the find result once it found before 10 seconds,
// otherwise throw `NoSuchElementException` if exceed 10 seconds.
diver.wait(xxx) // or simply use sleep() to wait the `loading` disappear.
Name = driver.find_element_by_xpath("//*[#id='username']")
Name.send_keys(NickNAME)
OK_GO(driver)

Interacting with page elements that change HTML values

I'm using Selenium on http://seattle.bestparking.com/ to click through the parking garage markers (red, grey, blue), with the intention of opening the pop-up info window, so I can then scrap the info (on the "rate" page in the pop-up window). See my code at the end.
However, it appears that each of the garage marker's inner HTML changes (!!), after you click any other garage marker of the same color, then closing it (due to content reloading).
E.g.: when running my code, "Motif Seattle" garage shows up as div id = daily_colored_marker_577. But after any other marker of the same color is clicked on, "Motif Seattle" shows up as div id = daily_colored_marker_1042 ... and keeps on changing.
This makes it seemingly impossible to iterate through all the markers that I've selected (result of find_elements_by_class), since it will throw the following type or error when reaching the 2nd element in the selected list:
WebDriverException: unknown error: Element <div
class="daily_colored_marker_n_a" id="daily_colored_marker_344" onmouseover="show_hide_balloonWindowMonthly('', 'IMPARK', 'IMPARK M Street
Garage', '344', 400, 'n_a', offsetPosition('daily_colored_marker_344')...
</div> is not clickable at point (708, 543). Other element would receive the
click: <div class="daily_colored_marker_grey"...
My code:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
driver = webdriver.Chrome()
driver.get('http://seattle.bestparking.com/')
driver.maximize_window()
'''Tells driver to wait until the "I understand" button to be clickable'''
wait = WebDriverWait(driver, 30)
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//*
[#id='calendar_navigation_hint']/button")))
'''makes the disclaimer pop-up and the left menu go away, to reveal more of
the map'''
driver.find_element_by_xpath("//*#id='calendar_navigation_hint']
/button").click()
time.sleep(2)
driver.find_element_by_xpath("//*
[#id='map_left_panel_min_max_btn_div_oa']").click()
time.sleep(2)
'''zooms out one level'''
driver.find_element_by_xpath("//*
[#id='google_maps_zoom_control_minus_id']").click()
time.sleep(5)
'''find all the red markers and click on them'''
all_red = driver.find_elements_by_css_selector("div.daily_colored_marker_n_a")
time.sleep(3)
for x in range(0,len(all_red)):
all_red[x].click()
time.sleep(2)
driver.find_element_by_xpath("//*[#id='marker_window_close_text']").click()
time.sleep(3)
I don't think that error means what you think it means. If you look at it, it's stating that another element would receive the click. Selenium clicks on the center of an element. If you look at the map, there are some pins that cover each other. It's possible that it's trying to click the next element and it's partially covered by a different element so it throws the error.
Since I'm assuming you aren't trying to perform some user scenario, you can use JavascriptExecutor to execute the click and click the precise element.
You can see an example of how to do this in this question.

Categories