Selenium in Python expected conditions works with Firefox but not Chrome - python

I log into a private site and then wait for a link text to appear and then open the link with browser.get(). The code below throws a timeout exception almost every time while waiting for the link text "OTD" to appear. It is strange that it works once out of many tries. Even stranger is that if I use a sleep timer instead of the wait for expected condition, it works, but that is not what I want to use.
Here is the html:
<a target="_blank" href="/privateurl">OTD</a>
And here is the code that uses chromedriver:
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 webbrowser
chrome_options = webdriver.ChromeOptions()
prefs = {'download.default_directory' : 'E:\\folder'}
chrome_options.add_experimental_option('prefs', prefs)
browser = webdriver.Chrome(chrome_options=chrome_options)
#browser.get('private url login page')
#enter login information and submit
wait = WebDriverWait(browser, 10)
wait.until(
EC.presence_of_element_located((By.LINK_TEXT, "OTD"))
)
browser.get('private url')
I have the same code, but for the Firefox driver and it works perfectly every time. I just need to use Chrome browser instead.
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 webbrowser
profile = webdriver.FirefoxProfile()
profile.set_preference("browser.download.folderList", 2)
profile.set_preference("browser.download.dir", 'E:\\folder')
profile.set_preference("browser.download.useDownloadDir", True)
browser=webdriver.Firefox(profile)
#browser.get('private url login page')
#enter login information and submit
wait = WebDriverWait(browser, 10)
wait.until(
EC.presence_of_element_located((By.LINK_TEXT, "OTD"))
)
browser.get('private url')
What am I doing wrong in the code using the chromedriver that is making it throw a timeout exception?

There are a couple of things to address :
Finally as you are invoking click() on the WebElement so instead of the expected_conditions clause as presence_of_element_located() you should use the clause element_to_be_clickable(locator).
When you use expected_conditions clause as element_to_be_clickable(locator) the WebElement is returned back and you can directly invoke click() method on it.
Your optimized line of code will be :
WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.LINK_TEXT, "OTD"))).click()
Update A
As per your comment update you are seeing the error :
'element_to_be_clickable' object has no attribute 'click'
An alternative would be to extract the href attribute and invoke get() as follows :
element = WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.LINK_TEXT, "OTD")))
attr_href = element.get_attribute("href")
driver.get(attr_href)
Update B
As per your comment update the idea of Selenium does interact with the element ... even though it is unable to attain visibility_of_element is not a full proof solution as :
The expected_conditions clause presence_of_element_located(locator) mentions :
class selenium.webdriver.support.expected_conditions.presence_of_element_located(locator)
An expectation for checking that an element is present on the DOM of a page. This does not necessarily mean that the element is visible.
locator - used to find the element returns the WebElement once it is located.
Where as the expected_conditions clause visibility_of_element_located(locator) mentions :
class selenium.webdriver.support.expected_conditions.visibility_of_element_located(locator)
An expectation for checking that an element is present on the DOM of a page and visible. Visibility means that the element is not only displayed but also has a height and width that is greater than 0.
locator - used to find the element returns the WebElement once it is located and visible.
From Selenium perspective if an element is not displayed Selenium won't be interact with the element e.g. invoking click()

Related

Possible security feature that disables automation?

I am making a Python script with the Selenium Chrome Webdriver to automate this website: https://nordicwellness.se/logga-in/?redirecturl=/mina-sidor/ (it's a Swedish website).
I am trying to automate the login process but I keep getting errors such as:
selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable
and
Keyboard not reachable
I am using the following code to locate the email input field and password input field:
emailInp = driver.find_element(By.XPATH, '//*[#id="UserName"]').send_keys('test')
passwordInp = driver.find_element(By.XPATH, '//*[#id="Password"]').send_keys('123')
I've tried passing multiple options and even used the WebDriverWait function, even tried Firefox and Safari, but nothing seems to work.
Is it possible that the website has some kind of security feature that doesn't allow automated scripts? If so, is there a way to bypass it?
You are using not unique locators. There are 3 elements matching //*[#id="UserName"] and //*[#id="Password"] locators.
Locators should be always unique. In this case you need to take in account the unique parent element to make locators unique.
The following code works:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
options = Options()
options.add_argument("start-maximized")
webdriver_service = Service('C:\webdrivers\chromedriver.exe')
driver = webdriver.Chrome(options=options, service=webdriver_service)
wait = WebDriverWait(driver, 10)
url = "https://nordicwellness.se/logga-in/?redirecturl=/mina-sidor/"
driver.get(url)
wait.until(EC.element_to_be_clickable((By.XPATH, "//main//input[#id='UserName']"))).send_keys('test')
wait.until(EC.element_to_be_clickable((By.XPATH, "//main//input[#id='Password']"))).send_keys('123')
The result screenshot is:
I believe it is due to your XPath. If you inspect the web elements in the page itself, there are actually 3 elements matched the XPath you entered //*[#id="UserName"]. Without specifying, Selenium will always match the first return element. Which is hidden in the top menu. This is why you get the error of element not interactable.
A quick solution is to use the direct full XPath as per below :
emailInp = driver.find_element(By.XPATH, '/html/body/div[1]/main/div/div/div/form/input[1]').send_keys('test')
passwordInp = driver.find_element(By.XPATH, '/html/body/div[1]/main/div/div/div/form/input[2]').send_keys('123')
Or you need to specifically filter out which //*[#id="UserName"] you trying to access, for an example :
emailInp = driver.find_element(By.XPATH, '//main//*[#id="UserName"]').send_keys('test')
passwordInp = driver.find_element(By.XPATH, '//main//*[#id="Password"]').send_keys('123')

