2011-01-06 10 views
12

tengo el siguiente código HTMLanálisis de HTML con la agilidad paquete de HTML y LINQ

(..) 
<tbody> 
<tr> 
    <td class="name"> Test1 </td> 
    <td class="data"> Data </td> 
    <td class="data2"> Data 2 </td> 
</tr> 
<tr> 
    <td class="name"> Test2 </td> 
    <td class="data"> Data2 </td> 
    <td class="data2"> Data 2 </td> 
</tr> 
</tbody> 
(..) 

La información que tengo es el nombre => modo "Prueba1" & "Test2". Lo que quiero saber es cómo puedo obtener los datos que están en "datos" y "datos2" basados ​​en el nombre que tengo.

Actualmente estoy usando:

var data = 
    from 
     tr in doc.DocumentNode.Descendants("tr") 
    from 
     td in tr.ChildNodes.Where(x => x.Attributes["class"].Value == "name") 
    where 
     td.InnerText == "Test1" 
    select tr; 

pero me da {"Object reference not set to an instance of an object."} cuando trato de mirar en data

+0

Exactamente, ¿qué estás tratando de hacer? ¿Y qué está haciendo el código que no quieres? –

+0

Hehe, lo siento, he olvidado agregarlo, lo estoy agregando ahora –

+0

¿Puede decirnos cuál es su error? ¿O lo que esperas que pase que no ocurra? –

Respuesta

13

En cuanto a su intento, tiene dos problemas con su código:

  1. ChildNodes es raro - También devuelve los nodos de texto de espacio en blanco, que no tienen un class atributos (no pueden tener atributos, de curso).
  2. Como comentó James Walford, los espacios alrededor del texto son significativos, es probable que desee recortarlos.

Con estas dos correcciones, las siguientes obras:

var data = 
     from tr in doc.DocumentNode.Descendants("tr") 
     from td in tr.Descendants("td").Where(x => x.Attributes["class"].Value == "name") 
    where td.InnerText.Trim() == "Test1" 
    select tr; 
1

Aquí es uno de los enfoques - en primer lugar analizar todos los datos en una estructura de datos, y luego leyó. Esto es un poco desordenado y sin duda necesita más validación, pero aquí va:

HtmlWeb hw = new HtmlWeb(); 
HtmlDocument doc = hw.Load("http://jsbin.com/ezuge4"); 
HtmlNodeCollection nodes = doc.DocumentNode 
           .SelectNodes("//table[@id='MyTable']//tr"); 
var data = nodes.Select(
    node => node.Descendants("td") 
     .ToDictionary(descendant => descendant.Attributes["class"].Value, 
         descendant => descendant.InnerText.Trim()) 
     ).ToDictionary(dict => dict["name"]); 
string test1Data = data["Test1"]["data"]; 

Aquí me vuelvo cada <tr> a un diccionario, donde la clase del <td> es una clave y el texto es un valor. A continuación, convierto la lista de diccionarios en un diccionario de diccionarios (consejo: abstraerlo), donde el name de cada <tr> es la clave.

0

en lugar de

td.InnerText == "Test1" 

tratar

td.InnerText == " Test1 " 

o

d.InnerText.Trim() == "Test1" 
4

Aquí es la forma XPATH - hmmm ... todo el mundo parece haber olvidado de la XPATH poder y concentrarse exclusivamente en C# XLinq, en estos días :-)

Esta función se lleva todos los valores de datos asociados a un nombre:

public static IEnumerable<string> GetData(HtmlDocument document, string name) 
{ 
    return from HtmlNode node in 
     document.DocumentNode.SelectNodes("//td[@class='name' and contains(text(), '" + name + "')]/following-sibling::td") 
     select node.InnerText.Trim(); 
} 

Por ejemplo, este código volcar todos los datos 'Prueba2':

HtmlDocument doc = new HtmlDocument(); 
    doc.Load(yourHtml); 

    foreach (string data in GetData(doc, "Test2")) 
    { 
     Console.WriteLine(data); 
    } 
+0

Pensé en un xpath con 'contiene', pero tiene un problema importante: la búsqueda de' Test1' también encontrará 'Test10',' NotTest1' y así sucesivamente. Realmente no sé suficiente xpath para superar ese problema ... – Kobi

+0

@Kobi - Si no quieres usar contiene, puedes usar =. Si los espacios en blanco son un problema, se pueden eliminar con normalize-space, o bien este enlace tiene más información: http://stackoverflow.com/questions/1852571/xpath-function-to-remove-white-space –

+2

La razón por la que preferir la respuesta de Linq sobre XPath es porque esta última es difícil de leer y comprender. El primero es perfectamente claro lo que se pretende y, si es necesario, puede dividir la consulta en subconsultas para depurarlo. XPath es obtuso e imposible de depurar. Es difícil verificar que está haciendo lo correcto sin una gran cantidad de datos de prueba. Solo buscar en Google una página autorizada sobre la sintaxis de XPath es una tarea odiosa. Todavía amo HAP, pero cada vez que veo una declaración XPath me avergüenza. –