Html Agility Pack按類獲取所有元素

c# html html-agility-pack

我正在攻擊html敏捷包並且無法找到正確的方法來解決這個問題。

例如:

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

但是,顯然你可以添加更多的類然後div,所以我嘗試了這個..

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

但是這並沒有處理你添加多個類的情況,而“float”只是其中之一。

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

有沒有辦法處理所有這些?我基本上想要選擇所有具有class =且包含float的節點。

**答案已記錄在我的博客上,並附有完整說明: Html Agility Pack按類獲取所有元素

一般承認的答案

(2018-03-17更新)

問題:

正如您所發現的那樣,問題是String.Contains不執行字邊界檢查,因此Contains("float")將為“foo float bar”(正確)和“unfloating”返回true (這是不正確的)。

解決方案是確保“浮動”(或任何您想要的類名稱)出現在兩端的字邊界旁邊 。字邊界是字符串(或行)的開頭(或結尾),空格,某些標點符號等。在大多數正則表達式中,這是\b 。所以你想要的正則表達式只是: \bfloat\b

使用Regex實例的一個缺點是,如果不使用.Compiled選項,它們可能運行緩慢 - 而且編譯速度很慢。所以你應該緩存正則表達式實例。如果您要查找的類名在運行時更改,則會更加困難。

或者,您可以通過將正則表達式實現為C#字符串處理函數,而不使用正則表達式來按字邊界搜索字符串,注意不要導致任何新字符串或其他對象分配(例如,不使用String.Split )。

方法1:使用正則表達式:

假設您只想查找具有單個設計時指定類名的元素:

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", "") ) );
    }
}

如果需要在運行時選擇單個類名,那麼可以構建一個正則表達式:

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", "") ) );
    }
}

如果您有多個類名並且想要匹配所有類名,則可以創建一個Regex對像數組並確保它們全部匹配,或者使用外觀將它們組合成單個正則Regex ,但這會導致極其複雜的表達式 -所以使用Regex[]可能更好:

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", "") ) );
    }
}

方法2:使用非正則表達式字符串匹配:

使用自定義C#方法進行字符串匹配而不是正則表達式的優勢在於假設性能更快,內存使用率更低(儘管在某些情況下正則Regex更快 - 總是先編寫代碼,孩子們!)

下面的方法: CheapClassListContains提供了一個快速的字邊界檢查字符串匹配函數,可以像regex.IsMatch一樣使用:

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", "") ) );
    }
}

方法3:使用CSS Selector庫:

HtmlAgilityPack有點停滯不支持.querySelector.querySelectorAll ,但是有第三方庫用它來擴展HtmlAgilityPack:即FizzlerCssSelectors 。 Fizzler和CssSelectors都實現了QuerySelectorAll ,因此您可以像這樣使用它:

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", "") ) );
    }
}

使用運行時定義的類:

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", "") ) );
    }
}

熱門答案

您可以使用Xpath查詢中的“包含”功能解決您的問題,如下所示:

var allElementsWithClassFloat = 
   _doc.DocumentNode.SelectNodes("//*[contains(@class,'float')]")

要在函數中重用它,請執行以下類似操作:

var allElementsWithClassFloat = 
   _doc.DocumentNode.SelectNodes("//*[contains(@class,'float')]")



許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因