¿Cómo puedo quitar etiquetas HTML de una cadena en ASP.NET?

123

Con ASP.NET, ¿cómo puedo quitar las etiquetas HTML de una cadena determinada de manera confiable (es decir, sin usar expresiones regulares)? Estoy buscando algo como PHP strip_tags.

Ejemplo:

<ul><li>Hello</li></ul>

Salida:

"Hola"

Estoy tratando de no reinventar la rueda, pero hasta ahora no he encontrado nada que satisfaga mis necesidades.

daniel
fuente
¡Me imagino que PHP strip_tags usa regex detrás de escena!
stevehipwell
10
@Daniel: porque regex es muy malo en eso, especialmente si tienes anidamiento.
Joel Coehoorn el
Hmm, no parece que Strip_Tags de PHP sea particularmente confiable, ya sea en las notas oficiales
Zhaph - Ben Duguid

Respuestas:

112

Si solo está eliminando todas las etiquetas HTML de una cadena, esto también funciona de manera confiable con regex. Reemplazar:

<[^>]*(>|$)

con la cadena vacía, globalmente. No olvides normalizar la cadena después, reemplazando:

[\s\r\n]+

con un solo espacio, y recortando el resultado. Opcionalmente, reemplace las entidades de caracteres HTML de nuevo a los caracteres reales.

Nota :

  1. Hay una limitación: HTML y XML permiten >valores de atributos. Esta solución va a volver marcado rota cuando se enfrentan a tales valores.
  2. La solución es técnicamente segura, como en: El resultado nunca contendrá nada que pueda usarse para hacer scripts de sitios cruzados o para romper un diseño de página. Simplemente no está muy limpio.
  3. Al igual que con todas las cosas HTML y expresiones regulares:
    use un analizador adecuado si debe hacerlo correctamente en todas las circunstancias.
Tomalak
fuente
52
Aunque no se solicitó, creo que muchos lectores querrán eliminar también la codificación HTM, como &quote;. Lo combino con WebUtility.HtmlDecodeeso (que a su vez no eliminará etiquetas). Úselo después de eliminar la etiqueta, ya que puede reescribir &gt;y &lt;. Ej .WebUtility.HtmlDecode(Regex.Replace(myTextVariable, "<[^>]*(>|$)", string.Empty))
Yahoo Serious
@YahooSerious Gracias por proporcionar un ejemplo. Esto funciona muy bien. Gracias.
SearchForKnowledge
Html Agility Pack es el camino a seguir, lo utilicé en los formularios web para eliminar páginas enteras y usar contenido.
Bojangles
3
@YahooSerious esto permitirá un vector XSS en & gt; guión & lt; alerta ("XXS"); & gt; / script & lt; El regex no lo desinfectará, pero HtmlDecode lo convertirá en alerta <script> ("XXS"); </ script>
1
@Heather Muy buen punto. La eliminación de etiquetas HTML tendría que hacerse nuevamente después de la decodificación de la entidad.
Tomalak
76

¡Vaya a descargar HTMLAgilityPack, ahora! ;) Descargar LInk

Esto le permite cargar y analizar HTML. Luego puede navegar por el DOM y extraer los valores internos de todos los atributos. En serio, te llevará unas 10 líneas de código como máximo. Es una de las mejores bibliotecas .net gratuitas que existen.

Aquí hay una muestra:

            string htmlContents = new System.IO.StreamReader(resultsStream,Encoding.UTF8,true).ReadToEnd();

            HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
            doc.LoadHtml(htmlContents);
            if (doc == null) return null;

            string output = "";
            foreach (var node in doc.DocumentNode.ChildNodes)
            {
                output += node.InnerText;
            }
Serapth
fuente
2
incluso puede consultar cada text()nodo, recortar el contenido y la cadena. Únase a aquellos con espacio. IEnumerable<string> allText = doc.DocumentNode.SelectNodes("//text()").Select(n => n.InnerText.Trim())
jessehouwing
o simplemente utilizar doc.DocumentNode.InnerText, aunque esto tiene algunos problemas con whitespacehandling parece ...
jessehouwing
17
¿Por qué el if (doc == null)cheque? Esto siempre es falso, ¿no es así?
avesse
67
Regex.Replace(htmlText, "<.*?>", string.Empty);
usuario95144
fuente
Simple y agradable ¡Gracias!
Tillito
55
Tiene muchos problemas: no se ocupa de los atributos que tienen <o> en ellos y no funciona bien con las etiquetas que abarcan más de una línea a menos que se ejecute con ellas RegexOptions.SingleLine.
ChrisF
2
Noooo, usa "<[^>] *>".
Paul Kienitz
11
protected string StripHtml(string Txt)
{
    return Regex.Replace(Txt, "<(.|\\n)*?>", string.Empty);
}    

