将 XPath 用于 select 带通配符的属性
Using XPath to select attributes with wildcards
我得到 HTML 我需要解析,我正在使用 C# 和 Html Agility Pack 库来选择节点。我的 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 敏捷包中:
//@*[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
}
}
}
}
}
使用 XPath 函数 contains()
或 starts-with()
。您需要像
这样的 XPath 表达式
//@*[contains(name(),'data-translate')]
或者也许
//@*[starts-with(name(),'data-translate')]
实际上检索 属性 节点。以上,@*
是您要查找的属性通配符。
而不是使用 name()
,而是使用 local-name()
例如:
var nodes = htmlDoc.DocumentNode.SelectNodes("//@*[starts-with(local-name(),'data-translate')]");
不同之处在于 name()
应该为您提供带有前缀的属性名称,例如 xml 中的名称空间,而 local-name()
将在您的case 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
我得到 HTML 我需要解析,我正在使用 C# 和 Html Agility Pack 库来选择节点。我的 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 敏捷包中:
//@*[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
}
}
}
}
}
使用 XPath 函数 contains()
或 starts-with()
。您需要像
//@*[contains(name(),'data-translate')]
或者也许
//@*[starts-with(name(),'data-translate')]
实际上检索 属性 节点。以上,@*
是您要查找的属性通配符。
而不是使用 name()
,而是使用 local-name()
例如:
var nodes = htmlDoc.DocumentNode.SelectNodes("//@*[starts-with(local-name(),'data-translate')]");
不同之处在于 name()
应该为您提供带有前缀的属性名称,例如 xml 中的名称空间,而 local-name()
将在您的case 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