gpt4 book ai didi

c# - Html Agility Pack 按类获取所有元素

转载 作者:IT王子 更新时间:2023-10-29 03:41:29 26 4
gpt4 key购买 nike

我正在尝试 html agility pack,但找不到正确的方法来解决这个问题。

例如:

var findclasses = _doc.DocumentNode.Descendants("div").Where(d => d.Attributes.Contains("class"));

但是,很明显你可以添加比 div 更多的类,所以我尝试了这个..

var allLinksWithDivAndClass = _doc.DocumentNode.SelectNodes("//*[@class=\"float\"]");

但这并不能处理您添加多个类并且“ float ”只是其中一个这样的情况..

class="className float anotherclassName"

有办法处理所有这些吗?我基本上想选择所有具有类 = 并包含 float 的节点。

**答案已记录在我的博客上,完整解释位于:Html Agility Pack Get All Elements by Class

最佳答案

(更新于 2018-03-17)

问题:

正如您所发现的,问题是 String.Contains不执行字边界检查,所以 Contains("float")将返回 true对于“foo float bar”(正确)和“unfloating”(不正确)。

解决方案是确保“float”(或任何您想要的类名)出现在两端的单词边界旁边。单词边界是字符串(或行)的开始(或结束)、空格、某些标点符号等。在大多数正则表达式中,这是 \b .所以你想要的正则表达式很简单:\bfloat\b .

使用 Regex 的缺点例如,如果您不使用 .Compiled,它们的运行速度可能会很慢选项 - 它们的编译速度可能很慢。所以你应该缓存正则表达式实例。如果您要查找的类名在运行时发生变化,这将更加困难。

或者,您可以通过将正则表达式实现为 C# 字符串处理函数来在不使用正则表达式的情况下按单词边界搜索字符串,注意不要导致任何新字符串或其他对象分配(例如,不使用 String.Split ).

方法一:使用正则表达式:

假设您只想查找具有单个设计时指定类名的元素:

class Program {

private static readonly Regex _classNameRegex = new Regex( @"\bfloat\b", RegexOptions.Compiled );

private static IEnumerable<HtmlNode> GetFloatElements(HtmlDocument doc) {
return doc
.Descendants()
.Where( n => n.NodeType == NodeType.Element )
.Where( e => e.Name == "div" && _classNameRegex.IsMatch( e.GetAttributeValue("class", "") ) );
}
}

如果您需要在运行时选择单个类名,那么您可以构建一个正则表达式:

private static IEnumerable<HtmlNode> GetElementsWithClass(HtmlDocument doc, String className) {

Regex regex = new Regex( "\\b" + Regex.Escape( className ) + "\\b", RegexOptions.Compiled );

return doc
.Descendants()
.Where( n => n.NodeType == NodeType.Element )
.Where( e => e.Name == "div" && regex.IsMatch( e.GetAttributeValue("class", "") ) );
}

如果你有多个类名并且你想匹配所有的类名,你可以创建一个 Regex 的数组对象并确保它们都匹配,或将它们组合成一个 Regex使用环顾四周,但结果是 in horrendously complicated expressions - 所以使用 Regex[]可能更好:

using System.Linq;

private static IEnumerable<HtmlNode> GetElementsWithClass(HtmlDocument doc, String[] classNames) {

Regex[] exprs = new Regex[ classNames.Length ];
for( Int32 i = 0; i < exprs.Length; i++ ) {
exprs[i] = new Regex( "\\b" + Regex.Escape( classNames[i] ) + "\\b", RegexOptions.Compiled );
}

return doc
.Descendants()
.Where( n => n.NodeType == NodeType.Element )
.Where( e =>
e.Name == "div" &&
exprs.All( r =>
r.IsMatch( e.GetAttributeValue("class", "") )
)
);
}

方法二:使用非正则字符串匹配:

使用自定义 C# 方法而不是正则表达式进行字符串匹配的优势是假设性能更快并减少内存使用(尽管 Regex 在某些情况下可能更快 - 总是首先分析您的代码, children !)

下面这个方法:CheapClassListContains提供了一个快速的字边界检查字符串匹配函数,可以像regex.IsMatch一样使用。 :

private static IEnumerable<HtmlNode> GetElementsWithClass(HtmlDocument doc, String className) {

return doc
.Descendants()
.Where( n => n.NodeType == NodeType.Element )
.Where( e =>
e.Name == "div" &&
CheapClassListContains(
e.GetAttributeValue("class", ""),
className,
StringComparison.Ordinal
)
);
}

/// <summary>Performs optionally-whitespace-padded string search without new string allocations.</summary>
/// <remarks>A regex might also work, but constructing a new regex every time this method is called would be expensive.</remarks>
private static Boolean CheapClassListContains(String haystack, String needle, StringComparison comparison)
{
if( String.Equals( haystack, needle, comparison ) ) return true;
Int32 idx = 0;
while( idx + needle.Length <= haystack.Length )
{
idx = haystack.IndexOf( needle, idx, comparison );
if( idx == -1 ) return false;

Int32 end = idx + needle.Length;

// Needle must be enclosed in whitespace or be at the start/end of string
Boolean validStart = idx == 0 || Char.IsWhiteSpace( haystack[idx - 1] );
Boolean validEnd = end == haystack.Length || Char.IsWhiteSpace( haystack[end] );
if( validStart && validEnd ) return true;

idx++;
}
return false;
}

方法 3:使用 CSS 选择器库:

HtmlAgilityPack 有点停滞不支持.querySelector.querySelectorAll ,但是有第三方库用它扩展 HtmlAgilityPack:即 FizzlerCssSelectors . Fizzler 和 CssSelectors 都实现了 QuerySelectorAll , 所以你可以像这样使用它:

private static IEnumerable<HtmlNode> GetDivElementsWithFloatClass(HtmlDocument doc) {

return doc.QuerySelectorAll( "div.float" );
}

使用运行时定义的类:

private static IEnumerable<HtmlNode> GetDivElementsWithClasses(HtmlDocument doc, IEnumerable<String> classNames) {

String selector = "div." + String.Join( ".", classNames );

return doc.QuerySelectorAll( selector );
}

关于c# - Html Agility Pack 按类获取所有元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13771083/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com