Usar expresiones regulares de C # para eliminar etiquetas HTML

139

¿Cómo uso la expresión regular de C # para reemplazar / eliminar todas las etiquetas HTML, incluidos los corchetes angulares? ¿Puede alguien ayudarme con el código?

Keltex
fuente
No lo indica, pero infiero que también desea eliminar completamente los elementos de estilo y script y no solo eliminar la etiqueta. La respuesta de HTML Agility Pack a continuación es correcta para eliminar las etiquetas, pero para eliminar el script y el estilo, también necesitará algo como stackoverflow.com/questions/13441470/…
John
1
La pregunta indicada como duplicado tiene mucha información (¡y Tony el Pony!), Pero solo pedía abrir etiquetas, no todas las etiquetas. Así que no estoy seguro de que técnicamente sea un duplicado. Dicho esto, la respuesta es la misma: no lo hagas.
goodeye

Respuestas:

154

Como se indicó anteriormente, no debe usar expresiones regulares para procesar documentos XML o HTML. No funcionan muy bien con documentos HTML y XML, porque no hay forma de expresar estructuras anidadas de manera general.

Podrías usar lo siguiente.

String result = Regex.Replace(htmlDocument, @"<[^>]*>", String.Empty);

Esto funcionará para la mayoría de los casos, pero habrá casos (por ejemplo, CDATA que contienen paréntesis angulares) en los que esto no funcionará como se esperaba.

Daniel Brückner
fuente
13
Esta es una implementación ingenua. Es decir, <div id = "x <4>"> es, desafortunadamente, html válido. Sin embargo
ocupa de la
8
Como se dijo, soy consciente de que esta expresión fallará en algunos casos. Ni siquiera estoy seguro de si el caso general puede ser manejado por cualquier expresión regular sin errores.
Daniel Brückner el
1
¡No, esto fallará en todos los casos! Es codicioso.
Jake
13
@Cipher, ¿por qué crees que la codicia es un problema? Suponiendo que la coincidencia comienza al comienzo de una etiqueta HTML válida, nunca se extenderá más allá del final de esa etiqueta. Para eso está el [^>].
Alan Moore el
1
@AlanMoore html no es un "lenguaje normal", es decir, no puede hacer coincidir correctamente todo lo que es html válido con expresiones regulares. ver: stackoverflow.com/questions/590747/…
Kache
78

La respuesta correcta es no hacer eso, use el paquete de agilidad HTML .

Editado para agregar:

Para robar descaradamente el comentario a continuación de jesse, y para evitar ser acusado de responder inadecuadamente la pregunta después de todo este tiempo, aquí hay un fragmento simple y confiable que usa el paquete de agilidad HTML que funciona incluso con los fragmentos de HTML caprichosos más imperfectamente formados:

HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(Properties.Resources.HtmlContents);
var text = doc.DocumentNode.SelectNodes("//body//text()").Select(node => node.InnerText);
StringBuilder output = new StringBuilder();
foreach (string line in text)
{
   output.AppendLine(line);
}
string textOnly = HttpUtility.HtmlDecode(output.ToString());

Hay muy pocos casos defendibles para usar una expresión regular para analizar HTML, ya que HTML no se puede analizar correctamente sin una conciencia de contexto que es muy dolorosa de proporcionar incluso en un motor de expresiones regulares no tradicionales. Puede llegar hasta allí con un RegEx, pero deberá hacer verificaciones manuales.

Html Agility Pack puede proporcionarle una solución sólida que reducirá la necesidad de corregir manualmente las aberraciones que pueden resultar del tratamiento ingenuo de HTML como una gramática libre de contexto.

Una expresión regular puede obtener la mayoría de las veces lo que desea, pero fallará en casos muy comunes. Si puede encontrar un analizador mejor / más rápido que HTML Agility Pack, hágalo, pero no someta al mundo a hackers de HTML más rotos.

