2013-02-21 11 views
28

Spero che qualcuno possa aiutarmi.HtmlAgilityPack e selezione di nodi e sottonodi

Mettiamoci dire che ho un documento HTML che contiene più div come in questo esempio:

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

sto usando HtmlAgilityPack per ottenere il documento html. Quello che ho bisogno di sapere è come posso ottenere gli span per ogni "search_hit" -div?

Il mio primo pensiero è stato qualcosa di simile:

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

    } 
} 

Ogni div deve essere un oggetto con le campate inclusi come proprietà. I. e.

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

E questo elenco è riempito poi:

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

Ma la sto usando XPATH non sta facendo una ricerca nel sottonodo come dovrebbe fare. Sembra che ricerchi l'intero documento ancora e ancora.

Voglio dire, ho già funzionato in questo modo che ottengo solo gli span dell'intera pagina. Ma poi non ho alcuna relazione tra gli span e le div. Mezzi: non so più quale intervallo è correlato a quale div.

Qualcuno conosce una soluzione? Ho già giocato così tanto che ora sono completamente confuso :)

Qualsiasi aiuto è apprezzato!

+0

Vedere la mia risposta al modo di analizzare il codice (soluzione completa di lavoro). –

risposta

24

i seguenti lavori per me. Il bit importante è proprio come BeniBela ha notato di aggiungere un punto in seconda chiamata a "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

Se si utilizza //, viene avviata la ricerca dal documento.

Usa .// per cercare tutti dal nodo corrente

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

o far cadere il prefisso del tutto per cercare solo per i bambini diretti:

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

Se si esegue questa operazione: foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes ("span [@prop]")) Visual Studio ha generato un errore. –

+0

che tipo di errore? Puoi anche provare il prefisso '. /' Per quello. (In realtà sto solo indovinando) – BeniBela

+0

Ho provato entrambi e entrambi si sono conclusi con: NullReferenceException: riferimento oggetto non impostato su un'istanza di un oggetto. –

2

Prima di tutto, dare un'occhiata a questo: Html Agility Pack - Problem selecting subnode

Ecco una soluzione di lavoro completa per la tua domanda:

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

Se leggete la domanda che ho fatto a , vedrai che fare ./span[@prop='name'] è esattamente lo stesso, poiché quei nodi span sono figli (diretti) del nodo div.


Se i nodi span non hanno quei prop attributi, e si desidera assegnare loro a seconda nell'ordine in cui appaiono, si può fare:

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

Vergogna su di me :)

Tutti quanti avevi ragione.

Ho trovato il problema. Questa NullReferenceException continuava a tormentarmi, così ho passato più tempo a guardarlo nei dettagli. Tra tutte quelle div c'era un div con lo stesso attributo "class = 'search-hit'" ma senza gli span all'interno. Ecco perché si verifica un errore nel secondo ciclo.

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

Il codice sopra riportato funziona.

Grazie ragazzi per il vostro tempo e il vostro aiuto!

0

L'ho usato. classe convert 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); }