Unable to Locate Element for Selenium Webdriver Path in Python - python

I've been trying to reference the two inputs in the following HTML code using Selenium's get_element_by_...() with the purpose of automatically entering a username and password into the fields. I'm running into trouble with accessing the proper HTML elements.
<body>
<div id="app">
<div>
<section id="root" class="page-login">
<section class="main">
<section class="auth-page register-fill">
<div class="page-center">
<a class="switch-lang" href="/cn/login">cn</a>
<a class="btn-back" href="en/login">
<span class="isvg loaded">
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 24 24">
<path class="a" d="M0,0H24V24H0Z"></path>
<path class="b" d="M20,11H7.83l5.59-5.59L12,4,4,12l8,8,1.41-1.41L7.83,13H20Z"></path>
</svg>
</span>
</a>
<div class="auth-page-wrapper">
<h1 class="auth-page-title">Welcome Back</h1>
<h2 class="auth-page-subtitle">Login to get started</h2>
<div class="auth-page-fields">
<div class="custom-input">
<input type="text" placeholder="Email">
<label>Email</label>
</div>
<div class="custom-input custom-input-password">
<a class="forgot-link" href="en/forgot-password">Forgot Password?</a>
<input type="password" placeholder="Minimum 6 characters">
<label>Password</label>
</div>
</div>
Note: The HTML may have some mistakes, I just quickly formatted it, but the mistakes should not affect the question.
Tried:
I've tried using all the different functions of the 'form find_element_by_...' and referencing a variety of things, but all tend to return "no such element: unable to locate element ..."
I've done absolute xpaths, relative xpaths, css selectors, ids, and combinations of these.
Some examples:
username = browser.find_element_by_xpath("//input[1]")
username = browser.find_element_by_xpath("//html/body/div/div/section/.../input")
The error begins at this point in the path:
username = browser.find_element_by_xpath("//html/body/div/div/section")
I'm likely making a stupid mistake, so sorry if this question isn't great, but other StackOverflow answers all have ids and such that are referable on the input field. (eg. <input id=username ...>)

To identify the Email field you have to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following Locator Strategies:
Using CSS_SELECTOR:
username = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input[placeholder='Email']")))
Using XPATH:
username = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//input[#placeholder='Email']")))
Note : You have to add the following imports:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

There are more than one way to do this. But, this should work browser.find_element_by_css_selector('[placeholder="Email"]')

Related

Automate the login to the flipkart advertising console using selenium with python

