HtmlAgilityPack selecciona datos de subnodo

c# html html-agility-pack linq parsing

Pregunta

Tengo el siguiente código HTML que me gustaría analizar:

<h3 class='bar'>
  <a href='http://anysite.com/index.php?showuser=7195' title='Profile view'>THIS_IS_USERNAME</a>
  &nbsp;
  <a href='http://anysite.com/index.php?showuser=7195&amp;f=' class='__user __id7195' title='Profile view'>
      <img src='http://anysite.com/public/style_images/car/user_popup.png' alt='' />
   </a>
</h3>

Lo que necesito aquí es seleccionar el nombre de usuario ("THIS_IS_USERNAME") y el enlace al perfil (" http://anysite.com/index.php?showuser=7195 ")

Puedo seleccionar el nodo h3 superior usando el siguiente código:

List<HtmlNode> resultSearch = HTMLPage.DocumentNode.Descendants()
                .Where(
                         x => x.Name.Equals("h3")
                         && x.Attributes["class"] != null
                         && x.Attributes["class"].Value.Equals("bar")                         
                      )
                .ToList();

Pero, ¿cómo puedo obtener no el nodo "h3" en sí, sino "a" dentro de "h3" con este enlace de atributos que contiene el nombre de usuario y el enlace al perfil que necesito?

Respuesta aceptada

Puede consultar el nodo de enlace directamente, es bastante distintivo con el atributo Título en él.

En este caso, usar un XPath es probablemente más sencillo, ya que maneja todas las comprobaciones nulas intermedias y es seguro para el tipo, ya que su consulta Linq tendrá muchas cadenas constantes:

var node = HTMLPage.DocumentNode.SelectSingleNode("//hr[@class='Bar']/a[@title='Profile View' and @href");
if (node != null)
{
    string link = node.Attributes["href"].Value;
    string username = node.InnerText;
}

Puede escribir código similar usando la sintaxis de Linq, primero busca la etiqueta de enlace y luego retrocede para encontrar un padre h3 para él. De esa manera no tienes que verificar los nulos intermedios;):

var node = HtmlPage.DocumentNode.Descendants("a")
    .Where(a =>
        a.Ascendants("h3")
            .Any(h3 =>
                h3.Attributes["class"] != null 
                && a.Attributes["class"].Value == "bar"
            )
    )
    .Where(a => 
        a.Attributes["title"] != null 
        && a.Attributes["title"].Value == "Profile View"
        && a.Attributes["href"] != null
    )
    .FirstOrDefault();

if (node != null)
{
    string link = node.Attributes["href"].value;
    string username = node.InnerText;
}

O puede usar la posición de ser el primer <a> hijo de "barra":

// the call to First() will throw an exception if the h3 isn't found.
// returning an empty HtmlNode will allow you to ignore that

var node = (HtmlPage.DocumentNode.Descendants("h3")
    .FirstOrDefault( h => 
            h3.Attributes["class"] != null 
            && a.Attributes["class"].Value == "bar")
    ) ?? HtmlPage.CreateElement("h3")) 
    .Elements("a").FirstOrDefault();

if (node != null)
{
    string link = node.Attributes["href"].value;
    string username = node.InnerText;
}


Related

Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow