Iterate through table row images and click on hyperlink - python

I have a table with multiple rows ( tr) that contains multiple cells ( td )
one of the cells contains an image hyperlink
I would link to iterate through the table rows and click on every cell containing the image of each row, using selenium.
for example, this is one of my tables:
<table class="thetable" cellspacing="1" >
<thead></thead>
<tbody>
<tr class="visibleRow">
<td class="Item"></td>
<td class="modified" style="color: gray;"></td>
<td class="imageHyperlink">
<a href="#" role="button" title="Edit the item">
<img src="web/service/editRow.gif" />
</a>
</td>
</tr>
<tr class="visibleRow"></tr>
<tr class="anotherow" style="display: none;"></tr>
<tr class="visibleRow"></tr>
<tr class="editorRow" style="display: none;"></tr>
</tbody>
</table>
the only rows that I want to iterate through are the ones containing the class name visibleRow, and the only cells that need to be clicked on are the cells containing the class name imageHyperlink
I implemented a for loop that iterate through the rows with class visibleRow, store the cell class name inside cell variable. and click on the cell:
for row in driver.find_elements_by_css_selector("tr.visibleRow"):
cell = row.find_elements_by_class_name("imageHyperlink")
cell.click()
However I am getting this error as it seems the cell is not the clickable item:
AttributeError: 'list' object has no attribute 'click'
How can I fix this ?

The call row.find_elements_by_class_name("imageHyperlink") (note the plural name elements) returns a list, which in your case will have zero or one element. Adding a second level of iteration should fix the problem:
for row in driver.find_elements_by_css_selector("tr.visibleRow"):
for cell in row.find_elements_by_class_name("imageHyperlink"):
cell.find_element_by_tag_name("a").click()
The inner loop iterates over the children of the that have the class imageHyperLink; in your example, there will be either one of these (for the first visible row) or none (for the others). It then finds the first <a> child element and clicks on it.

Related

How do I click on the next instance of an element that has the same attributes with another element in Python Selenium?

I'm trying to click on an element in the same group, that has the same attributes names and values.
I wanna click on the second td tag from this code:
<table id="FolderIcons" class="mstrLargeIconView">
<tbody>
<tr>
<td class="mstrLargeIconView" colspan="1" rowspan="1"></td>
<td class="mstrLargeIconView" colspan="1" rowspan="1"></td>
</tr>
</tbody>
</table>
I can click on the first element with this code:
wait.until(EC.visibility_of_element_located((By.XPATH,"//td[#rowspan='1']"))).click()
So, How can I click on the second element with the same attribute values?
Try this:
td1=wait.until(EC.visibility_of_element_located((By.XPATH, '//table[#id="FolderIcons"]/tbody/tr[1]/td[1]'))).click()
td2=wait.until(EC.visibility_of_element_located((By.XPATH, '//table[#id="FolderIcons"]/tbody/tr[1]/td[2]'))).click()

Python Selenium, select the next element in html DOM

i have to make some automation on a page.
The page consists of table where inside each td element i have 2 a tags, the first one with a class, the second one has no class or id.
i can easily select the one with the class, but how to get the other one? is there a way to select the element next to another one like in css?
this is a draft of the structure of the page
<table>
<tr>
<td>
<a class="mylink"> element 1 </a>
<a>
<img src="">
</a>
</td>
</tr>
<tr>
<td>
<a class="mylink"> element 2 </a>
<a>
<img src="">
</a>
</td>
</tr>
</table>
I can select the first one with
fileLinkClass = "mylink"
driver.find_element(by=By.CLASS_NAME, value=fileLinkClass)
but i need to select and click the a link without the class. How can i accomplish this?
Thank you so much
You can use xpath selector
'//td/a[2]'
to find all second 'a's under a 'td'
Try using css selector
For single element selection
driver.find_element(By.CSS_SELECTOR,'.mylink + a')
For multiple elements selection
driver.find_elements(By.CSS_SELECTOR,'.mylink + a')
Make a list slicing then click. For example:
element = driver.find_elements(By.CSS_SELECTOR,'.mylink + a')
element = element[0].clik()
element = element[1].clik()

Finding sibling items between headers

