how can i get following-sibling using HtmlAgilityPack? - c#

i have many tr tags in html code:
<div class="noticeTabBoxWrapper">
<tr>
<td>
<span>Text for anchor</span>
</td>
</tr>
<tr>
<td>
<span>*constantly changing text*</span>
</td>
</tr>
In my code i write this:
//div[#class = 'noticeTabBoxWrapper']//span[contains(text(), 'Text for anchor')]").InnerText
How can I rewrite code so that I can extract the necessary text that follows immediately after the anchor?
Thanks.

Assuming raw is your sample data :
var doc = new HtmlDocument();
doc.LoadHtml(raw);
var xpath = "//div[#class='noticeTabBoxWrapper']//span[contains(., 'Text for anchor')]/following::td[1]/span";
var result = doc.DocumentNode.SelectSingleNode(xpath);
Console.WriteLine(result.InnerText)
Output : *constantly changing text*

Related

How to find last column of a table using Html Agility Pack

I have a table like this:
<table border="0" cellpadding="0" cellspacing="0" id="table2">
<tr>
<th>Name
</th>
<th>Age
</th>
</tr>
<tr>
<td>Mario
</td>
<th>Age: 78
</td>
</tr>
<tr>
<td>Jane
</td>
<td>Age: 67
</td>
</tr>
<tr>
<td>James
</td>
<th>Age: 92
</td>
</tr>
</table>
I want to get the last td from all rows using Html Agility Pack.
Here is my C# code so far:
await page.GoToAsync(NumOfSaleItems, new NavigationOptions
{
WaitUntil = new WaitUntilNavigation[] { WaitUntilNavigation.DOMContentLoaded }
});
var html4 = page.GetContentAsync().GetAwaiter().GetResult();
var htmlDoc4 = new HtmlDocument();
htmlDoc4.LoadHtml(html4);
var SelectTable = htmlDoc4.DocumentNode.SelectNodes("/html/body/div[2]/div/div/div/table[2]/tbody/tr/td[1]/div[3]/div[2]/div/table[2]/tbody/tr/td[4]");
if (SelectTable.Count == 0)
{
continue;
}
else
{
foreach (HtmlNode row in SelectTable)//
{
string value = row.InnerText;
value = value.ToString();
var firstSpaceIndex = value.IndexOf(" ");
var firstString = value.Substring(0, firstSpaceIndex);
LastSellingDates.Add(firstString);
}
}
How can I get only the last column of the table?
I think the XPath you want is: //table[#id='table2']//tr/td[last()].
//table[#id='table2'] finds the table by ID anywhere in the document. This is preferable to a long brittle path from the root, since a table ID is less likely to change than the rest of the HTML structure.
//tr gets the descendent rows in the table. I'm using two slashes in case there might be an intervening <tbody> element in the actual HTML.
/td[last()] gets the last <td> in each row.
From there you just need to select the InnerText of each <td>.
var tds = htmlDoc.DocumentNode.SelectNodes("//table[#id='table2']//tr/td[last()]");
var values = tds?.Select(td => td.InnerText).ToList() ?? new List<string>();
Working demo here: https://dotnetfiddle.net/7I8yk1

Selecting all textnodes in table with XPath

This is a a page from an open databse about food:
http://www.dabas.com/ProductSheet/Details.ashx/121308
Im trying to get some info from this page using XPath.
The table I'm interested in is the one called: Näringsvärde.
I want to get all the textnodes inside "Näringsvärde" saved into a string.
This is the relevant portion of the code linked above:
<!DOCTYPE html>
<html>
...
<body>
...
<table class="width100" style="page-break-inside: avoid">
<caption>
Produktinformation
<img src="../../images/ProductSheet/draw-triangle3.png" id="toggleProduktinformation"
class="imgCaptionOn" />
</caption>
<tbody id="tbodyProduktinformation">
<tr>
<td class="col1">
Ursprungsland:
</td>
<td>
Sverige </td>
</tr>
...
</tbody>
</table>
<table id="tableHover" class="width100 marginTop30 bgTable">
<tr class="nohover">
<td class="tdLeft48 padding0">
<table id="nutritiveTabel" class="leftTable" style="page-break-inside: avoid">
<caption>
Näringsvärde
<img src="../../images/ProductSheet/draw-triangle3.png" id="toggleNutritiveValues"
class="imgCaptionOn" />
</caption>
<tbody id="tbodyNutritiveValues">
<tr id="divNutritiveValues">
<td class="padding">
<table class="noBorder width100">
<tr>
<td class="col1">
Tillagningsstatus:
</td>
<td>Tillagad</td>
<td colspan="2">
&amp;nbsp;
</td>
</tr>
...
</table>
</td>
</tr>
</tbody>
</table>
</td>
...
</html>
I tried using something like this so far, but it didn't work:
public List<string> GetNaring(string xid) {
HtmlWeb web = new HtmlWeb();
HtmlDocument doc = web.Load(xid);
var xpath = "/html/body/div/div[2]/div[2]/table[2]/tbody/tr/td/table/tbody";
var links = doc.DocumentNode.SelectNodes(xpath);
return links.Select(n => n.InnerText).ToList();
}
But this only gives back null, what am I missing?
The XPath expression:
/html/body/div/div[2]/div[2]/table[2]/tbody/tr/td/table/tbody
does not match any nodes.
Since you have an unique string you can match, you should use it. Searching for that string in the source code, you will find:
...
<td class="tdLeft48 padding0">
<table id="nutritiveTabel" class="leftTable" style="page-break-inside: avoid">
<caption>
Näringsvärde
<img src="../../images/ProductSheet/draw-triangle3.png" id="toggleNutritiveValues"
class="imgCaptionOn" />
</caption>
<tbody id="tbodyNutritiveValues">
<tr id="divNutritiveValues">
...
The string is a child of the caption element inside the table you want. You have to get the string value of that element, trim the extra spaces and use the result to compare to "Näringsvärde". You can select the correct table using this expression:
//table[normalize-space(caption/text())='Näringsvärde']
Once you have the correct table, you can navigate inside it and select the nodes you want, or you can get the string-value which is a concatenation of all the descendant text nodes:
//table[normalize-space(caption/text())='Näringsvärde']//td
This will return all td nodes, which is where the text is.

