I’m using HtmlAgilityPack in order to retrieve the following html (notice the nested table):
<table class="123">
<tr>
<table class="789">
<tr>
<td>abc</td>
</tr>
<tr>
<td>def</td>
</tr>
</table>
</tr>
<tr>
<td>info 1</td>
</tr>
<tr>
<td>info 2</td>
</tr>
<tr>
<td>info 3</td>
</tr>
</table>
Now, I’m trying to find a clever way to obtain some information from the parent table and some information from the nested table…
So far I have the following:
var parentTable = document.DocumentNode.SelectNodes("//table[#class='123']").FirstOrDefault();
var nestedTable = parentTable.SelectNodes("//table[#class='789']").FirstOrDefault();
I can now play around with the nestedTable and get what I want (abc, def)...
But when I try to get the <tr>’s from the parent table like so:
var parentTableRows = parentTable.SelectNodes(".//tr");
It seems to include (in the collection) the <tr>’s from the nested table as well...
In other words, according to the above html code, I was expecting to have a collection of 4 <tr>’s but since it includes the <tr>’s from the nested table, I’m getting a collection of 6 <tr>’s.
How can I skip the first <tr> that happens to hold the nested table so I can play around and get the information I want (info1, info2, info3)
(hope I’m making sense…)
Thanks in advance!
// is an XPATH expression that means "scan all nodes and sub nodes". That's why //tr gets all tr below the root one.
If you just do parentTable.SelectNodes("tr") (or "./tr" which is equivalent), you will select all TR below the root one.
If you want to skip the first one, then you can add an XPATH filter on element's position() (an XPATH function):
var parentTableRows = parentTable.SelectNodes("tr[position() > 1]");
Related
I am interested in displaying quick counts of email stats in my application, but I'm getting hung up on finding an efficient way to generate the counts. I am hoping to just use the eloquent relationship with some sort of "count where" statement inside of blade.
Getting the total count works as it should:
{{count($emails->mandrillemails)}}
but is something like this possible?:
{{count($emails->mandrillemails->msg_state == 'bounced')}}
{{count($emails->mandrillemails->msg_state == 'open')}}
Here is my block of code with the #if and #foreach statements:
#if(count($sentEmails) > 0)
#foreach ($sentEmails as $emails)
<tr>
<td>
#if(count($emails->mandrillemails) > 0)
<p><span class="badge"><i class="fa fa-paper-plane"></i> {{count($emails->mandrillemails)}}</span> <span class="badge"><i class="fa fa-exclamation-triangle"></i> {{count($emails->mandrillemails->msg_state == 'bounced')}}</span></p>
#endif
</td>
</tr>
When I attempt this, I get an undefined property error on "msg_state"
You can count a relationship like this:
$emails->mandrillemails()->whereMsgState('bounced')->count();
Since you're #foreaching things, you may want to eager load this data. This is a little more complex when you're doing counts on the relationship - see http://laravel.io/forum/05-03-2014-eloquent-get-count-relation and http://softonsofa.com/tweaking-eloquent-relations-how-to-get-hasmany-relation-count-efficiently/ for some possible techniques to eager-load the count value instead of the whole list of related items.
$emails->mandrillemails()->whereMsgState('bounced')->count() should work.
I'm trying to use Watir to scrape a page which has AJAX-generated tables. As each level of the table is expanded, additional nested tables are inserted and the expand link moves in one level. The number of levels is not always the same. The end result is something like this, loaded in asynchronously one layer at a time:
<table>
Data Type 1
<table>
<table>
<table>
<img src="expand.gif" />
</table>
</table>
</table>
</table>
<table>
Data Type 2
<table>
<table>
<table>
<img src="expand.gif" />
<img src="expand.gif" />
</table>
</table>
</table>
</table>
I want to interact only with the table containing "Data Type 1." The table IDs are randomly generated (this is an annoying ASP.net application), so the only thing to select on is "Data Type 1."
Thus, I have:
while browser.table(:text => /Type 1/).image(:src => "expand.gif").present?
links = browser.table(:text => /Type 1/).images(:src => "expand.gif")
links.each do |li|
li.click
end
end
This works fine to expand the first nested table (with the expand.gif directly inside the table element that contains the selector text. However, the next layer of tables does not expand--clearly, my selector isn't going into child tables.
If I leave out the table selector altogether, the code will happily expand every nested table on the page until it runs out of levels, but I only want "Data Type 1."
Any suggestions how to accomplish this? Thanks in advance.
As you see in the picture an item has subitems which are <th> and <td>. When I query item to get <td>, it returns null. Here is the code: item.SelectSingleNode("td")
Shouldn't it get td node?
(http://i.stack.imgur.com/EXu7W.png)
It seems that <td> isn't direct child of current item. To select descendant that isn't direct child you can use double slashes (//) :
item.SelectSingleNode(".//td")
And if I see it correctly, <td> is child of <th>, so you can also do this way :
item.SelectSingleNode("th/td")
I've faced with a problem how to find first level children from the current element ?
For example i have html :
<table>
<tr>abc</tr>
<tr>def</tr>
<table>
<tr>second</tr>
</table>
</table>
I am using Nokogiri for rails :
table = page.css('table')
table.css('tr')
It returns all tr inside table.
But I need only 2 that first level for the table.
When you say this:
table = page.css('table')
you're grabbing both tables rather than just the top level table. So you can either go back to the document root and use a selector that only matches the rows in the first table as mosch says or you can fix table to be only the outer table with something like this:
table = page.css('table').first
trs = table.xpath('./tr')
or even this (depending on the HTML's real structure):
table = page.xpath('/html/body/table')
trs = table.xpath('./tr')
or perhaps one of these for table (thanks Phrogz, again):
table = page.at('table')
table = page.at_css('table')
# or various other CSS and XPath incantations
You can do
rows = page.css('body > table > tr')
Perhaps you have to adapt the selector to your container element (i chose 'body' here)
As yet another way, you can try to use something like this:
text = <<HERE
<table>
<tr>abc</tr>
<tr>def</tr>
<table>
<tr>second</tr>
</table>
</table>
HERE
xml = Nokogiri::XML(text)
xml.xpath("/table/tr/").each do |node|
puts node.text
end
In this example, '/table/tr' expression represents an absolute path to the required element - 'tr' in our case.
xpath did not work for me
The below code worked fine for me.
table = page.css('table')
table.css('> tr')
I got a table with <tr> data as:
<tr class="iceDatTblRow1" id="body-subview:myMainPage:MainTabs:0:dataTable:0">
<td class="iceDatTblCol1"><span class="iceOutTxt" id="body-
subview:myMainPage:MainTabs:0:dataTable:0:j_idt252">Data that I want</span>
</td>
</tr>
for some reason, i can't seem to locate the <td> with id attribute within it...
please share your expertise - thanks,
I don't know about Selenium but there are many XPath solutions here. A few examples:
span everywhere:
//span[#id='body-subview:myMainPage:MainTabs:0:dataTable:0:j_idt252']
span only inside td:
//td/span[#id='body-subview:myMainPage:MainTabs:0:dataTable:0:j_idt252']
If your requirement is to get all the first column for all rows with Selenium and Xpath then you can try this code:
String tdLocator="//tr"
int count = selenium.getXpathCount(tdLocator)
for(int i = 1;i<=count;i++)
{
get to td by using //tr[i]/td[0]
}
If you have a table ID then you can also use
selenium.getTable("table_id.[rownumber].[colnumber])