How do I find the value of X cells after a matched contains with xpath and lxml - html

I have a document with multiple rows that has a value in the 4th TD element that I can't figure out how to retrieve. There is nothing unique in the tags so I have to match based on the word TOTAL, and then get the value I need from the 4th TD in the existing row. This is one TR for illustration:
<TR>
<TD ALIGN="right" COLSPAN="30" bgcolor=d8caca><div class=small4>SECTION TOTAL</div></TD>
<TD ALIGN="right" COLSPAN="8" bgcolor=d8caca> </TD>
<TD ALIGN="right" COLSPAN="13" bgcolor=gold><div class=small4> 11.907531</div>
</TD>
<TD ALIGN="right" COLSPAN="13" bgcolor=gold><div class=small4> $773.10</div></TD>
</TR>
I want to match on the word "TOTAL" and then get the value exactly three cells later, or in this case, $773.10.
This successfully gathers each of the "TOTAL" text in an array without issue:
titles = tree.xpath("//tr/td[contains(., 'TOTAL')]//text()")
However, I am unable to get the values in the last element. I've tried numerous variations of the following searching for the TOTAL and then trying to use following or following-sibling to no avail:
totals = tree.xpath("//tr/td[contains(., 'TOTAL')]/../following::td[4]/div/text()")
...but I either get an array of the non-breakable space from the immediate next TD after the TOTAL, no data at all, or "element" references that when expanded to text are null. How do I properly get the value inside td[4] in the existing TR after the contains is matched?
I am trying to get every occurence, not just one, so that the titles and totals arrays are a 1:1 match. If there is a way to do a key=>value pairing that'd be even better.

You can use following-sibling axis to get td located after td that contain text "TOTAL" in the same parent, and then filter the result further to get only the last of such td using predicate [last()], then return the child div/text() :
query = "//tr/td[contains(., 'TOTAL')]/following-sibling::td[last()]/div/text()"
titles = tree.xpath(query)
xpathtester demo: http://www.xpathtester.com/xpath/5cf0aa473d030da66de1bec73bcb8795

Related

how to iterate a collection in a html table with limited (n) columns in a row using thymeleaf