Protected Function StripHtml(Txt as String) as String
    Return Regex.Replace(Txt, "<(.|\n)*?>", String.Empty)
End Function
meramez
fuente
2
No funciona para muchos casos, incluidos los saltos de línea que no son Unix.
ChrisF
6

He publicado esto en los foros de asp.net, y todavía parece ser una de las soluciones más fáciles que existen. No garantizo que sea el más rápido o el más eficiente, pero es bastante confiable. En .NET puede usar los objetos HTML Web Control ellos mismos. Todo lo que realmente necesita hacer es insertar su cadena en un objeto HTML temporal como un DIV, luego use el 'Texto interno' incorporado para capturar todo el texto que no está contenido en las etiquetas. Vea a continuación un ejemplo simple de C #:


System.Web.UI.HtmlControls.HtmlGenericControl htmlDiv = new System.Web.UI.HtmlControls.HtmlGenericControl("div");
htmlDiv.InnerHtml = htmlString;
String plainText = htmlDiv.InnerText;
Michael Tipton
fuente
esto no parece funcionar, lo probé con InnerHtml = "<b> foo </b>" simple; e InnerText tiene el valor "<b> foo </b>" :(
Axarydax
No hagas esto. Esta solución inyecta html sin codificar directamente en la salida. Esto lo dejaría abierto a los ataques de Cross Site Scripting: ¡acaba de permitir que cualquiera que pueda cambiar la cadena html inyecte html y javascript arbitrarios en su aplicación!
saille
5

He escrito un método bastante rápido en C # que supera a Regex. Está alojado en un artículo sobre CodeProject.

Sus ventajas son, entre un mejor rendimiento, la capacidad de reemplazar entidades HTML nombradas y numeradas (aquellas como &amp;amp;y &203;) y el reemplazo de bloques de comentarios y más.

Lea el artículo relacionado en CodeProject .

Gracias.

Andrei Rînea
fuente
4

Para aquellos de ustedes que no pueden usar el HtmlAgilityPack, el lector XML .NET es una opción. Sin embargo, esto puede fallar en HTML bien formateado, por lo que siempre debe agregar una captura con regx como copia de seguridad. Tenga en cuenta que esto NO es rápido, pero proporciona una buena oportunidad para el paso de la vieja escuela a través de la depuración.

public static string RemoveHTMLTags(string content)
    {
        var cleaned = string.Empty;
        try
        {
            StringBuilder textOnly = new StringBuilder();
            using (var reader = XmlNodeReader.Create(new System.IO.StringReader("<xml>" + content + "</xml>")))
            {
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Text)
                        textOnly.Append(reader.ReadContentAsString());
                }
            }
            cleaned = textOnly.ToString();
        }
        catch
        {
            //A tag is probably not closed. fallback to regex string clean.
            string textOnly = string.Empty;
            Regex tagRemove = new Regex(@"<[^>]*(>|$)");
            Regex compressSpaces = new Regex(@"[\s\r\n]+");
            textOnly = tagRemove.Replace(content, string.Empty);
            textOnly = compressSpaces.Replace(textOnly, " ");
            cleaned = textOnly;
        }

        return cleaned;
    }
Cubeta
fuente
3
string result = Regex.Replace(anytext, @"<(.|\n)*?>", string.Empty);
Alex
fuente
1

Para aquellos que están cumpliendo con la solución de Michael Tiptop que no funciona, aquí está la forma .Net4 + de hacerlo:

public static string StripTags(this string markup)
{
    try
    {
        StringReader sr = new StringReader(markup);
        XPathDocument doc;
        using (XmlReader xr = XmlReader.Create(sr,
                           new XmlReaderSettings()
                           {
                               ConformanceLevel = ConformanceLevel.Fragment
                               // for multiple roots
                           }))
        {
            doc = new XPathDocument(xr);
        }

        return doc.CreateNavigator().Value; // .Value is similar to .InnerText of  
                                           //  XmlDocument or JavaScript's innerText
    }
    catch
    {
        return string.Empty;
    }
}
Annie
fuente
1
using System.Text.RegularExpressions;