Find xpath or something similar (=identifier) on web page

I am trying to click on a place on a video. I tried it with xpath already, but without success.
For example on this tiktok video: https://www.tiktok.com/#willsmith/video/7125844820328926510?is_from_webapp=v1&item_id=7125844820328926510&web_id=7139992072584676869
I'm trying to click on the heart with selenium (python).
That's my code:
if driver.find_element_by_xpath("/html/body/div[2]/div[2]/div[2]/div[1]/div[3]/div[1]/div[1]/div[3]/button[1]/span/div/svg/g/path") :
driver.find_element_by_xpath("/html/body/div[2]/div[2]/div[2]/div[1]/div[3]/div[1]/div[1]/div[3]/button[1]/span/div/svg/g/path").click()
It says that it's "Unable to locate element". I don't know why. I even added some sleep to the code because I thought that the website didn't load up fully or even tried with a different xpath.
I also tried to do it with the ID of the "heart-location" but the ID is very hard to understand if I inspect element.
Could someone please help me out? Thanks in advance!
You need to use the correct locator
And to wait for the element to be clickable.
For the former WebDriverWait Expected Conditions explicit wait should be used.
The below code works:
(In case you are already logged in)
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
options = Options()
options.add_argument("start-maximized")
webdriver_service = Service('C:\webdrivers\chromedriver.exe')
driver = webdriver.Chrome(service=webdriver_service, options=options)
url = "https://www.tiktok.com/#willsmith/video/7125844820328926510?is_from_webapp=v1&item_id=7125844820328926510&web_id=7139992072584676869"
driver.get(url)
wait = WebDriverWait(driver, 10)
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "span[data-e2e='like-icon']"))).click()
In case you want to use XPath instead of CSS Selector just change the line above with
wait.until(EC.element_to_be_clickable((By.XPATH, "//span[#data-e2e='like-icon']"))).click()

