¿es posible solucionar el problema en HtmlAgilityPack cuando hay una etiqueta html no cerrada?

c# html-agility-pack xpath

Pregunta

bueno tengo el siguiente problema
el html que tengo tiene un formato incorrecto y tengo problemas con la selección de nodos que usan el paquete de agilidad html cuando este es el caso.
el código está abajo:

string strHtml = @"
<html>
  <div>
    <p><strong>Elem_A</strong>String_A1_2 String_A1_2</p>
    <p><strong>Elem_B</strong>String_B1_2 String_B1_2</p>
  </div>
  <div>
    <p><strong>Elem_A</strong>String_A2_2 <String_A2_2> asdas</p>
    <p><strong>Elem_B</strong>String_B2_2 String_B2_2</p>
  </div>
</html>";
HtmlAgilityPack.HtmlDocument objHtmlDocument = new HtmlAgilityPack.HtmlDocument();
objHtmlDocument.LoadHtml(strHtml);
HtmlAgilityPack.HtmlNodeCollection colnodePs = objHtmlDocument.DocumentNode.SelectNodes("//p");
List<string> lststrText = new List<string>();
foreach (HtmlAgilityPack.HtmlNode nodeP in colnodePs)
{
  lststrText.Add(nodeP.InnerHtml);
}

el problema es que String_A2_2 está encerrado entre paréntesis.
así que htmlagility pack devuelve 5 cadenas en lugar de 4 en el lststrText.
entonces, ¿es posible dejar que htmlagility pack devuelva el elemento 3 como "<strong>Elem_A</strong>String_A2_2 <String_A2_2> asdas" ?
¿O tal vez puedo hacer algo de preprocesamiento para cerrar el elemento?
El contenido actual de lststrText es

lststrText[0] = "<strong>Elem_A</strong>String_A1_2 String_A1_2"  
lststrText[1] = "<strong>Elem_B</strong>String_B1_2 String_B1_2"  
lststrText[2] = ""  
lststrText[3] = ""  
lststrText[4] = "<strong>Elem_B</strong>String_B2_2 String_B2_2"

Respuesta aceptada

La mayoría de los analizadores html intentan construir un DOM funcional, lo que significa que no se aceptan las etiquetas colgantes. Se convertirán, o se cerrarán de alguna manera.

Si solo seleccionar los nodos es importante para usted, y la velocidad y las enormes cantidades de datos no son un problema, puede agarrar todas sus etiquetas <p> con una expresión regular:

Regex reMatchP = new Regex(@"<(p)>.*?</\1>");
foreach (Match m in reMatchP.Matches(strHtml))
{
   Console.WriteLine(m.Value);
}

Esta expresión regular asume que las etiquetas <p> están bien formadas y cerradas.

Si va a ejecutar este Regex mucho en su programa, debe declararlo como:

static Regex reMatchP = new Regex(@"<(p)>.*?</\1>", RegexOptions.Compiled);

[Editar: cambio de paquete de agilidad]

Si desea utilizar el paquete HtmlAgility, puede modificar la función PushNodeEnd en HtmlDocument.cs:

if (HtmlNode.IsCDataElement(CurrentNodeName()))
{
   _state = ParseState.PcData;
   return true;
}

// new code start
if ( !AllowedTags.Contains(_currentnode.Name) )
{
    close = true;
}
// new code end

donde AllowedTags sería una lista de todas las etiquetas conocidas: b, p, br, span, div, etc.

¿La salida no es 100% lo que quieres, pero quizás lo suficientemente cerca?

<strong>Elem_A</strong>String_A1_2 String_A1_2
<strong>Elem_B</strong>String_B1_2 String_B1_2
<strong>Elem_A</strong>String_A2_2 <ignorestring_a2_2></ignorestring_a2_2> asdas
<strong>Elem_B</strong>String_B2_2 String_B2_2

Respuesta popular

Podría usar TidyNet para realizar el proceso previo / posterior al que alude. ¿Puede editar su respuesta para explicar por qué eso no sería aplicable en su caso?



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é