I am new to front end and using thymeleaf. I am looking for an approach to build a dynamic form. I am sending a map from controller to front end (html), I am able to display the values of my map using below code with thymeleaf attributes
<tr class="row" th:each="element : ${elementMap}">
<td th:text="${element.key}"></td>
<td>
<input type="text" id=${element.keyth:name=${element.key} th:value="${element.value}" /><br>
</td>
</tr>
I need to display 4 columns in a row. Or If converting above Map to ArrayList is it possible to allow 4 columns in a row. But when I use th:each && th:text for iterating content of Arraylist how to mention 4 columns in a row (Any css style would be available)? Is there any other approach available to fix this ?
on your page you are iterating one line for each element
the columns are the td tags, which according to the code you informed are two columns per line (key and value).
but if you want to limit the number of columns, you can use css, but by the structure of your code, you have dynamic lines (1 line for each element of the list) and you only have two columns per line.
if the problem is too many columns, you can only put up to 4 td tags

To create xpath for html table to access desired record using 3 inputs

This is a structure of very basic html table. I want to create xpath for following scenario.
I will insert 2 field names like 'Name' and 'Salary' along with value of 'Name' field (say for example 'STU') then output should be 25k.
I was given hint like
string (xpath which will have 2 field names and one value) output of this function will be that key.
<html>
<body>
<h3>MY TABLE</h3>
<table>
<tbody>
<tr>
<th>id</th>
<th>Name</th>
<th>date</th>
<th>Address</th>
<th>salary</th>
</tr>
<tr>
<td>XYZ</td>
<td>STU</td>
<td>12/20/2015</td>
<td>Mumbai</td>
<td>25k</td>
</tr>
<tr><td>PQR</td>
<td>ABC</td>
<td>01/05/2015</td>
<td>Chennai</td>
<td>25k</td>
</tr>
<tr>
<td>ABC</td>
<td>PQR</td>
<td>03/09/2015</td>
<td>Bangalore</td>
<td>20k</td>
</tr>
<tr>
<td>emp4</td>
<td>XYZ</td>
<td>08/12/2015</td>
<td>Delhi</td>
<td>30k</td>
</tr>
</tbody>
</table>
</body>
</html>
Basics:
The first thing you will need to do is find which column(s) in the table contain(s) the field(s) you are searching for.
For example, to find the position of the Name column:
count(//tbody/tr/th[./text() = 'Name']/preceding-sibling::th) + 1
The above will return 2. (Of course, this technique won't necessarily work well if there are colspan attributes used in the HTML, but there are none in your example.)
Then, you can find the rows that contain a specific value in this column - for example STU - in this column like this:
//tbody/tr/td[position() = $WherePosition][./text() = 'STU']/..
Notice the use of the variable $WherePosition above. Here, I have used it to represent the result of the previous query (1). Depending on what tool you are using to perform the XPath query, you may or may not have the option to set variables. If you wish to use a single XPath expression or you don't want to or can't use variables, you can simply replace it with the previous XPath expression, although it will become less readable.
It's worth noting that aside from readability, variables will also make it easier to re-use the same expression, because you can, for example, substitute 'Name' in the first query for $WhereField, if you tell the XPath evaluator that you want to set the $WhereField variable to Name. And in the query to find the specific row (2), you can substitute 'STU' for $WhereValue, if you also set this variable accordingly.
Apply it:
Now to get the position of the salary column. If you used my tip above about using variables, you could re-execute the same expression as (1) again, but with the $WhereField variable set to salary instead of Name. i.e. count(//tbody/tr/th[./text() = $WhereField]/preceding-sibling::th) + 1
And if you stored the result of query (2) as a variable called $matching_rows and the salary column position as $SalaryPosition, then to return the salary for the row where Name = STU, you could simply finish with: $matching_rows/td[position() = $SalaryPosition]/text(), and you would get the answer 25k.
TL;DR
In summary, as an XPath 1.0 one-liner to get the salary for the row with Name = STU:
//tbody/tr/td[position() = count(//tbody/tr/th[./text() = 'Name']/preceding-sibling::th) + 1][./text() = 'STU']/../td[position() = count(//tbody/tr/th[./text() = 'salary']/preceding-sibling::th) + 1]/text()

Using XPath to find a dynamic table value

In this table with dynamically changing prices, I wish to always select the link belonging to the first price over $400,00 in XPath 1.0. The correct solution might not always be in the second row, so tr[2]/td[1] will not always be the correct result.
<table>
<tr>
<td>1</td>
<td>-$200,00</td>
</tr>
<tr>
<td>2</td>
<td>$500,00</td>
</tr>
<tr>
<td>3</td>
<td>$100,00</td>
</tr>
</table>
My (non-working) XPath that comes closest so far is:
//tr/td[2][starts-with(.,'$')]/(number(substring-after(.,'$')))>400.00/preceding-sibling::td
It selects the prices by using td[2]
It only selects the prices that have positive values (I don't need the negative values anyway)
It removes the $ signs from the remaining prices
Here is the problem:
I'm trying to use number to convert the strings into numbers so I can compare them to 400 (which is not working)
After that I try to select the corresponding link after the right price has been found.
Any help would be really appreciated (I'm just a simple tester getting dragged into the magical world of test automation) :)
I wish to always select the link belonging to the first price over $400,00 in XPath 1.0.
That would be
//td[number(translate(normalize-space(), ',$', '.')) > 400]/..//a
Note that I translate , to . and $ to nothing**, so if your numbers are formatted any further (digit grouping, for example) this might not be 100% correct yet. Know your data, make the appropriate changes.
For the fun of it (and to demonstrate XPath's flexibility), here's a different path with the same result:
//a[../..//td[number(translate(normalize-space(), ',$', '.')) > 400]]
** translate() replaces one list of characters with another:
translate('abc', 'ac', 'AC') returns 'AbC'
translate('abc', 'ac', 'A') returns 'Ab'.

how to align checkboxes to the left of a table in html

I have a table representing data in a database and I'd like there to be checkboxes to the left so that the user can do operations on the selected items (ie delete, modify). My question is, how can I align the checkboxes to be to the left of the table?
<table border="2">
<tr>
<th>A bunch of headers</th>
</tr>
<tr>
<td>A row of items</td>
</tr>
A lot more rows
</table>
Pretty straitforward, I have no idea how to proceed with checkboxes, I tried to put a form around the table but that didn't work.
You can add a column that contains checkboxes only. Using td elements, they will be left-aligned by default.
However, checkboxes should be used to provide a control for selecting or not selecting some parameters, not for triggering actions. Consider using e.g. button elements for actions.
Use CSS to align them.
margin-left:100px;
float:left;
You could just create an "actions" column which you insert as the first column in your table that contains the checkboxes. That way all checkboxes are kept separate from the contents which makes formatting and manipulation via javascript easier.
Alternatively if you must use a form for each row you could do something like:
<td><input type="checkbox" /><span>Data...</span></td>
... but if you go the second route you loose the power of tables** & may as well go for a div only solution.
** for displaying tabular data; before someone shoots me down for table vs div for layouts.

Select a row based on the contents of a cell with xpath

I have a table that consists of multiple rows that each contain 5 cells, like this:
<tr>
<td></td>
<td>123456</td>
<td>statusText</td>
<td><a>linkText</a></td>
<td>editButton</td>
</tr>
The 123456 could be any string of random letters and numbers. I want to be able to select a link based on the contents of the second cell in the table. I've been trying something like this:
//tr[contains(td, '123456')]
to get me to the cell, but it either returns every row or nothing, depending on how I tweak the xpath.
I've been trying something like this:
//tr[contains(td, '123456')]
to get me to the cell, but it either
returns every row or nothing,
depending on how I tweak the xpath
You get what you asked for. The above XPath expression selects any tr element (row) in the document that has (at least one) td child whose string value contains '123456'.
But you want:
//tr/td[text() = '123456']
this selects every td element (cell) in the document, that has a text node child, whose string value is '123456'.
There can be different variations, depending on whether a td may have more than one text nodes and on whether the white space in a text node should be normalized, but the question doesn't provide any information if any of these apply in this particular case.
I'd research something like //tr[string(td[2]) = '123456']. If this does not work, I'd look up XPath axes.