How to select nodes by attribute that starts with... in C#

I have this xml document and I want to select nodes by attribute that starts with '/employees/'.
<table>
<tr>
<td>
Employee 1
</td>
<td>Robert</td>
</tr>
<tr>
<td>
Employee 2
</td>
<td>Jennifer</td>
</tr>
</table>
So in C#, I would do something like this:
parentNode.SelectNodes("//table/tr/th/a[#href='/employees/.....']")
Is this possible with C#?
Thanks!
The simple starts-with function does what you need:
parentNode.SelectNodes("//table/tr/td/a[starts-with(#href, '/employees/')]")
using pure LINQ you can do something like this
var doc = XDocument.Parse("YOUR_XML_STRING");
var anchors = from e in doc. Descendants("a") where e.Attribute("href").Value.StartsWith("/employee/") select e;
// now you can seelect any node by doing a combination of .Parent.Parent.....
So, something like this?
var xml = #"<table>
<tr>
<td>
Employee 1
</td>
<td>Robert</td>
</tr>
<tr>
<td>
Employee 2
</td>
<td>Jennifer</td>
</tr>
</table>";
var doc = new XmlDocument();
doc.LoadXml(xml);
var employees = doc.SelectNodes("/table/tr/td/a[starts-with(#href, '/employees/')]");
DoWhatever(employees);
Sure, you can load your XML into the XDocument instance and use XPathSelectElements method to search using your expression.

How to get a link's title and href value separately with html agility pack?

Im trying to download a page contain a table like this
<table id="content-table">
<tbody>
<tr>
<th id="name">Name</th>
<th id="link">link</th>
</tr>
<tr class="tt_row">
<td class="ttr_name">
<a title="name_of_the_movie" href="#"><b>name_of_the_movie</b></a>
<br>
<span class="pre">message</span>
</td>
<td class="td_dl">
<img alt="Download" src="#">
</td>
</tr>
<tr class="tt_row"> .... </tr>
<tr class="tt_row"> .... </tr>
</tbody>
</table>
i want to extract the name_of_the_movie from td class="ttr_name" and download link from td class="td_dl"
this is the code i used to loop through table rows
HtmlAgilityPack.HtmlDocument hDocument = new HtmlAgilityPack.HtmlDocument();
hDocument.LoadHtml(htmlSource);
HtmlNode table = hDocument.DocumentNode.SelectSingleNode("//table");
foreach (var row in table.SelectNodes("//tr"))
{
HtmlNode nameNode = row.SelectSingleNode("td[0]");
HtmlNode linkNode = row.SelectSingleNode("td[1]");
}
currently i have no idea how to check the nameNode and linkNode and extract data inside it
any help would be appreciated
Regards
I can't test it right now, but it should be something among the lines of :
string name= namenode.Element("a").Element("b").InnerText;
string url= linknode.Element("a").GetAttributeValue("href","unknown");
nameNode.Attributes["title"]
linkNode.Attributes["href"]
presuming you are getting the correct Nodes.
public const string UrlExtractor = #"(?: href\s*=)(?:[\s""']*)(?!#|mailto|location.|javascript|.*css|.*this\.)(?<url>.*?)(?:[\s>""'])";
public static Match GetMatchRegEx(string text)
{
return new Regex(UrlExtractor, RegexOptions.IgnoreCase).Match(text);
}
Here is how you can extract all Href Url. I'm using that regex in one of my projects, you can modify it to match your needs and rewrite it to match title as well. I guess it is more convenient to match them in bulk

How to extract text from HTML using htmlagilitypack for this sample?

I wanna extract the text from a HTML source. I'm trying with c# and htmlagilitypack dll.
The source is:
<table>
<tr>
<td class="title">
<a onclick="func1">Here 2</a>
</td>
<td class="arrow">
<img src="src1" width="9" height="8" alt="Down">
</td>
<td class="percent">
<span>39%</span>
</td>
<td class="title">
<a onclick="func2">Here 1</a>
</td>
<td class="arrow">
<img src="func3" width="9" height="8" alt="Up">
</td>
<td class="percent">
<span>263%</span>
</td>
</tr>
</table>
How can I get the text Here 1 and Here 2 from the table?
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml("web page string");
var xyz = from x in htmlDoc.DocumentNode.DescendantNodes()
where x.Name == "td" && x.Attributes.Contains("class")
where x.Attributes["class"].Value == "title"
select x.InnerText;
not so pretty but should work
Xpath version
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(t);
//this simply works because InnerText is iterative for all child nodes
HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//td[#class='title']");
//but to be more accurate you can use the next line instead
//HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//td[#class='title']/a");
string result;
foreach (HtmlNode item in nodes)
result += item.InnerText;
and for the LINQ version just change the var Nodes = .. line with:
var Nodes = from x in htmlDoc.DocumentNode.DescendantNodes()
where x.Name == "td" && x.Attributes["class"].Value == "title"
select x.InnerText;

Categories