I'm trying to scrape some documentation for data files composed in XML. I had been writing the XSD manually by reading the pages and typing it out, but it has occurred to me that this is a prime case for page scraping. The general format (as far as I can tell based on a random sample) is something like the following:
<h2>
<span class="mw-headline" id="The_.22show.22_Child_Element">
The "show" Child Element
</span>
</h2>
<dl>
<dd>
<table class="infotable">
<tr>
<td class="leftnormal">
allowstack
</td>
<td>
(Optional) Boolean – Indicates whether the user is allowed to stack items within the table, subject to the restrictions imposed for each item. Default: "yes".
</td>
</tr>
<tr>
<td>
agentlist
</td>
<td>
(Optional) Id – If set to a tag group id, only picks with the agent pick's identity tag from that group are shown in the table. Default: None.
</td>
</tr>
<tr>
<td>
allowmove
</td>
<td>
(Optional) Boolean – Indicates whether picks in this table can be moved out of this table, if the user drags them around. Default: "yes".
</td>
</tr>
<tr>
<td>
listpick
</td>
<td>
(Optional) Id – Unique id of the pick to take the table's list expression from (see listfield, below). Note that this does not work when used with portals. Default: None.
</td>
</tr>
<tr>
<td>
listfield
</td>
<td>
(Optional) Id – Unique id of the field to take the table's list expression from (see listpick, above). Note that this does not work when used with portals. Default: None.
</td>
</tr>
</table>
</dd>
</dl>
<p>
The "show" element also possesses child elements that define additional behaviors of the table. The list of these child elements is below and must appear in the order shown. Click on the link to access the details for each element.
</p>
<dl>
<dd>
<table class="infotable">
<tr>
<td class="leftnormal">
<a href="index.php5#title=TableDef_Element_(Data).html#list">
list
</a>
</td>
<td>
An optional "list" element may appear as defined by the given link. This element defines a
<a href="index.php5#title=List_Tag_Expression.html" title="List Tag Expression">
List Tag Expression
</a>
for the table.
</td>
</tr>
</table>
</dd>
</dl>
There's a pretty clear pattern of each file having a number of elements defined by a header followed by text followed by a table (generally the attributes), and possibly another set of text and a table (for the child elements). I think I can reach a reasonable solution by simply using next or next-sibling to step through items and trying to scan the text to determine if the following table is attributes or classes, but it feels a bit weird that I can't just grab everything in between two header tags and then scan that.
You can search for multiple elements at the same time, for example <h2> and <table>. You can then make a note of each <h2> contents before processing each <table>.
For example:
soup = BeautifulSoup(html, "html.parser")
for el in soup.find_all(['h2', 'table']):
if el.name == 'h2':
h2 = el.get_text(strip=True)
h2_id = el.span['id']
else:
for tr in el.find_all('tr'):
row = [td.get_text(strip=True) for td in tr.find_all('td')]
print([h2, h2_id, *row])

Select radio button using Selenium with value that changes

