2008-11-20 36 views
33

Esto es para .NET. IgnoreCase está configurado y MultiLine NO está configurado.¿Cómo puedo filtrar todas las etiquetas HTML, excepto una determinada lista blanca?

Normalmente estoy decente en expresiones regulares, tal vez estoy quedando sin cafeína ...

Los usuarios se les permite entrar codificados en HTML entidades (< lt ;, < amp ;, etc.), y usar las siguientes etiquetas HTML:

u, i, b, h3, h4, br, a, img 

de cierre automático < br/> y no se permiten < img/>, con o sin el espacio extra, pero no son necesarios.

Quiero:

  1. Gaza toda iniciar y finalizar las etiquetas HTML distintas de las enumeradas anteriormente.
  2. eliminar atributos de las etiquetas restantes, excepto anclas pueden tener un href.

Mi patrón de búsqueda (sustituido con una cadena vacía) hasta el momento:

<(?!i|b|h3|h4|a|img|/i|/b|/h3|/h4|/a|/img)[^>]+> 

Este parece ser pelar todos, pero las etiquetas de inicio y fin que quiero, pero hay tres problemas:

  1. tener que incluir la versión de etiqueta final de cada etiqueta permitido es feo.
  2. Los atributos sobreviven. ¿Puede suceder esto en un solo reemplazo?
  3. Etiquetas comenzando con los nombres de las etiquetas permiten deslizarse a través. Por ejemplo, "<abreviatura>" y "<iframe>".

El siguiente patrón sugerido no elimina las etiquetas que no tienen atributos.

</?(?!i|b|h3|h4|a|img)\b[^>]*> 

Como se menciona más adelante, ">" es legal en un valor de atributo, pero es seguro decir que no voy a apoyar eso. Además, no habrá bloques CDATA, etc. de qué preocuparse. Solo un poco de HTML. respuesta

de escapatoria es el mejor hasta ahora, gracias! He aquí su patrón (esperando que el PRE funciona mejor para mí):

static string SanitizeHtml(string html) 
{ 
    string acceptable = "script|link|title"; 
    string stringPattern = @"</?(?(?=" + acceptable + @")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:([""']?).*?\1?)?)*\s*/?>"; 
    return Regex.Replace(html, stringPattern, "sausage"); 
} 

Algunos pequeños retoques creo que todavía se podría hacer a esta respuesta:

  1. Creo que esto podría ser modificado para capturar HTML simple comentarios (aquellos que no contienen etiquetas) agregando "! -" a la variable "aceptable" y haciendo un pequeño cambio al final de la expresión para permitir un "\ s--" posterior opcional.

  2. Creo que esto se rompería si hay múltiples espacios en blanco entre los atributos de caracteres (por ejemplo: HTML con mucho formato con saltos de línea y pestañas entre los atributos).

Editar 2009-07-23: Aquí está la solución final fui con (en VB.NET):

Dim AcceptableTags As String = "i|b|u|sup|sub|ol|ul|li|br|h2|h3|h4|h5|span|div|p|a|img|blockquote" 
Dim WhiteListPattern As String = "</?(?(?=" & AcceptableTags & _ 
     ")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:([""']?).*?\1?)?)*\s*/?>" 
html = Regex.Replace(html, WhiteListPattern, "", RegExOptions.Compiled) 

La advertencia es que el atributo HREF de un etiquetas todavía se restregó que no es ideal

+0

por favor elimine innecesaria [regular] la etiqueta –

+0

¿ha tenido alguna suerte para quitar los atributos? La respuesta de la laguna no parece hacer esto? – russau

Respuesta

26

Aquí hay una función que escribí para esta tarea:

static string SanitizeHtml(string html) 
{ 
    string acceptable = "script|link|title"; 
    string stringPattern = @"</?(?(?=" + acceptable + @")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:(["",']?).*?\1?)?)*\s*/?>"; 
    return Regex.Replace(html, stringPattern, "sausage"); 
} 

Editar: Por alguna razón he publicado una corrección a mi respuesta anterior como una respuesta separada, así que los estoy consolidando aquí.