string str = Regex.Replace(HttpUtility.HtmlDecode(HTMLString), "<.*?>", string.Empty);
Karan
fuente
0

He examinado las soluciones basadas en Regex sugeridas aquí, y no me llenan de confianza, excepto en los casos más triviales. Un corchete angular en un atributo es todo lo que se necesitaría para romper, y mucho menos HTML mal formado desde la naturaleza. ¿Y qué hay de las entidades como &amp;? Si desea convertir HTML en texto sin formato, también debe decodificar entidades.

Así que propongo el siguiente método.

Usando HtmlAgilityPack , este método de extensión elimina eficazmente todas las etiquetas HTML de un fragmento html. También decodifica entidades HTML como &amp;. Devuelve solo los elementos de texto interno, con una nueva línea entre cada elemento de texto.

public static string RemoveHtmlTags(this string html)
{
        if (String.IsNullOrEmpty(html))
            return html;

        var doc = new HtmlAgilityPack.HtmlDocument();
        doc.LoadHtml(html);

        if (doc.DocumentNode == null || doc.DocumentNode.ChildNodes == null)
        {
            return WebUtility.HtmlDecode(html);
        }

        var sb = new StringBuilder();

        var i = 0;

        foreach (var node in doc.DocumentNode.ChildNodes)
        {
            var text = node.InnerText.SafeTrim();

            if (!String.IsNullOrEmpty(text))
            {
                sb.Append(text);

                if (i < doc.DocumentNode.ChildNodes.Count - 1)
                {
                    sb.Append(Environment.NewLine);
                }
            }

            i++;
        }

        var result = sb.ToString();

        return WebUtility.HtmlDecode(result);
}

public static string SafeTrim(this string str)
{
    if (str == null)
        return null;

    return str.Trim();
}

Si usted es realmente serio, que querría hacer caso omiso de los contenidos de determinadas etiquetas HTML también ( <script>, <style>, <svg>, <head>, <object>vienen a la mente!), Ya que probablemente no contienen contenido legible en el sentido de que estamos buscando. Lo que hagas allí dependerá de tus circunstancias y de cuán lejos quieras llegar, pero usar HtmlAgilityPack sería bastante trivial para incluir en la lista blanca o lista negra las etiquetas seleccionadas.

Si está volviendo a procesar el contenido en una página HTML, asegúrese de comprender la vulnerabilidad XSS y cómo prevenirla, es decir, siempre codifique cualquier texto ingresado por el usuario que se vuelva a procesar en una página HTML (se >convierte en &gt;etc.).

saille
fuente
0

Para el segundo parámetro, es decir, mantener algunas etiquetas, es posible que necesite un código como este mediante HTMLagilityPack:

public string StripTags(HtmlNode documentNode, IList keepTags)
{
    var result = new StringBuilder();
        foreach (var childNode in documentNode.ChildNodes)
        {
            if (childNode.Name.ToLower() == "#text")
            {
                result.Append(childNode.InnerText);
            }
            else
            {
                if (!keepTags.Contains(childNode.Name.ToLower()))
                {
                    result.Append(StripTags(childNode, keepTags));
                }
                else
                {
                    result.Append(childNode.OuterHtml.Replace(childNode.InnerHtml, StripTags(childNode, keepTags)));
                }
            }
        }
        return result.ToString();
    }

Más explicación en esta página: http://nalgorithm.com/2015/11/20/strip-html-tags-of-an-html-in-c-strip_html-php-equivalent/

Yuksel Daskin
fuente
0

También puede hacer esto con AngleSharp, que es una alternativa a HtmlAgilityPack (no es que HAP sea malo). Es más fácil de usar que HAP para sacar el texto de una fuente HTML.

var parser = new HtmlParser();
var htmlDocument = parser.ParseDocument(source);
var text = htmlDocument.Body.Text();

Puede echar un vistazo a la sección de características clave en la que defienden ser "mejores" que HAP. Creo que, en su mayor parte, probablemente sea excesivo para la pregunta actual, pero aún así, es una alternativa interesante.

Yepeekai
fuente
-4

Simplemente use string.StripHTML();

usuario3638478
fuente
3
Como señala @Serpiton, no existe tal método en el BCL. ¿Podría señalar una implementación de este método o proporcionar la suya propia?
Sven Grosen