【问题标题】:Using XPath to select attributes with wildcards使用 XPath 选择带有通配符的属性
【发布时间】:2015-06-30 09:23:09
【问题描述】:

我得到了需要解析的 HTML,我正在使用 C# 和 Html Agility Pack Library 来选择节点。我的 html 看起来像这样:

<input data-translate-atrr-placeholder="FORGOT_PASSWORD.FORM.EMAIL">

或:

<h1 data-translate="FORGOT_PASSWORD.FORM.EMAIL"></h1>

data-translate-attr-**** 是我需要找到的新属性模式

我可以使用这样的东西:

//[contains(@??,'data-translate-attr')]

但不幸的是,这只会在属性内搜索值。 如何使用通配符查找属性本身?

更新:@Mathias Muller

HtmlAgilityPack.HtmlDocument htmlDoc    
// this is the old code (returns nodes)
var nodes = htmlDoc.DocumentNode.SelectNodes("//@data-translate");  
// these suggestions return no nodes using the same data
var nodes = htmlDoc.DocumentNode.SelectNodes("//@*[contains(name(),'data-translate')]");  
var nodes = htmlDoc.DocumentNode.SelectNodes("//@*[starts-with(name(),'data-translate')]");

更新 2

这似乎是一个 Html Agility Pack 问题而不是 XPath 问题,我使用 chrome 来测试我的 XPath 表达式,并且以下所有内容都在 chrome 中工作,但在 Html Agility Pack 中没有:

//@*[contains(local-name(),'data-translate')]
//@*[starts-with(name(),'data-translate')]
//attribute::*[starts-with(local-name(.),'data-translate')]

我的解决方案

我最终只是按照老式的方式做事......

var nodes = htmlDoc.DocumentNode.SelectNodes("//@*");

if (nodes != null) {
    foreach (HtmlNode node in nodes) {
        if (node.HasAttributes) {
            foreach (HtmlAttribute attr in node.Attributes) {
                if (attr.Name.StartsWith("data-translate")) {
                    // code in here to handle translation node
                }
            }
        }
    }
}

【问题讨论】:

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


    【解决方案1】:

    使用 XPath 函数 contains()starts-with()。您需要一个 XPath 表达式,如

    //@*[contains(name(),'data-translate')]
    

    或许

    //@*[starts-with(name(),'data-translate')]
    

    实际上检索 attribute 节点。以上,@* 是您要查找的属性通配符。

    【讨论】:

    • 感谢您的回答,我已经更新了问题,将我拥有的内容与您的建议进行了比较。我犯了一些愚蠢的错误吗?
    • @Nnoel 这是不可能的,除非在您的实际输入文档中有命名空间属性。否则,//@data-translate 不可能返回节点,而 //@*[contains(name(),'data-translate')] 则不会。到目前为止,您还没有提供任何重要信息吗?您为解决问题做了哪些简化?
    • 将您的答案标记为正确,因为您的 XPath 在使用 chrome 测试时是正确的。
    • @Nnoel 它不起作用的原因是HAP does not support attribute selection - 不幸的是。
    • 奇怪,我以前用它来选择属性,只是“高级”通配符选择失败了。无论如何,感谢您的帮助,在发现 Html Agility Pack 是问题后,我尝试了 C# 的 xml 例程,但它们不适用于 html,所以回到 Html Agility Pack。发布了我的解决方案..
    【解决方案2】:

    不要使用name(),而是使用local-name()如:

    var nodes = htmlDoc.DocumentNode.SelectNodes("//@*[starts-with(local-name(),'data-translate')]");
    

    不同之处在于name() 应该为您提供带有前缀的属性名称,例如 xml 中的命名空间,如果存在,local-name() 将发出该前缀,在您的情况下,name()local-name() 应该可以工作同样的方式,因为它的 html 并且没有命名空间,但似乎它们没有,这可能是一个错误。

    测试:

        var html = "<h3 x='foo'></h3>";
        var doc = new HtmlAgilityPack.HtmlDocument();
        doc.LoadHtml(html);
        var ElementByName = doc.DocumentNode.SelectSingleNode("//*[name()='h3']");                //Works
        var ElementByLocalName = doc.DocumentNode.SelectSingleNode("//*[local-name()='h3']");     //Works
        var ElementByAttributeLocalName = doc.DocumentNode.SelectSingleNode("//*[@*[local-name()='x']]"); //Works
        var ElementByAttributeName = doc.DocumentNode.SelectSingleNode("//*[@*[name()='x']]");  //Does NOT
    
        //Mathias Way
        var ElementByAttributeLocalName_ = doc.DocumentNode.SelectSingleNode("//@*[local-name() = 'x']"); //Works
        var ElementByAttributeName_ = doc.DocumentNode.SelectSingleNode("//@*[name() = 'x']");  //Does NOT
    

    【讨论】:

    • 感谢您的回答,我测试了 'local-name()',在这种情况下它和 'name()' 一样有效。
    • @Nnoel in HtmlAgilityPack only local-name() 可以工作,name() 在 html 的情况下应该以相同的方式工作,但有一个错误。
    • 您怎么知道这是 AgilityPack 中的错误?有任何参考支持吗?
    • @MathiasMüller 因为我知道问题name() 在 HAP 中永远无法使用我总是使用 local-name()
    • 我删除了我的评论,因为我读到属性选择在 HAP 中是不可能的,这是真的吗?另外,我发现了一个支持你的说法的帖子:htmlagilitypack.codeplex.com/workitem/35920
    猜你喜欢
    • 1970-01-01
    • 2013-04-10
    • 1970-01-01
    • 2015-12-08
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 2014-12-09
    相关资源
    最近更新 更多