Select specific HREF link using Selenium Python - python

I'm trying to automate a download via Selenium using Python. The website I'm trying to download from has multiple options, with each option having a HTML HREF and an Excel HREF. So the site code looks like this:
</ul>
<li><a class="pnid-642 pv-pid-0 pvid-9972 cid-31"> </a>24. Option 24
<ul>
<li><table width='50%'><tr><td width='20%'> </td><td width='50%'>Select type</td><td width='15%'><A title='Html' HREF='/apps/carteras/genera_xsl_v2.0.php?param=RWJybTl4VEV4MnlHc0VSQVd5T1VKV3Q3STg4Rk5oS1RYUDdaa1dFbDhoWkwzam53L3huQzBnPT0='><span class="fa fa-file-code-o fa-2x" aria-hidden="true"'></span></a></td><td width='15%'><A title='Excel' HREF='/apps/carteras/genera_xsl2xls.php?param=RWJybTl4VEV4MnlHc0VSQVd5T1VKV3Q3STg4Rk5oS1RYUDdaa1dFbDhoWkwzam53L3huQzBnPT0='><span class="fa fa-file-excel-o fa-2x" aria-hidden="true"></span></a></td></tr></table></li>
</ul>
<li><a class="pnid-642 pv-pid-0 pvid-9972 cid-31"> </a>25. Option 25
<ul>
<li><table width='50%'><tr><td width='20%'> </td><td width='50%'>Select type<td width='15%'><A title='Html' HREF='/apps/carteras/genera_xsl_v2.0.php?param=RWJybTl4VEV4MnlHc0VSQVd5T1VKVTBSRDZ5aVNsb2JYUDdaa1dFbDhoWkwzam53L3huQzBnPT0='><span class="fa fa-file-code-o fa-2x" aria-hidden="true"'></span></a></td><td width='15%'><A title='Excel' HREF='/apps/carteras/genera_xsl2xls.php?param=RWJybTl4VEV4MnlHc0VSQVd5T1VKVTBSRDZ5aVNsb2JYUDdaa1dFbDhoWkwzam53L3huQzBnPT0='><span class="fa fa-file-excel-o fa-2x" aria-hidden="true"></span></a></td></tr></table></li>
</ul>
I'm trying to automate the download of the Option 25 Excel file, but as you can see the Excel HREF are identical for each option on the website. Is there a way I can use Selenium to download only that Excel file?
Thanks

To identify the 25th Excel file use following xpath to identify.
driver.find_element(By.XPATH, "//li[contains(., '25. Option 25')]/ul/li//a[#title='Excel']").click()
If you want to make it dynamic you can create a method and pass the option text as parameter.
def DownloadFileOptions(optionName) :
driver.find_element(By.XPATH, "//li[contains(., '{}')]/ul/li//a[#title='Excel']".format(optionName)).click()
DownloadFileOptions('25. Option 25')
DownloadFileOptions('24. Option 24')
I would suggest you to use webdriverwait() and wait for element to be clickable.
WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//li[contains(., '25. Option 25')]/ul/li//a[#title='Excel']"))).click()
you need to import following library.
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

You could try to find the element by the text it contains using
driver.find_elements_by_xpath("//*[contains(text(), '25. Option 25')]")

Related

Selenium with Python - Finding an element after a selected one