JasonTrue
fuente
27
HTML Agility Pack no es la respuesta a todo lo relacionado con el trabajo con HTML (por ejemplo, ¿qué pasa si solo desea trabajar con fragmentos del código HTML?).
PropellerHead
77
Funciona bastante bien con fragmentos de HTML, y es la mejor opción para el escenario descrito por el póster original. Un Regex, por otro lado, solo funciona con un HTML idealizado y se romperá con un HTML perfectamente válido, porque la gramática del HTML no es regular. Si él estuviera usando Ruby, todavía habría sugerido nokogiri o hpricot, o beautifulsoup para Python. Es mejor tratar HTML como HTML, no un flujo de texto arbitrario sin gramática.
JasonTrue
1
HTML no es una gramática regular y, por lo tanto, no se puede analizar únicamente con expresiones regulares. Puede usar expresiones regulares para lexing, pero no para analizar. Es realmente así de simple. Los lingüistas habrían acordado esto incluso antes de que HTML existiera.
JasonTrue
20
Esto no es una cuestión de opinión. Una expresión regular puede obtener principalmente lo que desea la mayor parte del tiempo, pero fallará en casos muy comunes. Si puede encontrar un analizador mejor / más rápido que HTML Agility Pack, hágalo, pero no someta al mundo a hackers de HTML más rotos.
JasonTrue
2
No puede identificar correctamente las etiquetas HTML de manera confiable sin analizar HTML. ¿Entiendes toda la gramática para HTML? Vea el truco malvado para acercarse "bastante" que sugieren otras respuestas, y dígame por qué querría tener que mantener eso. Votándome en contra porque un intento rápido de hacky funciona para su entrada de muestra no va a hacer que su solución sea correcta. De vez en cuando he usado expresiones regulares para generar informes a partir de contenido HTML o para corregir alguna referencia de CSS utilizando coincidencias negativas en & gt; para limitar la posibilidad de errores, pero hicimos verificaciones adicionales; No fue un propósito general.
JasonTrue
38

La pregunta es demasiado amplia para ser respondida definitivamente. ¿Estás hablando de eliminar todas las etiquetas de un documento HTML del mundo real, como una página web? Si es así, tendrías que:

  • elimine la declaración <! DOCTYPE o <? xml prolog si existen
  • eliminar todos los comentarios de SGML
  • eliminar todo el elemento HEAD
  • eliminar todos los elementos SCRIPT y STYLE
  • hacer Grabthar-know-what con elementos FORM y TABLE
  • eliminar las etiquetas restantes
  • elimine las secuencias <! [CDATA [y]]> de las secciones CDATA pero deje solo su contenido

Eso está justo en la parte superior de mi cabeza: estoy seguro de que hay más. Una vez que hayas hecho todo eso, terminarás con palabras, oraciones y párrafos juntos en algunos lugares, y grandes pedazos de espacios en blanco inútiles en otros.

Pero, suponiendo que esté trabajando con solo un fragmento y pueda salirse con la simple eliminación de todas las etiquetas, aquí está la expresión regular que usaría:

@"(?></?\w+)(?>(?:[^>'""]+|'[^']*'|""[^""]*"")*)>"

Coincidir cadenas de comillas simples y dobles en sus propias alternativas es suficiente para tratar el problema de los corchetes angulares en los valores de los atributos. No veo ninguna necesidad de hacer coincidir explícitamente los nombres de los atributos y otras cosas dentro de la etiqueta, como lo hace la expresión regular en la respuesta de Ryan; La primera alternativa maneja todo eso.

En caso de que te estés preguntando acerca de esas (?>...)construcciones, son grupos atómicos . Hacen que la expresión regular sea un poco más eficiente, pero lo que es más importante, evitan el retroceso descontrolado, que es algo que siempre debes tener en cuenta cuando mezclas alternancia y cuantificadores anidados como lo he hecho. Realmente no creo que eso sea un problema aquí, pero sé que si no lo menciono, alguien más lo hará. ;-)

Esta expresión regular no es perfecta, por supuesto, pero probablemente sea tan buena como la que alguna vez necesitarás.

Alan Moore
fuente
1
Esta es, de lejos, la mejor respuesta. Responde la pregunta del póster y explica por qué no se debe usar una expresión regular para la tarea dada. Bien hecho.
JWilliams
26
Regex regex = new Regex(@"</?\w+((\s+\w+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)+\s*|\s*)/?>", RegexOptions.Singleline);

