Selenium wait for element to be clickable python - python

All, I'm needing a little assistance with Selenium waits. I can't seem to figure out how to wait for an element to be ready.
The element that I am needing to wait I can locate and click using my script via the code below...
CreateJob = driver.find_element_by_xpath(".//*[#id='line']/div[1]/a")
or
CreateJob = driver.find_element_by_partial_link_text("Create Activity")
I'm needing to wait for this element to be on the page and clickable before I try to click on the element.
I can use the sleep command, but I have to wait for 5 seconds or more and it seems to be unreliable and errors out 1 out of 8 times or so.
I can't seem to find the correct syntax to use.
the HTML code for this is below.
<document>
<html manifest="https://tddf/index.php?m=manifest&a=index">
<head>
<body class="my-own-class mozilla mozilla48 mq1280 lt1440 lt1680 lt1920 themered" touch-device="not">
<noscript style="text-align: center; display: block;">Please enable JavaScript in your browser settings.</noscript>
<div id="wait" style="display: none;">
<div id="processing" class="hidden" style="display: none;"/>
<div id="loading" class="hidden" style="display: none;"/>
<div id="loadingPartsCatalog" class="hidden"/>
<div id="panel">
<div id="top-toolbar" class="hidden" style="display: block;">
<div id="commands-line" class="hidden" style="display: block;">
<div id="line">
<div class="action-link">
<a class="tap-active" href="#m=activity/a=set" action_link_label="create_activity" component_gui="action" component_type="action">Create Activity</a>
</div>
<div class="action-link">
<div class="action-link">
<div class="action-link">
</div>
<div id="commands-more" style="display: none;">
<div id="commands-list" class="hidden">
</div>
<div id="provider-search-bar" class="hidden center"

Here is a link to the 'waiting' section of the Python Selenium docs: http://selenium-python.readthedocs.io/waits.html#explicit-waits
You wait should look like this:
element = WebDriverWait(driver, 10).until(
EC.visibility_of((By.XPATH, ".//*[#id='line']/div[1]/a"))
)

I find this to be the easiest:
driver.implicitly_wait(10)
Where it waits for up to 10 seconds before the script might crash if expected conditions aren't met. I think it's better than always checking for the visibility of, the clickability of, or whatever it is about the element. Less effective and more error prone, however. So it would depend more on why you use selenium.
It also lets me cut down on try/except statements in my selenium scripts, and since I've found out about this I've reduced many time.sleep() functions as well.

Related

Cannot find element in selenium python?

I am trying to navigate to a search box and send_keys with selenium python but completely stuck.
And here is the source code snippet:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<div id="LeftTreeFrame" class="leftNavBackground" >
<div class="ui-widget searchPanelContainer">
<div id="searchPanel" class="search-field-container search-field-container-margin">
<input type="text" doesntDirty id="Search" name="Search" class="search-text-field-left-tree-frame" NoHighlight="nohighlight"/>
<div class="search-field-icon-container">
<a id="searchlbl" href="#"><img src="../images/normal_search_u39.svg" title="Go To Page" /></a>
</div>
</div>
</div>
<div id='pageNavigation'>
<div id='ootbNavigationPage'></div>
<div id='favoriteNavigationPage'></div>
<div id='adminNavigationPage'></div>
<div id='navigationEmptyState' class="treeEmptyState">
<div class="message"></div>
</div>
</div>
<div class="navigation-view-mode-container">
<div class="box" onclick="renderModel(0)">
<button type="button">
<span class="svg-load ootb-icon" data-src="~/images/Reskin/ootb-icon.svg"></span>
</button>
</div>
<div class="star" onclick="renderModel(1)">
<button type="button">
<span class="svg-load star-icon" data-src="~/images/Reskin/star.svg"></span>
</button>
</div>
<div class="person" onclick="renderModel(2)">
<button type="button">
<span class="svg-load person-icon" data-src="~/images/Reskin/person-nav.svg"></span>
</button>
</div>
</div>
</div>
When I try to do
element = driver.find_element(By.XPATH, '//input[#name="Search"]')
element.send_keys('test')
I get error "selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable"
I have tried everything I can imagine, but cannot click the element or send keys.
Also, this page is a new page that opens after the last successful click. I first tried switching to this page by
#printing handles
handles = driver.window_handles
i=0
for handle in handles:
print(f"Handle {i}: {handle}\n")
i +=1
#after confirming new page is second handle via:
driver.switch_to.window(handles[1])
print(f" Title: {driver.title}")
print(f" Current url: {driver.current_url}")
print('\n')
#I can even find the tag I am looking for after switching to new window:
all_div_tags = driver.find_elements(By.TAG_NAME, "input")
for tag in all_div_tags:
print(f"Attribute name: {tag.get_attribute('name')}\n")
#but i cannot get to the search box. Thank you in advance!
Look at the html code, notice that //input[#name="Search"] is contained in an <iframe>. In order to select an element inside an iframe with find_element() you have first to switch to the iframe, as shown in the code
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.ID, "frmCode")))
element = driver.find_element(By.XPATH, '//input[#name="Search"]')
...

