2012-01-20 8 views
5

Questo è l'esempio di codice HTML che sto cercando di analizzare con Html Agility Pack in ASP.Net (C#).Analisi di dl con HtmlAgilityPack

<div class="content-div"> 
    <dl> 
     <dt> 
      <b><a href="1.html" title="1">1</a></b> 
     </dt> 
     <dd> First Entry</dd> 
     <dt> 
      <b><a href="2.html" title="2">2</a></b> 
     </dt> 
     <dd> Second Entry</dd> 
     <dt> 
      <b><a href="3.html" title="3">3</a></b> 
     </dt> 
     <dd> Third Entry</dd> 
    </dl> 
</div> 

I Valori che voglio sono:

  • Il collegamento ipertestuale -> 1.html
  • The Anchor Text -> 1
  • interno testo od dd -> Primo Entry

(Ho preso esempi della prima voce qui ma voglio i valori di questi elementi per tutte le voci nell'elenco)

Questo è il codice che sto usando attualmente,

var webGet = new HtmlWeb(); 
      var document = webGet.Load(url2); 
var parsedValues= 
    from info in document.DocumentNode.SelectNodes("//div[@class='content-div']") 
    from content in info.SelectNodes("dl//dd") 
    from link in info.SelectNodes("dl//dt/b/a") 
     .Where(x => x.Attributes.Contains("href")) 
    select new 
    { 
     Text = content.InnerText, 
     Url = link.Attributes["href"].Value, 
     AnchorText = link.InnerText, 
    }; 

GridView1.DataSource = parsedValues; 
GridView1.DataBind(); 

Il problema è che ho i valori per il collegamento e il testo di ancoraggio in modo corretto, ma per il testo interno di ci vuole solo il valore della prima voce e riempie lo stesso valore per tutte le altre voci per il numero totale di volte che l'elemento si verifica e quindi ricomincia con il secondo. Non può essere così chiaro nella mia spiegazione Quindi, ecco un esempio di output sto ottenendo con questo codice:

First Entry  1.html 1 
First Entry  2.html 2 
First Entry  3.html 3 
Second Entry 1.html 1 
Second Entry 2.html 2 
Second Entry 3.html 3 
Third Entry  1.html 1 
Third Entry  2.html 2 
Third Entry  3.html 3 

Mentre io sto cercando di ottenere

First Entry  1.html  1 
Second Entry  2.html  2 
Third Entry  3.html  3 

Sono abbastanza nuovo per HAP e sono molto poca conoscenza su xpath, quindi sono sicuro che sto facendo qualcosa di sbagliato qui, ma non potrei farlo funzionare anche dopo aver passato ore su di esso. Qualsiasi aiuto sarebbe molto apprezzato.

risposta

4

Soluzione 1

Ho definito una funzione che dato un nodo dt restituirà la dd nodo successivo dopo che:

private static HtmlNode GetNextDDSibling(HtmlNode dtElement) 
{ 
    var currentNode = dtElement; 

    while (currentNode != null) 
    { 
     currentNode = currentNode.NextSibling; 

     if(currentNode.NodeType == HtmlNodeType.Element && currentNode.Name =="dd") 
      return currentNode; 
    } 

    return null; 
} 

e ora il codice LINQ può essere trasformato per:

var parsedValues = 
    from info in document.DocumentNode.SelectNodes("//div[@class='content-div']") 
    from dtElement in info.SelectNodes("dl/dt") 
    let link = dtElement.SelectSingleNode("b/a[@href]") 
    let ddElement = GetNextDDSibling(dtElement) 
    where link != null && ddElement != null 
    select new 
    { 
     Text = ddElement.InnerHtml, 
     Url = link.GetAttributeValue("href", ""), 
     AnchorText = link.InnerText 
    }; 

Soluzione 2

Senza funzioni aggiuntive:

var infoNode = 
     document.DocumentNode.SelectSingleNode("//div[@class='content-div']"); 

var dts = infoNode.SelectNodes("dl/dt"); 
var dds = infoNode.SelectNodes("dl/dd"); 

var parsedValues = dts.Zip(dds, 
    (dt, dd) => new 
    { 
     Text = dd.InnerHtml, 
     Url = dt.SelectSingleNode("b/a[@href]").GetAttributeValue("href", ""), 
     AnchorText = dt.SelectSingleNode("b/a[@href]").InnerText 
    }); 
+0

Grazie mille, funziona. Tuttavia non capisco esattamente cosa sta succedendo qui. Può essere fatto senza una funzione, usando un qualche tipo di xpath/regex? Questa soluzione è perfetta per me, sono solo curioso di sapere come funziona. Sarà bello se puoi dare una breve spiegazione, grazie. – redGREENblue

+1

Il problema è che abbiamo bisogno di qualcosa del tipo "dato questo tag' dt', dammi il tag 'dd' che viene immediatamente dopo". Ho cercato di renderlo più semplice, selezionando ddElement come dtElement.NextSibling, ma HtmlAgilityPack considera il nodo successivo come lo spazio bianco tra i tag '

' e '
'. Questo mi ha portato alla soluzione leggermente più brutta che vedi sopra. – GolfWolf

+1

Ho appena scoperto un'altra possibile soluzione che non prevede la dichiarazione di una funzione - si prega di consultare la risposta aggiornata. – GolfWolf

2

Solo un esempio di come è possibile analizzare alcuni elementi utilizzando Html Agility Pack

public string ParseHtml() 
{ 
    string output = null; 
    HtmlDocument htmldocument = new HtmlDocument(); 
    htmldocument.LoadHtml(YourHTML); 

    HtmlNode node = htmldocument.DocumentNode;  

    HtmlNodeCollection dds = node.SelectNodes("//dd"); //Select all dd tags 
    HtmlNodeCollection anchors = node.SelectNodes("//b/a[@href]"); //Select all 'a' tags that contais href attribute 

    for (int i = 0; i < dds.Count; i++) 
    { 
     string atributteValue = null. 
     Text = dds[i].InnerText; 
     Url = anchors[i].GetAttributeValue("href", atributteValue); 
     AnchorText = anchors[i].InnerText; 

     //Your code... 
    } 
    return output; 
}