I am trying to find an link text but after a specific image but i can't find a way to do it properly with Selenium . My code is just down bellow
browser.get(URL)
browser.find_element_by_xpath('//img[#src="Images"]')
browser.find_element_by_partial_link_text("Bla").click()
I need to start my partial link text selection after the specific xpath. Do you know an easy way to do this ?
This is the architecture of the html page (it's not public and it's just a portion of it ):
<tr style="background-color:#E5E5E5;font-size:9pt;">
<td align="center" style="width:70px;">05/03/2018</td><td align="center" style="width:40px;">
<img id="1" src="Images" style="border-width:0px;">
</td><td>
<span id="2"></span>
<a id="3" href="javascript:OuvreFenetre(3683826,241258727)">Blabla</a>
<span id="4"></span>
</td>
</tr>
I locate the img with the scr="Images" but i need to select the hyperlinks just after that (call here "Blabla").
I'd probably do something like this :
if driver.find_element_by_xpath('//img[#src="src"]').is_displayed():
driver.find_element_by_partial_link_text("words").click()
or if you want to introduce WebDriverWait, use it like below : (more reliable solution)
wait = WebDriverWait(driver, 10)
if wait.until(EC.visibility_of_element_located((By.XPATH, "your xpath here"))).is_displayed():
driver.find_element_by_partial_link_text("words").click()
or If anchor tag is child of image tag the probably you can do like this : //img[#src="src"]/a[contains(#href, ' href here')]
Imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
Update 1 :
You can select anchro tag based on Image src like below :
//img[contains(#src, 'Images')]/following-sibling::a
so instead of these two lines :
browser.find_element_by_xpath('//img[#src="Images"]')
browser.find_element_by_partial_link_text("Bla").click()
You can use the above xpath like this :
browser.find_element_by_xpath("//img[contains(#src, 'Images')]/following-sibling::a").click()
or
//img[contains(#src, 'Images')]/following-sibling::a[contains(#href, 'javascript')]
Update 2:
Use the below xpath :
//img[contains(#src, 'Images')]/../following-sibling::td/a

Is there a way to select an item on a webpage with Selenium with no unique ID?