Choosing appropriate locators when scraping dynamic content with Python and Selenium

I am trying to understand the correct way to select specific elements of a webpage using python and selenium, I am uncertain what dictates which approach to take such as xpath or CSS and so on.
https://dutchie.com/embedded-menu/revolutionary-clinics-somerville/menu
<a class="consumer-product-card__StyledLink-ncbvk2-1 jpGhIo" href="/embedded-menu/berkshire-roots/menu/cbd-tincture-2-1-225mg">
<span>CBD Tincture 2:1 225mg Details</span>
<div class="product-card__Container-sc-7s6mw-0 iWHVJj">
<div class="product-card__Content-sc-7s6mw-1 cfcIOW">
<div class="product-information__Container-sc-65h5ke-0 ejVwks">
<img class="product-information__StyledProductImage-sc-65h5ke-1 jupjtQ" width="218" height="218" src="https://images.dutchie.com/0f253b35120facc1465b75b08bfd4d66?auto=format&dpr=1&bg=FFFFFF&crop=faces&fit=fill&w=218&h=218&ixlib=react-7.2.0" alt="" srcset="https://images.dutchie.com/0f253b35120facc1465b75b08bfd4d66?auto=format&dpr=2&bg=FFFFFF&crop=faces&fit=fill&w=218&h=218&ixlib=react-7.2.0 2x, https://images.dutchie.com/0f253b35120facc1465b75b08bfd4d66?auto=format&dpr=3&bg=FFFFFF&crop=faces&fit=fill&w=218&h=218&ixlib=react-7.2.0 3x">
<div class="product-information__ProductInfo-sc-65h5ke-2 bwhblJ">
<div class="product-information__Price-sc-65h5ke-7 eEqLUB">$36.95</div>
<div class="product-information__BrandContainer-sc-65h5ke-5 dlSlvE list-only">
<div class="product-information__Brand-sc-65h5ke-6 ftehWE">Berkshire Roots</div>
</div>
<div class="product-information__TitleContainer-sc-65h5ke-3 fOoVwz list-only false">
<div class="product-information__Title-sc-65h5ke-4 eBIyJW --line2">CBD Tincture 2:1 225mg</div>
</div>
<div class="product-information__TitleContainer-sc-65h5ke-3 fOoVwz mobile-and-card">
<div class="product-information__Title-sc-65h5ke-4 eBIyJW">CBD Tincture 2:1</div>
<div class="product-information__Title-sc-65h5ke-4 eBIyJW --line2"> 225mg</div>
</div>
<div class="product-information__DetailsContainer-sc-65h5ke-9 ifqkuO">
<div class="product-information__Strain-sc-65h5ke-10 eWkod --high-cbd">High CBD</div>
<div class="product-information__PotencyInfo-sc-65h5ke-14 gUReQf"><b>THC: </b>72.3 mg | <b>CBD: </b>160.3 mg</div>
</div>
</div>
</div>
<div class="product-weights__Container-nwgli1-0 gwUwAi">
<div class="product-weights__Weights-nwgli1-1 kiObrJ">
<div aria-label="Add 0.41g to cart for $36.95" data-cy="product-card-weight" class="weight__Container-sc-11f1l3-2 dNvnhd">
<div class="weight__Price-sc-11f1l3-4 ZtHqz">$36.95</div>
<div class="weight__IconContainer-sc-11f1l3-1 zqIJt">
<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 10 10">
<path fill="#A6ACB3" fill-rule="nonzero" d="M9.176 5c0-.407-.031-.723-.438-.723l-3.022.007.007-3.022c0-.407-.326-.428-.722-.438-.407 0-.723.03-.722.436l.003 3.012-3.022.007c-.406 0-.426.325-.436.722-.01.396.031.722.438.722l3.022-.007.003 3.012c0 .407.326.427.723.438.407 0 .722-.03.721-.437l-.003-3.011 3.012.003c.406 0 .437-.315.436-.722z"></path>
</svg>
</div>
</div>
<div class="product-weights__Fill-nwgli1-2 dtfdkt"></div>
</div>
</div>
</div>
</div>
</a>
How would I use a loop of sorts to access each and every "consumer-product-card" without having scrolled to the bottom of the page? Or would I need to force the page to scroll first? Is the "consumer-product-card" approach correct or would xpath make more sense? With either I find it difficult to understand which is ideal for what reason, or even how to select it in one instance, as well as the next and next until I reach the end.
Thank you.
To find all cards use:
driver.find_elements_by_xpath("//div[contains(#class,'consumer-product-card__InViewContainer-ncbvk2-0 dWfGpk')]")
Then use as an example links I gave you in the previous question.
UPDATE
Solution to start with:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome(executable_path='/snap/bin/chromium.chromedriver')
driver.get('https://dutchie.com/embedded-menu/revolutionary-clinics-somerville/menu')
wait = WebDriverWait(driver, 30)
wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".product-card__Content-sc-7s6mw-1.cfcIOW")))
cards = driver.find_elements_by_css_selector(".product-card__Content-sc-7s6mw-1.cfcIOW")
data = []
for card in cards:
name = card.find_element_by_css_selector(".product-information__TitleContainer-sc-65h5ke-3.fOoVwz.list-only").text
data.append(name)
for i in data:
print(i)
It waits for cards and prints their names. But scrolling etc, other elements are completely different questions.
I found css selectors more suitable for this case.
Result is three items:
Rick Simpson Oil (RSO)
Live Sugar - Purple Pineapple Express
Live Sugar - Gelato #33
This is kind of an opinionated question.
I would likely use the simplest CSS Selector I can find that uniquely defines the element. XPath is slower and, I find, likely more brittle and harder to find good selectors for elements. But there is no "correct" approach.
I'm a little confused regarding the goal of the rest of the question. I think we would need some more detail and the code you've used to attempt this.
Also, your HTML is formatted on one line and very hard to view.