I'm trying to login to a this website: https://advertising.flipkart.com/
I need to download and automate some reports as their console is not great but its been a while since I've used selenium and I'm drawing a blank. I'm trying to use the placeholder texts inside the form to identify the login fields but can't get it to work.
Here's the HTML of the form element where I've isolated the input fields for login, password, and submit button:
<form>
<div class="styles__Row-sc-18s7njp-1 gUWbda">
<div class="styles-erqysw-0 bIQCUh">
<div class="styles__HeadingWrapper-erqysw-3 cIXYhi">
<div class="styles__Title-etu0th-0 cOPdhu"> Email address </div>
</div>
<div class="styles__TextFieldWrapper-sc-19rh1qy-6 lkVOVS">
<div class="styles__Container-sc-19rh1qy-0 dhcObI">
<input type="text"
placeholder="Enter email"
name=""
autocomplete="off"
class="styles__StyledInput-sc-19rh1qy-4 cMJHIl"
value="USERNAME GOES HERE">
</div>
</div>
</div>
</div>
<div class="styles__Row-sc-18s7njp-1 eAthVO">
<div class="styles-erqysw-0 bIQCUh">
<div class="styles__HeadingWrapper-erqysw-3 cIXYhi">
<div class="styles__Title-etu0th-0 cOPdhu"> Password </div>
</div>
<div class="styles__TextFieldWrapper-sc-19rh1qy-6 lkVOVS">
<div class="styles__Container-sc-19rh1qy-0 dhcObI">
<input type="password"
placeholder="Enter password"
name=""
autocomplete="off"
class="styles__StyledInput-sc-19rh1qy-4 cMJHIl"
value="PASSWORD GOES HERE">
<div class="styles__EyeContainer-sc-19rh1qy-3 jyEHZI"><svg width="24px" height="24px"
viewBox="0 2 24 24" color="#363D41">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g>
<g>
<path
d="M18.1660156,11.75 C18.1660156,11.8776048 18.0748707,12.1601541 17.8925781,12.5976562 C17.6464831,13.1901071 17.3639339,13.686847 17.0449219,14.0878906 C16.5162734,14.7350293 15.8828162,15.2454408 15.1445312,15.6191406 C14.2330684,16.0748721 13.1849018,16.3027344 12,16.3027344 C10.8150982,16.3027344 9.76693164,16.0748721 8.85546875,15.6191406 C8.10806918,15.2454408 7.47461197,14.7350293 6.95507812,14.0878906 C6.64518074,13.6959616 6.36718873,13.1992218 6.12109375,12.5976562 C5.92968654,12.1601541 5.83398438,11.8776048 5.83398438,11.75 C5.83398438,11.6223952 5.92968654,11.3398459 6.12109375,10.9023438 C6.36718873,10.3007782 6.64518074,9.80403842 6.95507812,9.41210938 C7.4837266,8.76497072 8.11262656,8.25455916 8.84179688,7.88085938 C9.75325977,7.42512793 10.8059836,7.19726562 12,7.19726562 C13.1940164,7.19726562 14.242183,7.42512793 15.1445312,7.88085938 C15.8919308,8.25455916 16.525388,8.76497072 17.0449219,9.41210938 C17.3639339,9.81315305 17.6419259,10.3098929 17.8789062,10.9023438 C18.0703135,11.3398459 18.1660156,11.6223952 18.1660156,11.75 Z M17.1542969,11.75 C16.9264312,11.0481736 16.6302102,10.4739606 16.265625,10.0273438 C15.8372374,9.48046602 15.3177114,9.05664213 14.7070312,8.75585938 C13.9414024,8.36393033 13.0390677,8.16796875 12,8.16796875 C10.9609323,8.16796875 10.0540403,8.36393033 9.27929688,8.75585938 C8.66861674,9.06575676 8.14909068,9.48958064 7.72070312,10.0273438 C7.35611797,10.4739606 7.05989697,11.0481736 6.83203125,11.75 C7.05989697,12.4518264 7.35611797,13.0260394 7.72070312,13.4726562 C8.14909068,14.0104194 8.66861674,14.4342432 9.27929688,14.7441406 C10.0540403,15.126955 10.9586537,15.3183594 11.9931641,15.3183594 C13.0276744,15.3183594 13.9322878,15.126955 14.7070312,14.7441406 C15.326826,14.4342432 15.8463521,14.0104194 16.265625,13.4726562 L16.2792969,13.4726562 C16.6347674,13.0260394 16.9264312,12.4518264 17.1542969,11.75 Z M13.6679688,10.0683594 C14.1328148,10.5240908 14.3652344,11.0800748 14.3652344,11.7363281 C14.3652344,12.3925814 14.1350934,12.9508441 13.6748047,13.4111328 C13.2145159,13.8714216 12.6562533,14.1015625 12,14.1015625 C11.3437467,14.1015625 10.7854841,13.8714216 10.3251953,13.4111328 C9.86490655,12.9508441 9.63476562,12.3925814 9.63476562,11.7363281 C9.63476562,11.0800748 9.86490655,10.5218122 10.3251953,10.0615234 C10.7854841,9.60123468 11.3437467,9.37109375 12,9.37109375 C12.6562533,9.37109375 13.2122373,9.6035133 13.6679688,10.0683594 Z M12.984375,10.7519531 C12.7109361,10.4785143 12.3828144,10.3417969 12,10.3417969 C11.6171856,10.3417969 11.2890639,10.4785143 11.015625,10.7519531 C10.7421861,11.025392 10.6054688,11.3535137 10.6054688,11.7363281 C10.6054688,12.1191425 10.7421861,12.4472643 11.015625,12.7207031 C11.2890639,12.994142 11.6171856,13.1308594 12,13.1308594 C12.3828144,13.1308594 12.7109361,12.994142 12.984375,12.7207031 C13.2578139,12.4472643 13.3945312,12.1191425 13.3945312,11.7363281 C13.3945312,11.3535137 13.2578139,11.025392 12.984375,10.7519531 Z"
fill="#363D41" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg></div>
</div>
</div>
</div>
<div class="styles__ForgetPassWrapper-fg24yr-1 eqzIXq"><a href="/forgot-password"
class="styles__Anchor-sc-5941cr-2 kYQwHs">
<div class="styles__TextContainer-sc-5941cr-0 cCBwan">Forgot Password?</div>
</a></div>
</div>
<div class="styles__Row-sc-18s7njp-1 gUWbda">
<div class="styles__Row-sc-18s7njp-1 gUWbda">
<button type="submit"
class="styles__DefaultButton-sc-5941cr-1 hHuXf">
<div class="styles__TextContainer-sc-5941cr-0 cCBwan">Login</div>
</button>
</div>
<div class="styles__BoxContentCenter-sc-18s7njp-3 kqkvB">
<div class="styles__Label-sc-18s7njp-9 jIhEMq"> Do not have an account? </div>
<div class="styles__Row-sc-18s7njp-1 ncyfJ"><a href="/register" class="styles__Anchor-sc-5941cr-2 kYQwHs">
<div class="styles__TextContainer-sc-5941cr-0 cCBwan">Register now</div>
</a></div>
</div>
</div>
</form>
This is the current code I have.
# import selenium module
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
# Login Information
url = "https://advertising.flipkart.com/"
login_id = "USERNAME"
login_password = "PASSWORD"
# Start the session
wd = webdriver.Chrome('./chromedriver')
def login(url, login_id, login_password):
""" This function will login to the website
parameters:
url: url of the website
login_id: login id of the user
login_password: password of the user
returns:
None
"""
# Open the website
wd.get(url)
# Find the input box with placeholder text "Enter email" and enter the login id
wd.find_element_by_xpath("//input[#placeholder='Enter email']").send_keys(login_id)
# Find the input box with placeholder text "Enter password" and enter the password
wd.find_element_by_xpath("//input[#placeholder='Enter password']").send_keys(login_password)
login(url, login_id, login_password)
First, you are using deprecated syntax: find_element_by_* was replaced with find_element(By.*...). The following is a correct way of logging into that website (you can adapt it to your own setup, just make sure you observe the imports, and the part after defining the browser/driver):
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
chrome_options = Options()
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument('disable-notifications')
chrome_options.add_argument("window-size=1280,720")
webdriver_service = Service("chromedriver/chromedriver") ## path to where you saved chromedriver binary
browser = webdriver.Chrome(service=webdriver_service, options=chrome_options)
url = 'https://advertising.flipkart.com/'
browser.get(url)
WebDriverWait(browser, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'input[placeholder="Enter email"]'))).send_keys('username#email.com')
WebDriverWait(browser, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'input[placeholder="Enter password"]'))).send_keys('password')
print('wrote credentials')
WebDriverWait(browser, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'button[type="submit"]'))).click()
print('clicked submit button')
Selenium documentation can be found at https://www.selenium.dev/documentation/
If you were to use inspect element, most browsers allow you to right click on the HTML element (the input fields in this case), and copy the XPath. So for example, for email it would be:
wd.find_element_by_xpath('//*[#id="app"]/div[1]/div[1]/div/div[2]/div/div[2]/form/div[1]/div/div[2]/div/input').send_keys(login_id)
This is not a perfect solution, but since you don't have an id or class, its one of the few solutions available. The biggest downside of this would be if the layout of the page was changed, at which point the XPath would change as well, meaning that you would have to update your script with the new XPath. However, if you only plan to use the script for yourself, this shouldn't be much of a problem.

