The following script follows a page in Instagram:
browser = webdriver.Chrome('./chromedriver')
# GO INSTAGRAM PAGE FOR LOGIN
browser.get('https://www.instagram.com/accounts/login/?hl=it')
sleep(2)
# ID AND PASSWORD
elem = browser.find_element_by_name("username").send_keys('test')
elem = browser.find_element_by_name("password").send_keys('passw')
# CLICK BUTTON AND OPEN INSTAGRAM
sleep(5)
good_elem = browser.find_element_by_xpath('//*[#id="react-root"]/section/main/div/article/div/div[1]/div/form/span/button').click()
sleep(5)
browser.get("https://www.instagram.com")
# GO TO PAGE FOR FOLLOW
browser.get("https://www.instagram.com/iam.ai4/")
sleep(28)
segui = browser.find_element_by_class_name('BY3EC').click()
If an element with class BY3EC isn't found I want the script to keep working.
When an element is not found it throws NoSuchElementException, so you can use try/except to avoid that, for example:
from selenium.common.exceptions import NoSuchElementException
try:
segui = browser.find_element_by_class_name('BY3EC').click()
except NoSuchElementException:
print('Element BY3EC not found') # or do something else here
You can take a look at selenium exceptions to get an idea of what each one of them is for.
surround it with try catches, than you can build a happy path and handle failures as well, so your test case will always work
Best practice is to not use Exceptions to control flow. Exceptions should be exceptional... rare and unexpected. The simple way to do this is to get a collection using the locator and then see if the collection is empty. If it is, you know the element doesn't exist.
In the example below we search the page for the element you wanted and check to see that the collection contains an element, if it does... click it.
segui = browser.find_elements_by_class_name('BY3EC')
if segui:
segui[0].click()
Related
I am trying to open a page and click on download button. It works fine for the pages that have download element but for the pages which doesn't have that element it raises error
Code:
for i in data["allurl"]:
driver.get('{0}'.format(i))
if(driver.find_element_by_id('ContentPlaceHolder1_grdFileUpload_lnkDownload_0')):
button_element = driver.find_element_by_id('ContentPlaceHolder1_grdFileUpload_lnkDownload_0')
button_element.click()
else:
pass
It should pass instead of raising the error but when I run this it says:
NoSuchElementException: Message: no such element: Unable to locate
element:
{"method":"id","selector":"ContentPlaceHolder1_grdFileUpload_lnkDownload_0"}
How do I solve this?
driver.find_element_by_id() doesn't return True or False as your if-statement expects. Either change your if-statement, or use a try/except statement.
from selenium.common.exceptions import NoSuchElementException
for i in data["allurl"]:
driver.get('{0}'.format(i))
try:
button_element = driver.find_element_by_id('ContentPlaceHolder1_grdFileUpload_lnkDownload_0')
button_element.click()
except NoSuchElementException:
pass
Check the length count of the web element.If it is more than 0 then element available and click otherwise it will go to else condition.
for i in data["allurl"]:
driver.get('{0}'.format(i))
if len(driver.find_elements_by_id('ContentPlaceHolder1_grdFileUpload_lnkDownload_0'))>0:
button_element = driver.find_element_by_id('ContentPlaceHolder1_grdFileUpload_lnkDownload_0')
button_element.click()
else:
pass
from selenium.common.exceptions import NoSuchElementException
try:
button_element = driver.find_element_by_id('ContentPlaceHolder1_grdFileUpload_lnkDownload_0')
except NoSuchElementException:
pass
else:
button_element.click()
Note that even if it worked as you expected, it's inefficient because you perform search for the element twice.
EDIT: included the import statement for the exception
UPDATE: as a side note, assuming elements in data["allurl"] are url (i.e. strings) there is no need for string formatting. driver.get(i) would do. And i is poor choice for variable name - better use something more meaningful....
I want to check
def first_page_error:
-if the element is not existing "break" and move on with the script
-if element exists then refresh the page
-if element stops existing after the refresh run firstPage()
I will appreciate any help I'm learning python :)
My code right now
def first_page_error():
cap_error = driver.find_element_by_xpath('//*[#id="js-register-with-email"]/div[2]')
while True:
try:
cap_error
except NoSuchElementException:
break
else:
time.sleep(5)
driver.refresh()
def firstPage():
element = driver.find_element_by_xpath('//*[#id="sign-up-link"]')
driver.execute_script("arguments[0].click();", element)
emailInput = driver.find_element_by_xpath('//*[#id="register-email"]')
emailInput.send_keys(emails[0])
condInput = driver.find_element_by_xpath('//*[#id="register-terms"]')
condInput.click()
firstPage()
first_page_error()
A few points of feedback...
If you are automating user scenarios, avoid using JS clicks. A user can't click an element that isn't visible or is covered or is off the screen, etc. Selenium was designed to act like a user and throw errors when a user can't click an element. That's a good thing. It helps you find the problems and solve them as a user would.
Prefer IDs and CSS selectors. They are faster, better supported, and so on. There's a lot of info on the web if you want more explanation. I converted your XPaths that were just locating by ID to *_by_id().
Check for existence by using driver.find_elements_* (Notice the plural, elementS) and check the len of the collection
I would write something like the below.
def first_page_error():
return len(driver.find_elements_by_xpath('//*[#id="js-register-with-email"]/div[2]')) > 0
def firstPage():
driver.find_element_by_id('sign-up-link').click()
driver.find_element_by_id('register-email').send_keys(emails[0])
driver.find_element_by_id('register-terms').click()
firstPage()
if first_page_error():
driver.refresh()
if first_page_error():
firstPage()
# move on with the script
Thank you all for input
I've resolved it with this
def err_redeem_func():
err_redeem = driver.find_element_by_class_name('error')
try:
if err_redeem.is_displayed() and err_redeem.is_enabled():
driver.refresh()
redeem_func()
except NoSuchElementException:
pass
err_redeem_func()
I'm writing a script in to do some webscraping on my Firebase for a few select users. After accessing the events page for a user, I want to check for the condition that no events have been logged by that user first.
For this, I am using Selenium and Python. Using XPath seems to work fine for locating links and navigation in all other parts of the script, except for accessing elements in a table. At first, I thought I might have been using the wrong XPath expression, so I copied the path directly from Chrome's inspection window, but still no luck.
As an alternative, I have tried to copy the page source and pass it into Beautiful Soup, and then parse it there to check for the element. No luck there either.
Here's some of the code, and some of the HTML I'm trying to parse. Where am I going wrong?
# Using WebDriver - always triggers an exception
def check_if_user_has_any_data():
try:
time.sleep(10)
element = WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.XPATH, '//*[#id="event-table"]/div/div/div[2]/mobile-table/md-whiteframe/div[1]/ga-no-data-table/div')))
print(type(element))
if element == True:
print("Found empty state by copying XPath expression directly. It is a bit risky, but it seems to have worked")
else:
print("didn’t find empty state")
except:
print("could not find the empty state element", EC)
# Using Beautiful Soup
def check_if_user_has_any_data#2():
time.sleep(10)
html = driver.execute_script("return document.documentElement.outerHTML")
soup = BeautifulSoup(html, 'html.parser')
print(soup.text[:500])
print(len(soup.findAll('div', {"class": "table-row-no-data ng-scope"})))
HTML
<div class="table-row-no-data ng-scope" ng-if="::config" ng-class="{overlay: config.isBuilderOpen()}">
<div class="no-data-content layout-align-center-center layout-row" layout="row" layout-align="center center">
<!-- ... -->
</div>
The first version triggers the exception and is expected to evaluate 'element' as True. Actual, the element is not found.
The second version prints the first 500 characters (correctly, as far as I can tell), but it returns '0'. It is expected to return '1' after inspecting the page source.
Use the following code:
elements = driver.find_elements_by_xpath("//*[#id='event-table']/div/div/div[2]/mobile-table/md-whiteframe/div[1]/ga-no-data-table/div")
size = len(elements)
if len(elements) > 0:
# Element is present. Do your action
else:
# Element is not present. Do alternative action
Note: find_elements will not generate or throw any exception
Here is the method that generally I use.
Imports
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
Method
def is_element_present(self, how, what):
try:
self.driver.find_element(by=how, value=what)
except NoSuchElementException as e:
return False
return True
Some things load dynamically. It is better to just set a timeout on a wait exception.
If you're using Python and Selenium, you can use this:
try:
driver.find_element_by_xpath("<Full XPath expression>") # Test the element if exist
# <Other code>
except:
# <Run these if element doesn't exist>
I've solved it. The page had a bunch of different iframe elements, and I didn't know that one had to switch between frames in Selenium to access those elements.
There was nothing wrong with the initial code, or the suggested solutions which also worked fine when I tested them.
Here's the code I used to test it:
# Time for the page to load
time.sleep(20)
# Find all iframes
iframes = driver.find_elements_by_tag_name("iframe")
# From inspecting page source, it looks like the index for the relevant iframe is [0]
x = len(iframes)
print("Found ", x, " iFrames") # Should return 5
driver.switch_to.frame(iframes[0])
print("switched to frame [0]")
if WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.XPATH, '//*[#class="no-data-title ng-binding"]'))):
print("Found it in this frame!")
Check the length of the element you are retrieving with an if statement,
Example:
element = ('https://www.example.com').
if len(element) > 1:
# Do something.
I am attempting to validate that text is present on a page. Validating an element by ID is simple enough, buy trying to do it with text isn't working right. And, I can not locate the correct attribute for By to validate text on a webpage.
Example that works for ID using By attribute
self.assertTrue(self.is_element_present(By.ID, "FOO"))
Example I am trying to use (doesn't work) for text using By attribute
self.assertTrue(self.is_element_present(By.TEXT, "BAR"))
I've tried these as well, with *error (below)
self.assertTrue(self.is_text_present("FOO"))
and
self.assertTrue(self.driver.is_text_present("FOO"))
*error: AttributeError: 'WebDriver' object has no attribute 'is_element_present'
I have the same issue when trying to validate By.Image as well.
From what I have seen, is_element_present is generated by a Firefox extension (Selenium IDE) and looks like:
def is_element_present(self, how, what):
try: self.driver.find_element(by=how, value=what)
except NoSuchElementException: return False
return True
"By" is imported from selenium.webdriver.common:
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
There are several "By" constants to address each API find_element_by_* so, for example:
self.assertTrue(self.is_element_present(By.LINK_TEXT, "My link"))
verifies that a link exists and, if it doesn't, avoids an exception raised by selenium, thus allowing a proper unittest behaviour.
First of all, it's discouraged to do so, it's better to change your testing logic than finding text in page.
Here's how you create you own is_text_present method though, if you really want to use it:
def is_text_present(self, text):
try:
body = self.driver.find_element_by_tag_name("body") # find body tag element
except NoSuchElementException, e:
return False
return text in body.text # check if the text is in body's text
For images, the logic is you pass the locator into it. (I don't think is_element_present exists in WebDriver API though, not sure how you got By.ID working, let's assume it's working.)
self.assertTrue(self.is_element_present(By.ID, "the id of your image"))
# alternatively, there are others like CSS_SELECTOR, XPATH, etc.
# self.assertTrue(self.is_element_present(By.CSS_SELECTOR, "the css selector of your image"))
I like wrapping the whole thing into a custom assertion
from selenium.common.exceptions import NoSuchElementException
def assertElementIsPresentByXPath(self, xpath, msg=None):
try:
self.browser.find_element_by_xpath(xpath)
self.assertTrue(True, msg)
except NoSuchElementException:
self.assertTrue(False, msg)
def test_element_10_should_exists(self):
self.browser.get('url/to/test')
self.assertElementIsPresentByXPath('//a[#id=el_10]')
I have a HTML code like this:
<div class="links nopreview"><span><a class="csiAction"
href="/WebAccess/home.html#URL=centric://REFLECTION/INSTANCE/_CS_Data/null">Home</a></span> • <span><span><a class="csiAction"
href="/WebAccess/home.html#URL=centric://SITEADMIN/_CS_Site">Setup</a></span> • </span><span><a
title="Sign Out" class="csiAction csiActionLink">Sign Out</a></span></div>
I would like to click on the link that has the text Home. As this Home link appears after login, I have a code like this:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import re
browser = webdriver.Firefox() # Get local session of firefox
browser.get("http://myServer/WebAccess/login.html") # Load App page
elem = browser.find_element_by_name("LoginID") # Find the Login box
elem.send_keys("Administrator")
elem = browser.find_element_by_name("Password") # Find the Password box
elem.send_keys("Administrator" + Keys.RETURN)
#try:
elem = browser.find_element_by_link_text("Home")
elem.click()
The part till login works great. However the last but one line is problematic
elem = browser.find_element_by_link_text("Home")
It raises this NoSuchElementException where the Home link is there as you can see from the HTML code.
raise exception_class(message, screen, stacktrace)
NoSuchElementException: Message: u'Unable to locate element: {"method":"link text","selector":"Home"}'
Any guidance as to what I am doing wrong, please?
Have you tried adding an implicit wait to this so that it waits instead of running to quickly.
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import re
browser = webdriver.Firefox() # Get local session of firefox
browser.implicitly_wait(10) #wait 10 seconds when doing a find_element before carrying on
browser.get("http://myServer/WebAccess/login.html") # Load App page
elem = browser.find_element_by_name("LoginID") # Find the Login box
elem.send_keys("Administrator")
elem = browser.find_element_by_name("Password") # Find the Password box
elem.send_keys("Administrator" + Keys.RETURN)
#try:
elem = browser.find_element_by_link_text("Home")
elem.click()
The implicitly_wait call makes the browser poll until the item is on the page and visible to be interacted with.
The most common issues with NoSuchElementException while the element is there are:
the element is in different window/frame, so you've to switch to it first,
your page is not loaded or your method of page load is not reliable.
Solution could include:
check if you're using the right frame/window by: driver.window_handles,
write a wait wrapper to wait for an element to appear,
try XPath instead, like: driver.find_element_by_xpath(u'//a[text()="Foo"]').click(),
use pdb to diagnose your problem more efficiently.
See also: How to find_element_by_link_text while having: NoSuchElement Exception?
Maybe the element you are looking for doesn't exactly match that text string?
I know it can be tricky if it looks like it does on-screen, but sometimes there are oddities embedded like this simple markup "Home" or "Home" which makes the first char italic:
"<i>H</i>ome" is visually identical to "<em>H</em>ome" but does not match text.
Edit: after writing the above answer, I studied the question closer and discovered the HTML sample does show "Home" in plain text, but was not visible due to long lines not wrapping. So I edited the OP to wrap the line for readability.
New observation: I noticed that the Logout element has a "title" attribute, but the Home link element lacks such--try giving it one and using that.
Try adding an implicit wait to this in order to wait, instead of running too quickly.
Or
else you can import time and use time.sleep(25)