python selenium find elements by xpath returns a list of a links but element not interactable

I am using this xpath //a[contains(., 'Download Python')]
on www.python.org/downloads
in chrome developer tools I get 4 matches, each one match the following
<a class="button" href="https://www.python.org/ftp/python/3.7.4/python-3.7.4-macosx10.9.pkg">Download Python 3.7.4</a>
<a class="button" href="https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tar.xz">Download Python 3.7.4</a>
<a class="button" href="https://www.python.org/ftp/python/3.7.4/python-3.7.4.exe">Download Python 3.7.4</a>
<a class="button" href="https://www.python.org/ftp/python/3.7.4/python-3.7.4.exe">Download Python 3.7.4</a>
the problem is only the third is clickable, the others give an error ElementNotInteractableException: Message: element not interactable
if I print the text of each element only the third one displays the text 'Download Python 3.7.4'
the rest print blank
elems = driver.find_elements_by_xpath(
"//a[contains(., 'Download Python')]")
for elem in elems:
print("-------")
print(elem.text)
print(elem.tag_name)
-------
a
-------
a
-------
Download Python 3.7.4
a
-------
a
as a result only elems[3].click works
This problem is not with your code, the python download page detects the OS you are browsing from and shows you the download link for that OS only. It has total 4 such buttons
<div class="download-os-mac-osx" style="display: none;">
<h1 class="call-to-action">Download the latest version for Mac OS X</h1>
<p class="download-buttons">
<a class="button" href="https://www.python.org/ftp/python/3.7.4/python-3.7.4-macosx10.9.pkg">Download Python 3.7.4</a>
</p>
</div>
<div class="download-os-source" style="display: none;">
<h1 class="call-to-action">Download the latest source release</h1>
<p class="download-buttons">
<a class="button" href="https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tar.xz">Download Python 3.7.4</a>
</p>
</div>
<div class="download-os-windows" style="">
<h1 class="call-to-action">Download the latest version for Windows</h1>
<p class="download-buttons">
<a class="button" href="https://www.python.org/ftp/python/3.7.4/python-3.7.4.exe">Download Python 3.7.4</a>
</p>
</div>
<div class="download-unknown" style="display: none;">
<h1 class="call-to-action">Download the latest version of Python</h1>
<p class="download-buttons">
<a class="button" href="/downloads/release/python-374/">Download Python 3.7.4</a>
</p>
</div>
You need to select the one which has style = "" rest others wont be displayed on the page.
You can also use the xpath with index and it will point to the button corresponding to the index
(Xpath)[index]
Eg: (//*[#class="button"])[0] -for first button
Let me know if it was helpful

Click ComboButton item with Selenium

I am trying to preform a simple click, but cannot find out what way to find it due to the type of element it is.
<div class="active">
<div class="action-title">Reconcile All</div>
<div class="action-description">Reconcile all IPv4 addresses</div>
</div>
<div class="active">
<img src="/images/icons/small/checks.gif" border="0">
</div>
I have tried doing it several ways. Such as,
driver.find_elements_by_link_text("Reconcile All").click()
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.LINK_TEXT, "Reconcile All"))).click()
I even tried based of the icon
driver.find_element_by_xpath("//*[contains(#src,'/images/icons/small/checks.gif')]").click()
Thanks in advance for any help
Div element can't click using link_text try Use following xpath and Webdriverwait to click.
WebDriverWait(driver,20).until(EC.element_to_be_clickable((By.XPATH,"//div[#class='active']//div[#class='action-title'][contains(.,'Reconcile All')]"))).click()