Upload file with Selenium when more than 1 Input with same class detected

So I have page structured like this :
<ul>
<li>
<div>
<div>
<span class="buttonUpload" custom="call.js">Button Upload</span>
<input class="fa-cloud-upload" name="file" type="file" accept="">
</div>
</div>
</li>
<li>
<div>
<div>
<span class="buttonUpload" custom="call.js">Button Upload</span>
<input class="fa-cloud-upload" name="file" type="file" accept="">
</div>
</div>
</li>
<li>
<div>
<div>
<span class="buttonUpload" custom="call.js">Button Upload</span>
<input class="fa-cloud-upload" name="file" type="file" accept="">
</div>
</div>
</li>
</ul>
I want to upload file only to the first input tag using send_keys() from selenium but for some reason it's not working, if there's only single li tags shown after refresh then the codes work, my code as bellow :
view_videos = WebDriverWait(driver, self.latency).until(EC.visibility_of_all_elements_located(
(By.CSS_SELECTOR, "a[i18n='i18n.openLink']")))
for i in range(len(view_videos)):
# some other script here
uploadme = "/home/user/myfile.png"
#script to upload
driver.find_element_by_css_selector("input[class='fa-cloud-upload']").send_keys(str(uploadme))
I even try with driver wait and select all element with selected array:
#script to upload
upload_first = WebDriverWait(driver, self.latency).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "input[class='fa-cloud-upload']")))
upload_first[0].send_keys(str(uploadme))
But nothing happened, I execute also javascript to erase 2 <li> tags after the first one before upload but still upload not working, nothing happens, I just want to upload to first input only using selenium, any clue?
Try using By.CLASS_NAME, 'fa-cloud-upload' or EC.presense_of_element_located((By.XPATH, 'its xpath'))
You can find Xpath of the element in F12 panel. Inspect the element, then right click on it in F12 panel and copy -> Xpath.
To send a character sequence to the first <input> tag instead of presence_of_all_elements_located() you have to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following Locator Strategies:
Using CSS_SELECTOR:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "ul > li input.fa-cloud-upload"))).send_keys(str(uploadme))
Using XPATH:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//ul/li//input[#class='fa-cloud-upload']"))).send_keys(str(uploadme))
Note: You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