I need to select the radio button below with value="QMBT-0029104.xlsx;SYPFJPC2MQHC-5-688478#QHBTW"
HTML:
<form name="ViewQueryForm" method="post" action="/equery/getAttachments.do">
<div class="txt_align_left innerdvcont" id="tabmenu1" style="display:">
<div class="clear"></div>
<div class="txt_align_left innerdvcont" id="tabmenu011" name="tabmenu011">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<th width="10%" style="text-align: left"></th>
<th width="60%" style="text-align: left">Attachment </th>
<th width="30%" style="text-align: left">Date </th>
</tr>
</thead>
<tbody>
<tr>
<td align="center" valign="top">
<input type="radio" name="getAttachmentValue" id="getAttachmentValue" value="Dispute_1466718.xlsx;SYPFJPC2MQHC-5-687433#QHBTW"> </td>
<td style="padding:5px 4px">Dispute_1466718.xlsx</td>
<td style="padding:5px 4px">2021-02-16T10:34:08.617</td>
</tr>
</tbody><tbody>
<tr>
<td align="center" valign="top">
<input type="radio" name="getAttachmentValue" id="getAttachmentValue" value="QMBT-0029104.xlsx;SYPFJPC2MQHC-5-688478#QHBTW"> </td>
<td style="padding:5px 4px">QMBT-0029104.xlsx</td>
<td style="padding:5px 4px">2021-03-27T08:08:46.09</td>
</tr>
</tbody>
</table>
</div>
So far I have been able to click on it using the code below:
radiobutton2 = driver.find_element_by_xpath("//input[#value='QMBT-0029104.xlsx;SYPFJPC2MQHC-5-688478#QHBTW']");
radiobutton2.click()
However, the value changes every time which means that it's not something that I can use when running the code. Is there any way to select the second radio button by default for example.
Alternatively, I will know the QMBT-00000 reference, so is there a way to select the radio button by searching for that text?
I have tried:
radiobutton2 = driver.find_element_by_xpath('//*[contains(text(), "QMBT-0029104") and #id="getAttachmentValue"]');
radiobutton2.click()
However, that gives me an error:
Unable to locate element
Great first question.
If the radio button you need to select will always be the second option, you can select it by the index (below is in C#, but should be similar for Python):
// get all elements where id = "getAttachmentValue"
var radioButtons = driver.FindElements(By.Id("getAttachmentValue"));
// click second element
radioButtons[1].Click();
Edit (Python):
from selenium.webdriver.common.by import By
radioButtons = driver.find_elements(By.ID, 'getAttachmentValue')
radioButtons[1].click()
I added your HTML to a simple HTML file and was able to select the second radio button using the above example (using C#):
You can select the second radio button by xpath using array index notation. Instead of targeting the value attribute, use the name attribute instead. The value of the name attribute seems to be consistent:
xpath = "(//input[#type = 'radio' and #name = 'getAttachmentValue'])[2]"
radio_button = driver.find_element_by_xpath(xpath)
radio_button.click()
You probably should not use the id attribute in your locator. The Id attribute value must be unique for the entire web page, but the Id attribute value is repeated for each radio button. This is invalid HTML. It is best to avoid using attributes in locators where the values of those attributes violate the HTML spec. You never fully know how the browser is going to treat it.
For your use case, the name attribute would work best. It is perfectly valid HTML to have repeated name attribute values, and the value of the name attribute appears to be stable for each page view.
If you have only two same ids, the answer is simple. Try this.
driver.find_element_by_css_selector("#getAttachmentValue:nth-of-type(2)")
If there are more - the solution may be more complicated.
How may IDs are there?

Selenium python click icon next to a certain text

I have the following structure, where I am trying to click the second trash icon which is a button next to Test1.
<tr class=“ng-scope”>
<td class=“ng-scope”></td>
<td class=“ng-scope”></td>
<td class=“ng-scope”>
<span class=“ng-binding”>Test0</span>
</td>
<td class=“ng-scope”>
<button class=“bin btn-xs”>
<i class="glyphicon glyphicon-trash"></i>
</button>
</td>
</tr>
<tr class=“ng-scope”>
<td class=“ng-scope”></td>
<td class=“ng-scope”></td>
<td class=“ng-scope”>
<span class=“ng-binding”>Test1</span>
</td>
<td class=“ng-scope”>
<button class=“bin btn-xs”>
<i class="glyphicon glyphicon-trash"></i>
</button>
</td>
</tr>
Currently how I am implementing is by doing find_element_by_xpath where xpath is //i#class="glyphicon glyphicon-trash" and do an index searching with the given results.
This however I find quit inefficient, especially the given results can be theoretically many and I have to loop through the result list.
I tried also the following lines:
myxpath = "//*[contains(text(), 'Test1')]/following-sibling::tr/button[#class='glyphicon glyphicon-trash']"
driver.find_by_xpath(myxpath)
which does not work (because the trash icon is not actually the sibling of Test1.
How can I implement this in a better way (i.e. I want to use Test1 as anchor and click the trash button next to it and not next to Test0)?
To select the button in the row having the text:
//tr[.//text()='Test1']//button
To select the button in cell 4 in the row having the text in cell 3:
//tr[td[3]//text()='Test1']/td[4]//button
To select the cell having the text and then the button in the following cell:
//td[.//text()='Test1']/following-sibling::td[1]//button"
I'm not very clear whether you want the button or the icon...
here is for the i tag
try
//i[#class='glyphicon glyphicon-trash' and ../../../td/span/text() = "Test1"]
ps note also that:
<span class="ng-binding">Test1</span>
and
<span class="ng-binding"> Test1 </span>
are different.

Categories