Html Agility Pack,从一个节点选择节点。

19

为什么这条代码会选中我文档中所有的 <li> 元素?

HtmlWeb web = new HtmlWeb();
HtmlDocument doc = web.Load(url);

var travelList = new List<Page>();
var liOfTravels = doc.DocumentNode.SelectSingleNode("//div[@id='myTrips']")
                     .SelectNodes("//li");

我想要的是获取所有<div>元素中,id为"myTrips"的<li>元素。

5个回答

17

这有点令人困惑,因为您期望它会在只针对ID为“myTrips”的div元素执行selectNodes操作,但是如果您执行另一个SelectNodes("//li"),它将从文档顶部执行另一个搜索。

我通过将语句合并为一条来解决此问题,但这仅适用于具有id为“ mytrips”的一个div的网页。查询将如下所示:

doc.DocumentNode.SelectNodes("//div[@id='myTrips'] //li");


17
var liOfTravels = doc.DocumentNode.SelectSingleNode("//div[@id='myTrips']")
                 .SelectNodes(".//li");

注意第二行的圆点。就这一点而言,HTMLAgilityPack完全依赖于XPath语法,然而结果是不直观的,因为这些查询实际上是相同的:

doc.DocumentNode.SelectNodes("//li");
some_deeper_node.SelectNodes("//li");

我认为这些查询不一样。实际上,当他执行第一个选择“//div[@id='myTrips']”时,当前节点会更改。这就是为什么第二个选择应该是“.//li”(从当前节点的任何位置),而不是“//li”(从根的任何位置)。Agility正好做了预期的事情。 - derloopkat
@derloopkat,它们是相同的(这里没有IMHO;如果它们不同,你可以在解决方案查询中省略点,但你不能,对吧?)。不幸的是,HTMLAgilityPack无论你在哪个节点,都会从根节点开始搜索。IMHO部分是这样的-通常关注给定节点的重点是你继续从该节点搜索,而不是再次从根节点开始。在第二个子查询中添加点的解决方案查询根本没有意义,因此问题是为什么要支持它们? - greenoldman
我们谈论的是不同的事情。当我说查询不相同时,我指的是"//li"和".//li"。而你所提到的“那些查询”是下面的查询。 - derloopkat
在xpath语法中,".//li"点符号表示相对于当前级别而不是相对于根级别。所以这对我来说完全直观。但你应该删除你的其他评论,因为它会让问题变得混乱。 - Jroonk

7

在某些情况下,创建一个新的节点可以非常有益,使您更加直观地使用xpaths。我在一些场合发现这很有用。

var myTripsDiv = doc.DocumentNode.SelectSingleNode("//div[@id='myTrips']");
var myTripsNode = HtmlNode.CreateNode(myTripsDiv.InnerHtml);
var liOfTravels = myTripsNode.SelectNodes("//li");

4
您可以使用Linq查询来完成此操作:
HtmlWeb web = new HtmlWeb();
HtmlDocument doc = web.Load(url);

var travelList = new List<HtmlNode>();
foreach (var matchingDiv in doc.DocumentNode.DescendantNodes().Where(n=>n.Name == "div" && n.Id == "myTrips"))
{
    travelList.AddRange(matchingDiv.DescendantNodes().Where(n=> n.Name == "li"));
}

我希望你能够得到帮助


2
这对我来说也很反直觉,如果你在特定节点上运行selectNodes方法,我认为它只会搜索该节点下的内容,而不是整个文档。
无论如何,如果您更改此行:
var liOfTravels = 
doc.DocumentNode.SelectSingleNode("//div[@id='myTrips']").SelectNodes("//li");

TO:

var liOfTravels = 
doc.DocumentNode.SelectSingleNode("//div[@id='myTrips']").SelectNodes("li");

我认为你会没问题的,我刚遇到同样的问题,这个解决方法对我有效。但是我不确定li是否必须是你所拥有节点的直接子元素。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接