How to click an input role as radio located under in selenium using python

I am doing some automation script for the website https://silkdb.bioinfotoolkits.net in selenium using python language where I have you can see a button named "Blast" that pops up a window with some default values on click.
<div data-meta="Field" id="program" role="radiogroup" class="next-radio-group next-radio-button next-radio-button-medium">
<label dir="ltr" aria-checked="true" class="next-radio-wrapper checked "><span class="next-radio-single-input">
<input role="radio" tabindex="0" type="radio" aria-checked="true" class="next-radio-input"></span><span class="next-radio-label">blastn</span>
</label>
<label dir="ltr" aria-checked="false" class="next-radio-wrapper "><span class="next-radio-single-input">
<input role="radio" tabindex="-1" type="radio" aria-checked="false" class="next-radio-input"></span><span class="next-radio-label">blastx</span></label>
<label dir="ltr" aria-checked="false" class="next-radio-wrapper "><span class="next-radio-single-input"><input role="radio" tabindex="-1" type="radio" aria-checked="false" class="next-radio-input"></span><span class="next-radio-label">tblastn</span></label>
<label dir="ltr" aria-checked="false" class="next-radio-wrapper "><span class="next-radio-single-input"><input role="radio" tabindex="-1" type="radio" aria-checked="false" class="next-radio-input"></span><span class="next-radio-label">blastp</span></label></div>
The default label named "blastn" is selected on defualt. But I want to select lable named "blastp".
I have tried many things using xpath but I am unable to get it selected.
Please help me in this.
It is important to send the click events to the appropriate receiving elements which can actually react to the click event. In this case, i think the click must be send to the input element, not the span.
Please try with the following xpath.
//span[#class="next-radio-label" and contains(text(),'blastp')]/preceding-sibling::span/input
Please use following xpath to click your option //span[contains(text(),'blastp')]
To click on the element you have to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following Locator Strategies:
Using CSS_SELECTOR:
driver.get("https://silkdb.bioinfotoolkits.net/")
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button#btn-blast"))).click()
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div#program label:last-child span:nth-child(2)"))).click()
Using XPATH:
driver.get("https://silkdb.bioinfotoolkits.net/")
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//button[#id='btn-blast']"))).click()
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//span[#class='next-radio-label' and text()='blastp']"))).click()
Note : You have to add the following imports:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
Browser Snapshot:

Can find element within an iframe using Firefox but not Chrome