Fuente

Ryan Emerle
fuente
18

@JasonTrue es correcto, que la eliminación de etiquetas HTML no debe hacerse a través de expresiones regulares.

Es bastante simple quitar las etiquetas HTML usando HtmlAgilityPack:

public string StripTags(string input) {
    var doc = new HtmlDocument();
    doc.LoadHtml(input ?? "");
    return doc.DocumentNode.InnerText;
}
zzzzBov
fuente
1
Si bien llego un poco tarde, me gustaría mencionar que esto también funciona en xml, como el producido por Word y otros productos de oficina. cualquiera que haya tenido la necesidad de lidiar con Word xml haría bien en ver el uso de esto porque ayuda mucho, especialmente si necesita quitar etiquetas del contenido, que es exactamente para lo que lo necesitaba.
Steve Pettifer
Cuando todo parecía fallar, este simple fragmento de código salvó el día. ¡Gracias!
Ted Krapf
13

Me gustaría hacer eco de la respuesta de Jason, aunque a veces es necesario analizar ingenuamente algunos HTML y extraer el contenido del texto.

Necesitaba hacer esto con un poco de HTML creado por un editor de texto enriquecido, siempre divertido y con juegos.

En este caso, es posible que deba eliminar el contenido de algunas etiquetas, así como solo las etiquetas mismas.

En mi caso y las etiquetas fueron arrojadas a esta mezcla. Alguien puede encontrar mi implementación (muy ligeramente) menos ingenua como un punto de partida útil.

   /// <summary>
    /// Removes all html tags from string and leaves only plain text
    /// Removes content of <xml></xml> and <style></style> tags as aim to get text content not markup /meta data.
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static string HtmlStrip(this string input)
    {
        input = Regex.Replace(input, "<style>(.|\n)*?</style>",string.Empty);
        input = Regex.Replace(input, @"<xml>(.|\n)*?</xml>", string.Empty); // remove all <xml></xml> tags and anything inbetween.  
        return Regex.Replace(input, @"<(.|\n)*?>", string.Empty); // remove any tags but not there content "<p>bob<span> johnson</span></p>" becomes "bob johnson"
    }
CountZero
fuente
1
Además de los problemas obvios de salto de línea de plataforma cruzada, tener un cuantificador no greged es lento cuando el contenido está delimitado. Use cosas como <xml>.*(?!</xml>)</xml>con el RegexOptions.SingleLinemodificador para los dos primeros y <[^>]*>para el último. Los primeros también se pueden combinar mediante una alternancia capturada en el primer nombre de etiqueta y referencias posteriores a ella en la etiqueta de búsqueda negativa y final.
ChrisF
5

pruebe el método de expresión regular en esta URL: http://www.dotnetperls.com/remove-html-tags

/// <summary>
/// Remove HTML from string with Regex.
/// </summary>
public static string StripTagsRegex(string source)
{
return Regex.Replace(source, "<.*?>", string.Empty);
}

/// <summary>
/// Compiled regular expression for performance.
/// </summary>
static Regex _htmlRegex = new Regex("<.*?>", RegexOptions.Compiled);

/// <summary>
/// Remove HTML from string with compiled Regex.
/// </summary>
public static string StripTagsRegexCompiled(string source)
{
return _htmlRegex.Replace(source, string.Empty);
}
Owidat
fuente
3

utilizar este..

@"(?></?\w+)(?>(?:[^>'""]+|'[^']*'|""[^""]*"")*)>"
Swaroop
fuente
-1

Use este método para eliminar etiquetas:

public string From_To(string text, string from, string to)
{
    if (text == null)
        return null;
    string pattern = @"" + from + ".*?" + to;
    Regex rx = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
    MatchCollection matches = rx.Matches(text);
    return matches.Count <= 0 ? text : matches.Cast<Match>().Where(match => !string.IsNullOrEmpty(match.Value)).Aggregate(text, (current, match) => current.Replace(match.Value, ""));
}
AnisNoorAli
fuente