Explicaré la expresión regular un poco, porque es un poco larga.

La primera parte coincide con un corchete abierto y 0 o 1 barras (en caso de que sea una etiqueta de cierre).

A continuación, verá un constructo si-entonces con una mirada hacia adelante. (? (? = SomeTag) then | else) Estoy comprobando si la siguiente parte de la cadena es una de las etiquetas aceptables. Puede ver que concateno la cadena de expresiones regulares con la variable aceptable, que son los nombres de etiqueta aceptables separados por una barra vertical para que coincida cualquiera de los términos. Si es una coincidencia, puede ver que puse la palabra "notag" porque ninguna etiqueta coincidiría con eso y, si es aceptable, quiero dejarlo solo. De lo contrario, pasaré a la parte else, donde coincido con cualquier nombre de etiqueta [az, AZ, 0-9] +

A continuación, deseo hacer coincidir 0 o más atributos, que supongo que están en la forma attribute = " valor".entonces ahora agrupo esta parte que representa un atributo pero utilizo el signo?: para evitar que este grupo sea capturado por velocidad: (?: \ s [az, AZ, 0-9, -] + =? (?: ([" ?.??","]) \ 1))

Aquí comienzo con el carácter de espacio en blanco que sería entre la etiqueta y los nombres de atributo, entonces coincidir con un nombre de atributo: [az, AZ, 0-9 , -] +

siguiente Coincido con un signo igual, y luego cualquiera de los dos. Agrupo la cita para que sea capturada, y puedo hacer una referencia posterior más tarde \ 1 para que coincida con el mismo tipo de cita. Entre estas dos citas, puede ver que utilizo el período para hacer coincidir cualquier cosa, sin embargo, ¿uso la versión perezosa *? en lugar de la versión codiciosa * para que solo coincida con la próxima cita que terminaría con este valor.

siguiente ponemos un * después de cerrar los grupos con paréntesis para que coincida con múltiples combinaciones attirbute/valor (o ninguna). Por último, combinamos algunos espacios en blanco con \ s, y 0 o 1 barras diagonales en la etiqueta para las etiquetas de cierre automático del estilo xml.

Puedes ver que estoy reemplazando las etiquetas con salchichas, porque tengo hambre, pero podrías reemplazarlas también por cadenas vacías para despejarlas.

+0

lol ... todavía hay una coma en el último rango de caracteres. ¡Gracias por la actualización! Ajusté el código en el OP. – richardtallent

+0

¡Gracias por el código! ¿Este código está actualizado o la coma debe eliminarse de la expresión? – Saber

+0

solo para agregar una nota de advertencia, tuve mi entrada html para esto viniendo de una fuente externa, tenía una etiqueta br no válida "
PeteN

2

Los atributos son el principal problema con el uso de expresiones regulares para intentar trabajar con HTML. Considere la gran cantidad de atributos potenciales, y el hecho de que la mayoría de ellos son opcionales, y también el hecho de que pueden aparecer en cualquier orden, y el hecho de que ">" es un carácter legal en los valores de los atributos entre comillas. Cuando comiences a tratar de tener todo eso en cuenta, la expresión regular que necesitarías para manejarlo todo se volverá rápidamente inmanejable.

Lo que haría en su lugar es utilizar un analizador HTML basado en eventos, o uno que le proporcione un árbol DOM que pueda recorrer.

10

Este es un buen ejemplo de trabajo en el filtrado etiqueta html:

Sanitize HTML

+0

El sitio web RefactorMyCode ha estado inactivo por un tiempo. Creo que ya no está en servicio. – sohtimsso1970

+0

@sohtimsso1970, sí, no me he dado cuenta hasta ahora, aquí está la página web archivada de septiembre de 2010: http://web.archive.org/web/20100901160940/http://refactormycode.com/codes/333-sanitize- html – CMS

+0

Mirando el código, esta es la respuesta más estricta y la mejor de las expresiones regulares que he visto aquí. No puedo ver ningún defecto inmediato, aunque recomendaría no intentar desinfectar HTML con expresiones regulares. –

1

La razón por la que la adición del límite de palabra \ b no funcionó es que no lo puso dentro de la búsqueda hacia delante. Por lo tanto, \ b se intentará después de < donde siempre coincidirá si el < inicia una etiqueta HTML.

ponerlo dentro de la búsqueda hacia delante como esto:

<(?!/?(i|b|h3|h4|a|img)\b)[^>]+> 

Esto también muestra cómo se puede poner el/antes de la lista de etiquetas, en lugar de con cada etiqueta.

0

Creo que originalmente tenía la intención de hacer que los valores fueran opcionales, pero no los cumplí, ya que puedo ver que agregué un ? después del signo igual y agrupé la parte del valor de la coincidencia. Agreguemos un ? después de ese grupo (marcado con un carot) para que también sea opcional en el partido. No estoy en mi compilador en este momento, pero a ver si esto funciona:

@"</?(?(?=" + acceptable + @")notag|[a-z,A-Z,0-9]+)(?:\s[a-z,A-Z,0-9,\-]+=?(?:(["",']?).*?\1?)?)*\s*/?>"; 
                          ^
+0

Igual que mi comentario sobre la respuesta aceptada: no seguro, fácilmente pasado por alto. –

2

Acabo de notar que la solución actual permite que las etiquetas comiencen con cualquiera de las etiquetas aceptables. Por lo tanto, si "b" es una etiqueta aceptable, "blink" también lo es. No es un gran problema, pero algo a considerar si eres estricto sobre cómo filtra HTML. Ciertamente no querrá permitir "s" como una etiqueta aceptable, ya que permitiría "script".

1
/// <summary> 
    /// Trims the ignoring spacified tags 
    /// </summary> 
    /// <param name="text">the text from which html is to be removed</param> 
    /// <param name="isRemoveScript">specify if you want to remove scripts</param> 
    /// <param name="ignorableTags">specify the tags that are to be ignored while stripping</param> 
    /// <returns>Stripped Text</returns> 
    public static string StripHtml(string text, bool isRemoveScript, params string[] ignorableTags) 
    { 
     if (!string.IsNullOrEmpty(text)) 
     { 
      text = text.Replace("&lt;", "<"); 
      text = text.Replace("&gt;", ">"); 
      string ignorePattern = null; 

      if (isRemoveScript) 
      { 
       text = Regex.Replace(text, "<script[^<]*</script>", string.Empty, RegexOptions.IgnoreCase); 
      } 
      if (!ignorableTags.Contains("style")) 
      { 
       text = Regex.Replace(text, "<style[^<]*</style>", string.Empty, RegexOptions.IgnoreCase); 
      } 
      foreach (string tag in ignorableTags) 
      { 
       //the character b spoils the regex so replace it with strong 
       if (tag.Equals("b")) 
       { 
        text = text.Replace("<b>", "<strong>"); 
        text = text.Replace("</b>", "</strong>"); 
        if (ignorableTags.Contains("strong")) 
        { 
         ignorePattern = string.Format("{0}(?!strong)(?!/strong)", ignorePattern); 
        } 
       } 
       else 
       { 
        //Create ignore pattern fo the tags to ignore 
        ignorePattern = string.Format("{0}(?!{1})(?!/{1})", ignorePattern, tag); 
       } 

      } 
      //finally add the ignore pattern into regex <[^<]*> which is used to match all html tags 
      ignorePattern = string.Format(@"<{0}[^<]*>", ignorePattern); 
      text = Regex.Replace(text, ignorePattern, "", RegexOptions.IgnoreCase); 
     } 

     return text; 
    } 
+0

¿Podría agregar alguna explicación sobre por qué y cómo responde esto a la pregunta? –

+0

por favor escriba al menos una breve explicación de su código. –

+0

Esta solución realmente funcionó para lo que necesitaba. Necesito quitar todo el html a excepción de las etiquetas (de enlace) ...string [] ignorableTags = {"a"}; StripHtml (mytextwithlinks, true, ignorableTags); – Tim

Cuestiones relacionadas