So I'm trying to write a test for a webpage which has some elements within an iframe. I've been able to successfully run the test using webdriver.Firefox() without any problems but if I switch it over to webdriver.Chrome() I get a timeout exception on the following lines of code:
self.driver.switch_to.frame(0)
self.activity_status = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, '#overview > div.details.w-66 > div > div.duration-and-status > span.status.stat_incomplete#')))
It'd be great to get a solution to this as I'm all out of ideas.
Thanks for your help.
edit, partial html for the page:
<iframe id="iframe_course_details" allowfullscreen="" src="../Course/Details.aspx?HidePageNav=true&IsInIframe=true"></iframe>
Close
Edit (Inactive)
Edit
<span id="ctl00_cph_main_content_area_ucCourseDetails_spn_favourite" class="favourite button tooltipstered" style="display: none;">Favourite</span>
<span id="ctl00_cph_main_content_area_ucCourseDetails_spn_basket_dull" class="add-to-basket button delete tooltipstered" style="display: none;">Enrolled (Remove From Enrolments)</span>
<span id="ctl00_cph_main_content_area_ucCourseDetails_spn_basket" class="add-to-basket button tooltipstered">Add to Enrolments</span>
<span id="ctl00_cph_main_content_area_ucCourseDetails_spn_print" class="print button tooltipstered">Print</span>
</div>
<section id="overview" style="opacity: 1;">
<div id="fullname" class="fullname w-100" style="display: none;">
</div>
<div class="image w-33" style="cursor: pointer;">
<div style="background-image:url(/App_Themes/MainTheme-responsive/Images/Course/webcast.jpg);"></div></div>
<div class="details w-66">
<div class="inner">
<h2>testing activity</h2>
<div class="star-rating-num-ratings">
<div class="star-rating">
<span></span><span></span><span></span><span></span><span></span>
</div>
<span class="num-of-ratings">0 Ratings</span>
</div>
<div class="duration-and-status">
<span class="duration">
<label>
Duration:
</label>
<span>0</span>
</span>
<span class="status stat_incomplete">Started</span>
</div>
Edit 2:
So we've managed to find a solution to this and its even more confusing than the original problem
WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.ID, 'iframe_course_details')))
time.sleep(0)
self.activity_status = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[#id="overview"]/div[3]/div/div[2]/span[2]')))
I'd be really curious to hear some theories on why this works, it times out without the 'time.sleep(0).
If you reference the iframe directly rather then an integer that will work between Firefox/Chrome.
self.driver.switch_to.frame(driver.find_element_by_name("iframe"))
You can find the iframe element any way you wish e.g by css/xpath etc
As the the desired element is within an <iframe> so to invoke click() on the element you have to:
Induce WebDriverWait for the desired frame to be available and switch to it.
Code Block:
# as per your comment assuming -> there is only one frame on the page
WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.TAG_NAME,"iframe")))
self.element = self.activity_status = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, '#overview > div.details.w-66 > div > div.duration-and-status > span.status.stat_incomplete#')))
Note : You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
Reference
You can find a relevant detailed discussion in:
Ways to deal with #document under iframe

How to jump between two different elements located same level?

The problem is the following, in the same html I have a footer and a div located on the same level first I want to click on the img of the following code:
<footer>
<div layout="row" layout-align="center center" flex="" class="layout-align-center-center layout-row flex">
<img src="assets/images/vtr-icon-tecnico.png" class="icon" ng-click="vm.openModal($event, 'appt')" role="button" tabindex="0">
</div>
</footer>
and then click on an input of a input of the following code:
<div class="md-dialog-container ng-scope" tabindex="-1" style="top: 0px; height: 937px;">
<form name="appsForm" class="ng-pristine ng-invalid ng-invalid-required" style="">
<!-- ngIf: !links -->
<md-content class="md-padding ng-scope layout-column" layout="column" ng-if="!links" style="">
<md-input-container class="md-input-invalid"><label for="input_10557">Clave</label>
<input required="" type="password" name="password" md-maxlength="30" ng-model="key.password" class="ng-pristine md-input ng-empty ng-invalid ng-invalid-required ng-touched" id="input_10557" aria-invalid="true" ng-trim="false" style="">
</md-input-container>
</md-content>
</form>
</div>
Using xpath I tried with:
pass = wait.until(EC.element_to_be_clickable((By.XPATH, "//div[contains(#class,'ng-scope layout-column flex')]//div[#id='input_10555']")))
But the answer is the following in the console:
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//div[contains(#class,'ng-scope layout-column flex')]//div[#id='input_10555']"}
I use Python, Selenium Web Driver and Chrome Browser
thank you who has better understanding on how to do it
The desired element is an Angular element so to locate and click() on the element you have to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following Locator Strategies:
Using CSS_SELECTOR:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input.ng-pristine.md-input.ng-empty.ng-invalid.ng-invalid-required.ng-touched[id^='input_'][name='password']"))).click()
Using XPATH:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//input[#class='ng-pristine md-input ng-empty ng-invalid ng-invalid-required ng-touched' and starts-with(#id,'input_')][#name='password']"))).click()
Note : You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

Categories

Resources