How to click/submit onclick values in Python

I have a question about the Selenium Python code I'm writing.
The website has a bunch of time slots, and I'm trying to simulate a mouse click, specifically targeting the 8:30-11:30 timeslot.
Here is a snippet the html:
<div class="item-link" onclick="$('#SelectedStartTime').val('2015/10/16 08:30:00');$('#frmTimes').submit();">
<div class="item">
<div class="title">8:30 AM-11:30 AM
<span class="available">(Spaces: <strong class="num">20</strong>)</span>
</div>
</div>
</div>
<div class="item-link" onclick="$('#SelectedStartTime').val('2015/10/16 09:00:00');$('#frmTimes').submit();">
<div class="item">
<div class="title">9:00 AM-12:00 PM
<span class="available">(Spaces: <strong class="num">20</strong>)</span>
</div>
</div>
</div>
</body>
</html>
I've tried targeting the "8:30 AM-11:30 AM" text,
Time0830 = driver.find_elements_by_class_name("title")
for each in Time0830:
if "8:30 AM-11:30 AM" in each.text:
each.click()
break
for each in Time0830:
if each.text == "8:30 AM-11:30 AM"
each.click()
break
Both do not do anything.
I've also tried targeting the onclick based on this question.
Time0830 = driver.find_element_by_xpath('.//div[contains(#onclick,"8:30 AM-11:30 AM")]')
print (Time0830)
Time0830.click()
This resulted in Error: NoSuchElementException
Time0830 = driver.find_element_by_css_selector("div[onclick*='8:30 AM-11:30 AM']")
print(Time0830)
Time0830.click()
This also resulted in Error: NoSuchElementException.
What do I need to do to get this to work?
EDIT!!
Thanks for the help on xpath help. I think the problem was also that I could not get it to click.
I've been using .click(), but it's not working any more.
My code is now:
time.sleep(1.5)
Time0830 = driver.find_element_by_xpath('//div[#class = "item-link" and .//div[#class = "title" and contains(text(),"8:30 PM-11:30 PM")]]').click()
But the page does not move, still. I have time.sleep to account for loading time.
I've tried it without the click() and it seems to find an element. Am I finding the wrong element, or using the wrong function?
How about you would match a div with item-link class that has a descendant div element having 8:30 AM-11:30 AM text:
//div[#class = "item-link" and .//div[#class = "title" and contains(., "8:30 AM-11:30 AM")]]

Categories