posicionamiento para metadatos etiquetas codigo buscadores c# html vb.net regex

c# - para - ¿Cómo filtro todas las etiquetas HTML excepto una cierta lista blanca?



seo html (7)

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, publiqué una corrección en 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 cerrada).

A continuación, verá una construcción si-luego 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, quiero hacer coincidir 0 o más atributos, que supongo que están en la forma attribute = "value". 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í empiezo con el carácter de espacios en blanco que estaría entre la etiqueta y los nombres de los atributos, luego hago coincidir el nombre de un atributo: [az, AZ, 0-9, -] +

A continuación, comparo un signo igual y luego cito. 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.

luego colocamos un * después de cerrar los grupos con paréntesis para que coincida con múltiples combinaciones de attirbute / valor (o ninguno). 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 por cadenas vacías también para despejarlas.

Esto es para .NET. IgnoreCase está configurado y MultiLine NO está configurado.

Usualmente soy decente en Regex, tal vez me estoy quedando sin cafeína ...

Los usuarios pueden ingresar entidades codificadas en HTML (<lt ;, <amp ;, etc.) y utilizar las siguientes etiquetas HTML:

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

El cierre automático <br/> y <img /> están permitidos, con o sin espacio adicional, pero no son obligatorios.

Quiero:

  1. Elimine todas las etiquetas HTML de inicio y finalización distintas de las enumeradas anteriormente.
  2. Elimine los atributos de las etiquetas restantes, excepto que los anclajes pueden tener un href.

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

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

Esto parece quitar todas las etiquetas de inicio y final que quiero, pero hay tres problemas:

  1. Tener que incluir la etiqueta final de cada etiqueta permitida es feo.
  2. Los atributos sobreviven. ¿Puede suceder esto en un solo reemplazo?
  3. Etiquetas que comienzan con los nombres de las etiquetas permitidas. Por ejemplo, "<abbrev>" 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 a continuación, ">" es legal en un valor de atributo, pero es seguro decir que no apoyaré eso. Además, no habrá bloques CDATA, etc. de qué preocuparse. Solo un poco de HTML.

La respuesta de Loophole es la mejor hasta ahora, ¡gracias! Aquí está su patrón (esperando que el PRE funcione 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 aún podrían hacerse a esta respuesta:

  1. Creo que esto podría modificarse para capturar comentarios HTML simples (aquellos que no contienen etiquetas) al agregar "! -" a la variable "aceptable" y hacer un pequeño cambio al final de la expresión para permitir un seguimiento opcional "/ s--".

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

Editar 2009-07-23: Aquí está la solución final que fui (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 las etiquetas A todavía se restriega, lo que no es ideal.


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 de igual y agrupado la porción de valor de la coincidencia. Vamos a agregar un ? después de ese grupo (marcado con un carot) para hacerlo opcional en el partido también. No estoy en mi compilador en este momento, pero mira si esto funciona:

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


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

Sanitize HTML


La razón por la que agregar la palabra límite / b no funcionó es que no la incluyó dentro de la búsqueda anticipada. Por lo tanto, / b se intentará después de <donde siempre coincidirá si <comienza una etiqueta HTML.

Ponlo dentro de la expectativa de esta manera:

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

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


Los atributos son el principal problema con el uso de expresiones regulares para tratar de 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.


Me di cuenta de que la solución actual permite etiquetas que comienzan 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".


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