2012-12-07 30 views
51

Tôi đang dùng một đoạn mã tại gói nhanh nhẹn html và gặp khó khăn khi tìm đúng cách để thực hiện việc này.Gói Agility Html nhận tất cả các phần tử theo lớp

Ví dụ:

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

Tuy nhiên, rõ ràng là bạn có thể thêm các lớp học để hơn rất nhiều sau đó divs vì vậy tôi cố gắng này ..

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

Nhưng điều đó không xử lý các trường hợp bạn thêm nhiều lớp và "phao" chỉ là một trong số chúng như thế này ..

class="className float anotherclassName" 

Có cách nào để xử lý tất cả điều này không? Về cơ bản tôi muốn chọn tất cả các nút có class = và chứa float.

** trả lời đã được ghi nhận trên blog của tôi với một lời giải thích đầy đủ tại địa chỉ: Html Agility Pack Get All Elements by Class

Trả lời

79

Chỉ cần bổ sung thêm điều khoản cho vị ngữ của bạn:

var findclasses = _doc.DocumentNode 
    .Descendants("div") 
    .Where(d => 
     d.Attributes.Contains("class") 
     && 
     d.Attributes["class"].Value.Contains("float") 
    ); 

tôi có thể khuyên bạn nên tạo một phương pháp khuyến nông HasClass và sử dụng nó như vậy:

IEnumerable<HtmlNode> hasFloatClass = _doc.DocumentNode 
    .Descendants("div") 
    .Where(div => div.HasClass("float")); 

public static Boolean HasClass(this HtmlNode element, String className) 
{ 
    if(element == null) throw new ArgumentNullException(nameof(element)); 
    if(String.IsNullOrWhitespace(className)) throw new ArgumentNullException(nameof(className)); 
    if(element.NodeType != HtmlNodeType.Element) return false; 

    HtmlAttribute classAttrib = element.Attributes["class"]; 
    if(classAttrib == null) return false; 

    Boolean hasClass = CheapClassListContains(classAttrib.Value, className, StringComparison.Ordinal); 
    return hasClass; 
} 

/// <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; 
} 

HtmlAgilityPack nhằm cung cấp triển khai các giao diện DOM (ví dụ: createElement, getElementById, v.v.) nhưng bây giờ hơi muộn và thiếu các tính năng DOM mới như classList sẽ làm cho điều này trở nên tầm thường.

... Tôi có thể gửi yêu cầu vá với các thay đổi mới, nhưng HtmlAgilityPack không có repo GitHub chính thức.

+0

Wont nguyên nhân này chỉ divs được tìm thấy? Nếu tôi thêm lớp đó vào một Adam

+1

Sau đó, loại bỏ vị từ "div". – Dai

+0

bạn có thể làm .Descendants ("")? – Adam

68

Bạn có thể giải quyết vấn đề của bạn bằng cách sử dụng 'chứa' chức năng trong truy vấn XPath của bạn, như sau:

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

Để tái sử dụng này trong một chức năng làm điều gì đó tương tự như sau:

string classToFind = "float";  
var allElementsWithClassFloat = 
    _doc.DocumentNode.SelectNodes(string.Format("//*[contains(@class,'{0}')]", classToFind)); 
+0

loại đối tượng 'allElementsWithClassFloat' là gì? –

+0

'allElementsWithClassFloat' là một HtmlNodeCollection – feztheforeigner

+0

Thay vì string.Format bạn cũng có thể sử dụng' $ "// * [contains (@class, '{classToFind}')]" ' – feztheforeigner

-7

Bạn có thể sử dụng tập lệnh sau:

var findclasses = _doc.DocumentNode.Descendants("div").Where(d => 
    d.Attributes.Contains("class") && d.Attributes["class"].Value.Contains("float") 
); 
2

Tôi đã sử dụng phương pháp mở rộng này rất nhiều trong dự án của mình. Hy vọng nó sẽ giúp một trong các bạn.

public static bool HasClass(this HtmlNode node, params string[] classValueArray) 
    { 
     var classValue = node.GetAttributeValue("class", ""); 
     var classValues = classValue.Split(' '); 
     return classValueArray.All(c => classValues.Contains(c)); 
    } 
+1

Không sử dụng 'ToLower()' khi những gì bạn thực sự muốn là so sánh IgnoreCase. Việc chuyển 'StringComparison.CultureIgnoreCase' sẽ rõ ràng hơn và hiển thị ý định rõ ràng hơn. –

+0

Vâng, bạn nói đúng. Chúng tôi chắc chắn có thể sử dụng điều đó. –

0
public static List<HtmlNode> GetTagsWithClass(string html,List<string> @class) 
    { 
     // LoadHtml(html);   
     var result = htmlDocument.DocumentNode.Descendants() 
      .Where(x =>x.Attributes.Contains("class") && @class.Contains(x.Attributes["class"].Value)).ToList();   
     return result; 
    }  
Các vấn đề liên quan