Analizando dl con HtmlAgilityPack

asp.net c# html-agility-pack screen-scraping

Pregunta

Este es el HTML de muestra que trato de analizar con Html Agility Pack en ASP.Net (C #).

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

Los valores que quiero son:

  • El hipervínculo -> 1.html
  • El texto de anclaje -> 1
  • Texto interno od dd -> Primera entrada

(He tomado ejemplos de la primera entrada aquí, pero quiero los valores de estos elementos para todas las entradas en la lista)

Este es el código que estoy usando actualmente,

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

El problema es que obtengo los valores para el enlace y el texto de anclaje correctamente, pero para el texto interno solo toma el valor de la primera entrada y llena el mismo valor para todas las demás entradas para el número total de veces que ocurre el elemento y luego comienza de nuevo con el segundo. Puede que no sea tan claro en mi explicación, así que aquí hay una salida de muestra que obtengo con este código:

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

Mientras que estoy tratando de conseguir

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

Soy bastante nuevo en HAP y tengo muy poco conocimiento sobre xpath, por lo que estoy seguro de que estoy haciendo algo mal aquí, pero no pude hacerlo funcionar incluso después de pasar horas en él. Cualquier ayuda sería muy apreciada.

Respuesta aceptada

Solución 1

He definido una función que, dado un nodo dt , devolverá el siguiente nodo dd después de él:

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

y ahora el código LINQ se puede transformar en:

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

Solucion 2

Sin funciones adicionales:

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

Respuesta popular

Solo un ejemplo de cómo se pueden analizar algunos elementos utilizando 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;
}


Related

Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué