2013-02-21 12 views
28

Mam nadzieję, że ktoś mi pomoże.HtmlAgilityPack i wybieranie węzłów i podwęzłów

przejdźmy że mam dokument HTML, który zawiera wiele elementów div jak w poniższym przykładzie:

<div class="search_hit"> 

    <span prop="name">Richard Winchester</span> 
    <span prop="company">Kodak</span> 
    <span prop="street">Arlington Road 1</span> 

</div> 
<div class="search_hit"> 

    <span prop="name">Ted Mosby</span> 
    <span prop="company">HP</span> 
    <span prop="street">Arlington Road 2</span> 

</div> 

jestem przy użyciu HtmlAgilityPack dostać dokument HTML. Co muszę wiedzieć, w jaki sposób mogę uzyskać rozpiętości dla każdego "search_hit" -div?

Moją pierwszą myślą było coś takiego:

foreach (HtmlAgilityPack.HtmlNode node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']")) 
{ 
    foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes("//span[@prop]")) 
    { 

    } 
} 

Każdy div powinny być obiektem z dołączonych przęseł jak właściwości. I. e.

public class Record 
    { 
     public string Name { get; set; } 
     public string company { get; set; } 
     public string street { get; set; } 
    } 

A ta lista zostanie wypełniona następnie:

public List<Record> Results = new List<Record>(); 

Ale jestem XPATH użyciu nie robi poszukiwania w podwęzeł jak należy to zrobić. Zgadza się, że wielokrotnie przeszukuje cały dokument.

Mam na myśli, że już działa to w taki sposób, że mam po prostu rozpiętość całej strony. Ale nie mam związku między przęsłami i divami. Znaczy: nie wiem już, która rozpiętość jest związana z tym divem.

Czy ktoś zna rozwiązanie? Już grałem tyle, że jestem totalnie zdezorientowany :)

Każda pomoc jest doceniana!

+0

Zobacz moją odpowiedź dla sposobów parsowania kodu (pełne rozwiązanie robocze). –

Odpowiedz

24

następujące prace dla mnie. Ważne jest to, że BeniBela zauważyła dodanie kropki w drugim wywołaniu do "SelectNodes".

List<Record> lstRecords=new List<Record>(); 
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']")) 
{ 
    Record record=new Record(); 
    foreach (HtmlNode node2 in node.SelectNodes(".//span[@prop]")) 
    { 
    string attributeValue = node2.GetAttributeValue("prop", ""); 
    if (attributeValue == "name") 
    { 
     record.Name = node2.InnerText; 
    } 
    else if (attributeValue == "company") 
    { 
     record.company = node2.InnerText; 
    } 
    else if (attributeValue == "street") 
    { 
     record.street = node2.InnerText; 
    } 
    } 
    lstRecords.Add(record); 
} 
42

Jeśli użyjesz //, rozpocznie się wyszukiwanie od początku dokumentu.

Zastosowanie .// szukać wszystko od bieżącego węzła

foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes(".//span[@prop]")) 

lub spadek prefiks całkowicie szukać tylko dla bezpośrednich dzieci:

foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes("span[@prop]")) 
+0

Jeśli to zrobię: foreach (HtmlAgilityPack.HtmlNode node2 w node.SelectNodes ("span [@prop]")) Visual Studio występuje z błędem. –

+0

jakiego rodzaju błąd? Możesz również wypróbować przedrostek '. /' Do tego. (Tak naprawdę po prostu zgaduję) – BeniBela

+0

Próbowałem obu i kończy się na: NullReferenceException: Odwołanie do obiektu nie jest ustawione na wystąpienie obiektu. –

2

Przede wszystkim spojrzeć na to: Html Agility Pack - Problem selecting subnode

Oto pełna roztwór roboczy na swoje pytanie:

IList<Record> results = new List<Record>(); 
foreach (var node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']")) { 
    var record = new Record(); 
    record.Name = node.SelectSingleNode(".//span[@prop='name']").InnerText; 
    record.company = node.SelectSingleNode(".//span[@prop='company']").InnerText; 
    record.street = node.SelectSingleNode(".//span[@prop='street']").InnerText; 
    results.Add(record); 
} 

Jeżeli odczytuje pan kwestię wskazałem ci , zobaczysz, że wykonanie ./span[@prop='name'] jest dokładnie takie samo, ponieważ te węzły są węzłami (bezpośrednim) węzła div.


Jeśli span węzły nie mają tych prop atrybuty i chcesz przypisać je w zależności od kolejności ich występowania, można zrobić:

foreach (var node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']")) { 
    var spanNodes = node.SelectNodes("./span"); 
    var record = new Record(); 
    record.Name = spanNodes[0].InnerText; 
    record.company = spanNodes[1].InnerText; 
    record.street = spanNodes[2].InnerText; 
    results.Add(record); 
} 
2

Wstyd na mnie :)

Wszyscy mieliście rację.

Znalazłem problem. Ten wyjątek NullReferenceException wciąż mnie dręczył, więc poświęciłem więcej czasu, aby przyjrzeć się temu szczegółowo. Pomiędzy tymi wszystkimi divami był jeden div z tym samym atrybutem "class =" search-hit "", ale bez wewnętrznych przęseł. To dlatego przechodzi błąd w drugiej pętli.

foreach (HtmlAgilityPack.HtmlNode node in doc.DocumentNode.SelectNodes("//span[@prop]/ancestor::div[@class='search_hit']")) 
    { 
     Record rec = new Record(); 
     foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes(".//span[@prop]")) 
      { 
      } 
      rList.Results.Add(rec); 
    } 

Powyższy kod działa.

Dziękuję wam za poświęcony czas i pomoc!

0

Użyłem tego. konwersja klasy id

HtmlNodeCollection nodes = dokuman.DocumentNode.SelectNodes("//div[@id='search_hit']//span[@prop]"); 


      for (int i = 0; i < nodes .Count; i++) 
     { 
      var record = new Record(); 


       record.Name = links[i].InnerText; results.Add(record); } 
Powiązane problemy