无法使用HtmlAgilityPack和XPath

时间:2015-08-05 10:59:01

标签: c# xpath rss html-agility-pack

我正在使用Html Agility包从rss xml中选择文本数据。对于每个其他节点类型(title,pubdate,guid .etc),我可以使用XPath约定选择内部文本,但是当查询“// link”或确实“item / link”时,返回空字符串。

public static IEnumerable<string> ExtractAllLinks(string rssSource)
{
    //Create a new document.
    var document = new HtmlDocument();
    //Populate the document with an rss file.
    document.LoadHtml(rssSource);
    //Select out all of the required nodes.
    var itemNodes = document.DocumentNode.SelectNodes("item/link");
    //If zero nodes were found, return an empty list, otherwise return the content of those nodes.
    return itemNodes == null ? new List<string>() : itemNodes.Select(itemNode => itemNode.InnerText).ToList();
}

是否有人理解为什么这个元素与其他元素的行为不同?

附加:运行“item / link”返回零节点。运行“// link”会返回正确数量的节点,但内部文本的长度为零个字符。

使用下面的测试数据,“// name”返回“fred”的单个记录,但是使用“// link”返回带有空字符串的单个记录。

<site><link>Hello World</link><name>Fred</name></site>

我确信它是因为世界的“链接”。如果我将其更改为“linkz”,则效果非常好。

以下解决方法完美无缺。但是,我想了解为什么搜索“// link”不像其他元素那样工作。

public static IEnumerable<string> ExtractAllLinks(string rssSource)
{
    rssSource = rssSource.Replace("<link>", "<link-renamed>");
    rssSource = rssSource.Replace("</link>", "</link-renamed>");
    //Create a new document.
    var document = new HtmlDocument();
    //Populate the document with an rss file.
    document.LoadHtml(rssSource);
    //Select out all of the required nodes.
    var itemNodes = document.DocumentNode.SelectNodes("//link-renamed");
    //If zero nodes were found, return an empty list, otherwise return the content of those nodes.
    return itemNodes == null ? new List<string>() : itemNodes.Select(itemNode => itemNode.InnerText).ToList();
}

1 个答案:

答案 0 :(得分:4)

如果您打印DocumentNode.OuterHtml,您会看到问题:

var html = @"<site><link>Hello World</link><name>Fred</name></site>";
var doc = new HtmlDocument();
doc.LoadHtml(html);
Console.WriteLine(doc.DocumentNode.OuterHtml);

输出

<site><link>Hello World<name>Fred</name></site>

link恰好是某些特殊标记 * 之一,被HAP视为自动关闭标记。您可以在解析HTML之前设置ElementsFlags来改变此行为,例如:

var html = @"<site><link>Hello World</link><name>Fred</name></site>";
HtmlNode.ElementsFlags.Remove("link");  //remove link from list of special tags
var doc = new HtmlDocument();
doc.LoadHtml(html);
Console.WriteLine(doc.DocumentNode.OuterHtml);
var links = doc.DocumentNode.SelectNodes("//link");
foreach (HtmlNode link in links)
{
    Console.WriteLine(link.InnerText);
}

<强> Dotnetfiddle Demo

输出

<site><link>Hello World</link><name>Fred</name></site>
Hello World

*)除了link之外的特殊标记的完整列表,默认情况下包含在ElementsFlags字典中,可以在{{3}的源代码中看到}。其中一些最受欢迎的是<meta><img><frame><input><form><option>等。