2013-02-21 10 views
28

Hoffe jemand kann mir helfen.HtmlAgilityPack und Auswahl von Knoten und Unterknoten

Lasst uns sagen, dass ich ein HTML-Dokument, das mehrere divs wie in diesem Beispiel enthält:

<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> 

Ich bin HtmlAgilityPack mit dem HTML-Dokument zu erhalten. Was ich wissen muss ist, wie kann ich die Spannen für jedes "search_hit" -div bekommen?

Mein erster Gedanke war so etwas wie diese:

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

    } 
} 

Jedes div ein Objekt mit den mitgelieferten Spannweiten als Eigenschaften sein sollte. I. e.

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

Aber die XPATH du ist in dem untergeordneten Knoten eine Suche nicht verwenden, da es tun soll:

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

Und diese Liste wird dann gefüllt werden. Es scheint, dass es das gesamte Dokument immer wieder durchsucht.

Ich meine, ich habe es bereits funktioniert auf diese Weise, dass ich nur die Spannweiten der ganzen Seite bekommen. Aber dann habe ich keine Beziehung zwischen den Spannen und divs. Bedeutet: Ich weiß nicht mehr, welche Spanne mit welchem ​​div verwandt ist.

Kennt jemand eine Lösung? Ich habe schon so viel gespielt, dass ich jetzt total verwirrt bin :)

Jede Hilfe wird geschätzt!

+0

Siehe meine Antwort für die Möglichkeiten der Analyse Ihres Codes (vollständige Arbeitslösung). –

Antwort

24

Die folgenden Werke für mich. Das wichtige Bit ist genau wie BeniBela notierte, um einen Punkt im zweiten Aufruf zu 'SelectNodes' hinzuzufügen.

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

Wenn Sie // verwenden, wird vom Anfang des Dokuments aus gesucht.

Verwenden .// alle vom aktuellen Knoten suchen

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

Oder fällt das Präfix ganz einfach für die direkten Kinder suchen:

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

Wenn ich dies tue: foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes ("span [@prop]")) Visual Studio kommt mit einem Fehler. –

+0

Welche Art von Fehler? Sie können auch das '. /' Präfix dafür verwenden. (Ich bin eigentlich nur raten) – BeniBela

+0

Ich habe beide versucht und beide enden mit: NullReferenceException: Objektverweis nicht auf eine Instanz eines Objekts festgelegt. –

2

Zunächst einmal, werfen Sie einen Blick auf diese: Html Agility Pack - Problem selecting subnode

Hier eine vollständige Arbeitslösung für Ihre Frage ist:

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

Wenn Sie lesen Sie die Frage, die ich Ihnen zeigte auf Sie werden sehen, dass ./span[@prop='name'] ist genau das gleiche, da diese span Knoten (direkte) Kinder des div Knoten sind.


Wenn die span Knoten nicht jene prop Attribute haben, und Sie wollen, dass sie in der Größenordnung abhängig zuweisen sie erscheinen, können Sie tun:

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

Shame on me :)

Sie alle hatten Recht.

Ich habe das Problem gefunden. Diese NullReferenceException hat mich immer geärgert, also habe ich mehr Zeit darauf verwendet, sie im Detail zu betrachten. Zwischen all diesen divs gab es ein div mit dem gleichen Attribut "class = 'search-hit'", aber ohne die Spannen innerhalb. Deshalb durchläuft es einen Fehler in der zweiten Schleife.

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

Der obige Code funktioniert.

Vielen Dank für Ihre Zeit und Hilfe!

0

Ich habe das verwendet. Klasse konvertieren 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); }