My objective is to open a webpage, and click the app button for a specific app, like Anaplan. In the past, I've used get element by CSS selector with the combination of class, and ID, as shown in this past post.
first_item = driver.find_element_by_id("anaplan")
I've come across a webpage where the buttons seem to have literally no ID whatsoever, or unique values:
HTML output of the Anaplan App button:
<a
aria-label="launch app Anaplan"
class="chiclet a--no-decoration"
data-se="app-card"
href="https://gartner.okta.com/home/anaplan/0oaforg08lyATdLuw4x6/2487"
draggable="true"
><article class="chiclet--article">
<button
class="chiclet--action"
tabindex="0"
aria-label="Settings for Anaplan"
data-se="app-card-settings-button"
>
<svg
class="chiclet--action-kebab"
width="20"
height="4"
viewBox="0 0 20 4"
fill="#B7BCC0"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="2" cy="2" r="2"></circle>
<circle cx="10" cy="2" r="2"></circle>
<circle cx="18" cy="2" r="2"></circle>
</svg>
</button>
<section class="chiclet--main" data-se="app-card-main">
<img
class="chiclet--main-logo"
src="https://ok11static.oktacdn.com/fs/bcg/4/gfs1ev15ab63zqgZ91d8"
alt="Anaplan logo"
/>
</section>
<footer class="chiclet--footer" data-se="app-card-footer">
<o-tooltip content="Anaplan" position="bottom" class="hydrated"
><div slot="content"></div>
<div aria-describedby="o-tooltip-0">
<h1 class="chiclet--app-title" data-se="app-card-title">Anaplan</h1>
</div>
</o-tooltip>
</footer>
</article>
</a>
I grabbed the Xpath of the Anaplan button, which shows the following:
/html[#class='hydrated wf-proximanova-n4-inactive wf-
inactive']/body[#class='default']/div[#id='root']
/div[#class='enduser-app ']/section[#class='content-frame']
/main[#class='main-container has-top-bar']/div[#class='dashboard--main']/section[#id='main-
content']/section[#class='chiclet-area']
/section[#class='chiclet-grid--container']
/section/section[#class='chiclet-grid section-appear-done section-enter-done']
/a[#class='chiclet a--no-decoration'][1]/article[#class='chiclet--article']
The only differences between apps is the number in the bracket:
/a[#class='chiclet a--no-decoration'][1], where 1 seems to be Anaplan, 3 is G Drive, and so on. Is there a way to select elements such as this where there appears to be no unique identifier at all?
To locate the first button you can use one of the following xpaths //a[#aria-label='launch app Anaplan'] or //a[contains(#href,'anaplan')] and there are many other unique combinations. The same can be done with css selectors
Similarly to the above there are several combinations for all the other navigation buttons you provided here.
In case the element located inside <iframe> you have to switch to that <iframe> first and get out of it after that.
Locate the <iframe> with
iframe = driver.find_element_by_xpath("//iframe[#name='iframeName']") or whatever locator that it matches
Then switch_to the <iframe>:
driver.switch_to.frame(iframe)
If after that you need to continue anywhere out of the <iframe> switch out of it with
driver.switch_to.default_content()
It is possible both with xpath and css.
Example of xpath:
Anaplan:
//a[contains(#aria-label, 'Anaplan')]/article/button
Or:
//button[contains(#aria-label, 'Settings for Anaplan')]
Spam Quarantine:
//a[contains(#aria-label, 'Spam Quarantine')]
G-suite
//a[contains(#aria-label, 'G Suite Drive')]
The main idea is that you can find an element by writing a partial name of an attribute.
Update:
If an element is located inside an iframe, you should wait for it to load and switch to it. Selenium has very convenient method for it: frame_to_be_available_and_switch_to_it
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get(url)
wait = WebDriverWait(driver, 15)
wait.until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, "iframe[data-testid=shell-content]")))
After switching to iframe you work with elements inside it.

Selenium cannot find element by name or id (Python)

I am trying to automate logging into a website using selenium, but I am getting "no such element message" error. Here is my code, with the link to the website included:
from selenium import webdriver
import time
import datetime
driver = webdriver.Chrome("C:\\Users\\Family\\Downloads\\chromedriver_win32\\chromedriver.exe")
driver.get("https://login.microsoftonline.com/c4d72b4d-8155-4a90-9155-7705148c41ca/saml2?SAMLRequest=jdE9a8MwEAbgvdD%2fYLRbkh3ZVoQdCO0SSJek7dClnJVzYrClVCeX%2fvw6DaEdu90HLzzc1espntwOPyakmGweG0YwDuHav3eqqFSHOZRQKZAZdJDpDjToKisXiCx5xUC9dw3LuWTJhmjCjaMILs4jmWepVKnMnrPCyNIscq601pVUbyxZE2GIc%2fbBO5pGDHsMn73Fl922YacYz2SEiAdqOQ4IwfXu6F2E0HtuQRzyQQxnAbNeDP7YO3Fxby8Vn3cs%2bRoHRw2bgjMeqCfjYEQy0Zr9%2bmlrZq45Bx%2b99QNb3d8lSf2DD%2f8Jwo3OVjdokdlSVrZKUS10quTSphrLIi00drrVebksWh7RzYch3ob%2beIp0Bovc%2bvGXXosrYgbV4u9nVt8%3d&RelayState=%2fd2l%2fhome&sso_reload=true")
login_button = driver.find_element_by_id("i0116")
login_button.send_keys("sajjad.jessa#student.tdsb.on.ca")
And here is the element I am trying to access with my code:
<input type="email" name="loginfmt" id="i0116" maxlength="113" lang="en" class="form-control ltr_override input ext-input text-box ext-text-box" aria-required="true" data-bind="
externalCss: {
'input': true,
'text-box': true,
'has-error': usernameTextbox.error },
ariaLabel: tenantBranding.UserIdLabel || str['CT_PWD_STR_Username_AriaLabel'],
ariaDescribedBy: 'loginHeader' + (pageDescription && !svr.fHideLoginDesc ? ' loginDescription' : ''),
textInput: usernameTextbox.value,
hasFocusEx: usernameTextbox.focused,
placeholder: $placeholderText" aria-label="Enter your TDSB email address here, then click Next" aria-describedby="loginHeader" placeholder="Enter your TDSB email address here, then click Next">
From other answers I understand that you have to use driver.find_element_by_css_selector() and driver.switch_to.frame(), but if you look at the full hypertext of the website, the first frame to go into is a "div" tag without any attributes. It is however the only "div" tag alongside two "script" tags. I need the correct code to go into the frame, or another method to automate logging in.
It seems synchronization Issue.
Induce WebDriverWait() and wait for element_to_be_clickable()
login_button =WebDriverWait(driver,20).until(EC.element_to_be_clickable((By.ID,"i0116")))
login_button.send_keys("sajjad.jessa#student.tdsb.on.ca")
You need to import following libraries.
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
login_button =WebDriverWait(driver,20).until(EC.element_to_be_clickable((By.NAME,"loginfmt")))
The most of the large websites now uses dynamic layout. You should look for the parents with static css selector, and then do something like that:
.parent_css_selector > div:nth-child(1) > div:nth-child(3) > img > input
Token '>' is supposed to look only for direct childs, token ':nth-child' asserts to the child number in parent.
Example:
<div class="parent_css_selector">
<div id="random_id_12312313">
<div id="unneccesary_123123"></div>
<div id="random_id_341234">
<form id="random_id_345545">
<span></span>
<span></span>
<span></span>
<span></span>
<span>
<input id="neccesary_13843942"></input>
</span>
</form>
</div>
</div>
</div>
You can use this selector to access the input:
.parent_css_selector > div > div:nth-child(2) > form > span:nth-child(5) > input

clicking through all rows in a table from angular using selenium python web driver

I'm trying to iterate through a certain column of rows on a table/grid of an HTML page with I assume is a dynamic angular element.
I have tried to iterate through the rows by creating a list of common xpaths between each row. This only help me achieve 32 rows and not the full amount which is 332. I also tried waiting to see if the webpage would load and then have the full amount of web-elements. Then I tried to run a loop on searching for similar xpaths by scrolling down to the last element in the list. None of these ways helped me to iterate through the rows. Also I will not be able to share the website since the website is private.
python
webelement = []
driver.implicitly_wait(20)
ranSleep()
for webelement in driver.find_elements_by_xpath('//a[#class="ng-pristine ng-untouched ng-valid ng-binding ng-scope ng-not-empty"]'):
driver.implicitly_wait(20)
html for the rows
<a ng-model="row.entity.siteCode"
ng-click="grid.appScope.openSite(row.entity)"
style="cursor:pointer"
class="ng-pristine ng-untouched ng-valid ng-binding ng-scope ng-not-empty">
Albuquerque
<span title="Open defect(s) on site"
ng-show="row.entity.openDeficiencies"
style="background-color:yellow; color:#000;"
class="ng-hide">
!
</span>
</a>
I expect to be able to click all the links in each row once this is solved
Here is the snippet of the html code
<div id="table1" class="container-fluid">
<div ui-i18n="en"
class="grid advanceSearch ui-grid ng-isolate-scope grid1554731599680"
id="grid1" ui-grid="gridOptions"
ui-grid-expandable="" ui-grid-rowedit=""
ui-grid-resize-columns="" ui-grid-selection=""
ui-grid-edit="" ui-grid-move-columns="">
<!-- TODO (c0bra): add "scoped" attr here, eventually? -->
<style ui-grid-style="" class="ng-binding">
.grid1554731599680 {
/* Styles for the grid */
}
here is how the page looks with the table format
Here is the rows that I want to click through all of them
You might still be able to increment through each link by appending to the class name, as they seem to be a little unique in nature and using the last number as a char from the alphabet. Perhaps something like below could work :) Expanding on the classname's last character, in-case there's an increase, should solve the problem of there being more than 26.
Steps taken: increment classnames >append successes to list >move to link within list >click link >List item
import string
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
alpha = string.ascii_uppercase
successfulIncs = []
for char in alpha:
className = 'ng-pristine.ng-scope.ui-grid-coluiGrid-000' + char
try:
driver.find_elements_by_class_name(className)
successfullIncs.append(className)
except NoSuchElementException:
print("Element not found")
### First move to our element
for line in successfulIncs:
link = WebDriverWait(driver, 3).until(EC.visibility_of_element_located
(By.CLASS_NAME, line))
ActionChains(driver).move_to_element(link).perform()
#Click
ActionChains(driver).move_to_element(link).click(link).perform()

Can't trigger a click on a certain link using selenium

I've written a script in python with selenium to click on a certain link in a webpage to download an excel file. However, when I execute my script, it throws timeout exception. How can I make it work? Any help will be greatly appreciated.
Link to the site: webpage
Script I've tried with:
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
driver = webdriver.Chrome()
wait = WebDriverWait(driver, 10)
driver.get('replace_with_above_link')
item = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".hasmore #dlink")))
item.click()
driver.quit()
Html elements which contain the dropdown options:
<li class="hasmore drophover"><span>Share & more</span><div><ul><li><button class="tooltip" tip="Use a customizable report creator that can<br>output HTML, CSV, or a shareable link." id="share_on_ajax_result_table">Modify & Share Table</button></li><li><button class="tooltip" tip="Get a bit of widget code to emed this table on your site">Embed this Table</button></li><li><button class="tooltip" tip="Convert the table below to comma-separated values<br>suitable for use with excel">Get as Excel Workbook (experimental)</button><a id="dlink" style="display: none;"></a></li><li><button class="tooltip" tip="Export table as <br>suitable for use with excel">Get table as CSV (for Excel)</button></li><li><button class="tooltip" tip="">Strip Mobile Formatting</button></li><li><a id="a_ajax_result_table" name="ajax_result_table" href="#ajax_result_table::none">Copy Link to Table to Clipboard</a></li><li><button class="tooltip" tip="">About Sharing Tools</button></li><li><button class="tooltip" tip="">Video: SR Sharing Tools & How-to</button></li><li><button class="tooltip" tip="">Video: Stats Table Tips & Tricks</button></li></ul></div></li>
Location of that file in that webpage (the desired link is marked with pencil):
Target link is hidden and so wait for its visibility will always fail. You should try to handle button node instead:
item = wait.until(EC.visibility_of_element_located((By.XPATH, "//li[span='Share & more']")))
item.click()
wait.until(lambda driver: "drophover" in item.get_attribute("class"))
item.find_element_by_xpath("//button[.='Get as Excel Workbook (experimental)']").click()
As you are trying to click on the link with text as Get as Excel Workbook (experimental) and as per your comment you are already able to click on the Share&more link in the first place and found it working next your intended <a> tagged element contains the attribute style set to display: none;. So to invoke click() to download you can use the following code block :
Get_as_Excel_Workbook_link = driver.find_element_by_xpath("//li[#class='hasmore drophover']//ul//li//a[#id='dlink']")
driver.execute_script("arguments[0].removeAttribute('style')", Get_as_Excel_Workbook_link)
Get_as_Excel_Workbook_link.click()
Update A
As per your comment :
I am not sure if the xpath which you have used is a valid one or not :
"//li[a[#id='dlink']]/a"
You tried using :
Get_link = driver.find_element_by_xpath("//li[a[#id='dlink']]/a")
print(Get_link.get_attribute("outerHTML"))
But why? Is there any necessity?
As per my research and analysis you can be assured that you are at the right place. See the formatted version of the HTML you have shared and the resolution of the xpath I have provided.
<li class="hasmore drophover"><span>Share & more</span>
<div>
<ul>
<li><button class="tooltip" tip="Use a customizable report creator that can<br>output HTML, CSV, or a shareable link." id="share_on_ajax_result_table">Modify & Share Table</button></li>
<li><button class="tooltip" tip="Get a bit of widget code to emed this table on your site">Embed this Table</button></li>
<li><button class="tooltip" tip="Convert the table below to comma-separated values<br>suitable for use with excel">Get as Excel Workbook (experimental)</button>
<a id="dlink" style="display: none;"></a>
</li>
<li><button class="tooltip" tip="Export table as <br>suitable for use with excel">Get table as CSV (for Excel)</button></li>
<li><button class="tooltip" tip="">Strip Mobile Formatting</button></li>
<li><a id="a_ajax_result_table" name="ajax_result_table" href="#ajax_result_table::none">Copy Link to Table to Clipboard</a></li>
<li><button class="tooltip" tip="">About Sharing Tools</button></li>
<li><button class="tooltip" tip="">Video: SR Sharing Tools & How-to</button></li>
<li><button class="tooltip" tip="">Video: Stats Table Tips & Tricks</button></li>
</ul>
</div>
</li>
So the result you have seen is pretty correct. Now, for you understanding I have inserted some text as MyLink within the intended tag :
<a id="dlink" style="display: none;"></a>
Converted as :
<a id="dlink" style="display: none;">MyLink</a>
See the result :
Check out my solution once again I can ensure that works.
Update B
unable to locate element is good message to debug perhaps apart from "display: none;" you have pulled a rug over the actual issue by mentioning clicked on the share&more link in the first place and found it working. Troubles come up when i try to initiate a click on the link.
If you observe the HTML the element is within class="tooltip" so you need to induce a waiter as follows :
//perform click on the link Share&more
Get_as_Excel_Workbook_link = WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//li[#class='hasmore drophover']//ul//li//a[#id='dlink']")))
driver.execute_script("arguments[0].removeAttribute('style')", Get_as_Excel_Workbook_link)
Get_as_Excel_Workbook_link.click()

Categories

Resources