Issue with selenium.webdriver click function using find_elements(By.XPATH, [duplicate]

I'd like to click the button 'Annual' at a page that is by default set on 'Quarterly'. There are two links that are basically called the same, except that one has data-ptype="Annual" so I tryed to copy the xpath to click the button (also tried other options but none did work).
However, I get the AttributeError: 'list' object has no attribute 'click'. I read a lot of similar posts, but wasn't able to fix my problem.. so I assume that javascript event must be called/clicked/performed somehow differnt.. idk Im stuck
from selenium import webdriver
link = 'https://www.investing.com/equities/apple-computer-inc-balance-sheet'
driver = webdriver.Firefox()
driver.get(link)
elm = driver.find_elements_by_xpath("/html/body/div[5]/section/div[8]/div[1]/a[1]").click()
The html is the following:
<a class="newBtn toggleButton LightGray" href="javascript:void(0);" data-type="rf-type-button" data-ptype="Annual" data-pid="6408" data-rtype="BAL">..</a>
you need to use find_element_by_xpath not find_elements_by_xpath that return a list
driver.find_element_by_xpath("/html/body/div[5]/section/div[8]/div[1]/a[1]").click()
Also i think is better to use Waits for example.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.firefox.options import Options
options = Options()
options.add_argument("--window-size=1920,1080")
driver = webdriver.Firefox(firefox_options=options)
path = "/html/body/div[5]/section/div[8]/div[1]/a[1]"
try:
element = WebDriverWait(driver, 5).until(
EC.element_to_be_clickable((By.XPATH, path)))
element.click()
finally:
driver.quit()
I would still suggest you to go with linkText over XPATH. Reason this xpath : /html/body/div[5]/section/div[8]/div[1]/a[1] is quite absolute and can be failed if there is one more div added or removed from HTML. Whereas chances of changing the link Text is very minimal.
So, Instead of this code :
elm = driver.find_elements_by_xpath("/html/body/div[5]/section/div[8]/div[1]/a[1]").click()
try this code :
annual_link = driver.find_element_by_link_text('Annual')
annual_link.click()
and yes #Druta is right, use find_element for one web element and find_elements for list of web element. and it is always good to have explicit wait.
Create instance of explicit wait like this :
wait = WebDriverWait(driver,20)
and use the wait reference like this :
wait.until(EC.elementToBeClickable(By.LINK_TEXT, 'Annual'))
UPDATE:
from selenium import webdriver
link = 'https://www.investing.com/equities/apple-computer-inc-balance-sheet'
driver = webdriver.Firefox()
driver.maximize_window()
wait = WebDriverWait(driver,40)
driver.get(link)
driver.execute_script("window.scrollTo(0, 200)")
wait.until(EC.element_to_be_clickable((By.LINK_TEXT, 'Annual')))
annual_link = driver.find_element_by_link_text('Annual')
annual_link.click()
print(annual_link.text)
make sure to import these :
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
As per the documentation find_elements_by_xpath(xpath) returns a List with elements if any was found or else an empty list if not. Python's List have no click() method associated with it. Instead find_element_by_xpath(xpath) method have the click() method associated with it. So you have to use find_element_by_xpath(xpath) method inducing a waiter through WebDriverWait inconjunction with expected_conditions set as element_to_be_clickable(locator) as follows:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//a[#class='newBtn toggleButton LightGray' and #data-type='rf-type-button']"))).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
Notice that find_elements_by_xpath is plural it returns a list of elements. Not just one. The list can contain none, exactly one, or more elements.
You can for example click the first match with:
driver.find_elements_by_xpath("/html/body/div[5]/section/div[8]/div[1]/a[1]")[0].click()
or iterate through the list and click all these elements, or you can use the find_element_by_xpath (which returns a single element, if it can be found):
driver.find_element_by_xpath("/html/body/div[5]/section/div[8]/div[1]/a[1]").click()
For me, it was not working, and tried a hell lot of tricks, and none worked. Some people recommended driver.implicitly_wait(10) instead of time.sleep(10) which didn't work. so please try giving time.sleep(10) both above and below the .click() code line, and check if it works or not.

Selenium WebDriver find_element_by_xpath not working for text

I'm trying to click on a link on a webpage that has no ID and no individual class. The only thing to lock it down to is the text 'Sessions'.
I have tried:
driver.find_element_by_xpath("//*[contains(text(),'Sessions')]");
driver.find_element_by_xpath("//*[text()='Sessions']");
Both come back with "No such element".
Edit: I have also tried driver.find_element_by_link_text which also didn't work.
I've tried using the full xpath:
/html/body/div/div/div[1]/div/nav/a[3]
To no avail.
That is a link_Text cause it's between anchor tag, use this :
driver.find_element_by_link_text('Sessions').click()
or
A way more good approach is to use ExplicitWaits :
wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.LINK_TEXT, 'Sessions')))
element.click()
If you want explicit wait you would need to import the below :
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
If the above gives you NoSuchElementException, I would probably suspect this it is in iframe (See the screenshot first tag - I can see body), if it happens to be then in that case you would need to switch to iframe first and continute with this web element.
Code
wait = WebDriverWait(driver, 10)
wait.until(EC.frame_to_be_available_and_switch_to_it((By.XPATH, "iframe xpath here")))
wait.until(EC.element_to_be_clickable((By.PARTIAL_LINK_TEXT, "Sessions"))).click()
Imports :

selenium chromedriver different values of xpath between terminal and actual driver

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
url = 'https://www.msha.gov/mine-data-retrieval-system'
driver = webdriver.Chrome(executable_path='chromedriver')
driver.get(url)
#driver.find_element_by_xpath('//*[#id="mstr90"]/div[1]/div/div') error
#driver.find_elements_by_xpath('//input') gives 3 while in driver gives 10
I am unable to find element where the input "Search by Mine ID by typing here.." is, the document is fully loaded but it can't locate it. What I want to do is simply pass in an input "0100003" then submit
Iframe is present on your page. Before you interact with inputbox you need to switch on to iframee. Refer below code to resolve your issue.
wait = WebDriverWait(driver, 10)
driver.get("https://www.msha.gov/mine-data-retrieval-system")
driver.switch_to.frame("iframe1")
wait = WebDriverWait(driver, 10)
inputBox = wait.until(EC.element_to_be_clickable((By.XPATH, "//div[#class='mstrmojo-SimpleObjectInputBox-empty']"))).click()
inputBox1 = wait.until(EC.element_to_be_clickable((By.XPATH, "//div[#class='mstrmojo-SimpleObjectInputBox-container mstrmojo-scrollNode']//input")))
inputBox1.send_keys("0100003")
Updated Code to handle dropdown
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div#mstr100,mstrmojo-Popup.mstrmojo.SearchBoxSelector-suggest"))).click()
Note: please add below imports to your solution
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
Output:
The element you are trying to find is inside an iframe, so you will need to switch to that iframe first and then do your find element. Also, it's a best practice to use waits to give pages/elements time to load before a find element timeouts and throws an error.
iframe = WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.CSS_SELECTOR, '#iframe1')))
driver.switch_to.frame(iframe)
mine_id = WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.XPATH, '//*[#id="mstr90"]/div[1]/div/div')))
Then you need to click this element to make it interactable.
mine_id.click()
Once you click then you need to re-find the input box before sending keys.
mine_id_input = WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mstr90 input')))
mine_id_input.send_keys('0100003')
To select the suggestion displayed:
suggestion = WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mstr100')))
suggestion.click()
if you wanted to continue on interacting outside the iframe after this is done, you will want to switch back out of the iframe like this:
driver.switch_to.default_content()

Categories