html - pattern - validate regex javascript




RegEx coinciden con las etiquetas abiertas excepto las etiquetas autocontenidas XHTML (20)

Creo que la falla aquí es que HTML es una gramática de tipo 2 de Chomsky (gramática sin contexto) y RegEx es una gramática de tipo 3 de Chomsky (gramática regular) . Dado que una gramática de Tipo 2 es fundamentalmente más compleja que una gramática de Tipo 3 (consulte la jerarquía de Chomsky ), posiblemente no pueda hacer que esto funcione. Pero muchos lo intentarán, algunos reclamarán el éxito y otros encontrarán la falla y lo arruinarán totalmente.

Necesito hacer coincidir todas estas etiquetas de apertura:

<p>
<a href="foo">

Pero no estos:

<br />
<hr class="foo" />

Se me ocurrió esto y quería asegurarme de que lo tengo bien. Solo estoy capturando el az .

<([a-z]+) *[^/]*?>

Creo que dice:

  • Encuentra un menor que, entonces
  • Encuentra (y captura) az una o más veces, luego
  • Encuentra cero o más espacios, entonces
  • Encuentra cualquier carácter cero o más veces, codicioso, excepto / , luego
  • Encontrar un mayor que

¿Tengo ese derecho? Y lo más importante, ¿qué te parece?


En shell, puedes analizar HTML usando:

  • aunque sed

    1. Turing.sed
    2. Escribir HTML analizador (tarea)
    3. ???
    4. ¡Lucro!
  • hxselect del hxselect html-xml-utils

  • vim / ex (que puede saltar fácilmente entre etiquetas html ), por ejemplo:

    • Eliminar etiqueta de estilo con código interno:

      $ curl -s http://example.com/ | ex -s +'/<style.*/norm nvatd' +%p -cq! /dev/stdin
      
  • grep , por ejemplo:

    • extrayendo html externo de H1:

      $ curl -s http://example.com/ | grep -o '<h1>.*</h1>'
      <h1>Example Domain</h1>
      
    • extrayendo el cuerpo:

      $ curl -s http://example.com/ | tr '\n' ' ' | grep -o '<body>.*</body>'
      <body> <div> <h1>Example Domain</h1> ...
      
  • html2text para el análisis de texto sin formato:

    • como tablas de análisis :

      $ html2text foo.txt | column -ts'|'
      
  • usando xpath ( XML::XPath módulo perl), vea el ejemplo aquí

  • Perl o Python (ver ejemplo de @Gilles )

  • para analizar varios archivos a la vez, consulte: ¿Cómo analizar cientos de archivos de código fuente HTML en shell?

Relacionados (por qué no deberías usar la expresión regular):


Hay personas que te dirán que la Tierra es redonda (o quizás que la Tierra es un esferoide oblato si quieren usar palabras extrañas). Están mintiendo.

Hay personas que te dirán que las expresiones regulares no deben ser recursivas. Ellos te están limitando. Necesitan subyugarte, y lo hacen manteniéndote en la ignorancia.

Puedes vivir en su realidad o tomar la píldora roja.

Al igual que Lord Marshal (¿es un pariente de la clase Marshal .NET?), He visto el Regex-Verse basado en la pila Underversa y regresé con poderes que no puedes imaginar. Sí, creo que había un Viejo o dos que los protegían, pero estaban viendo fútbol en la televisión, así que no fue difícil.

Creo que el caso XML es bastante simple. El RegEx (en la sintaxis de .NET), desinflado y codificado en base64 para facilitar su comprensión por parte de su débil mente, debería ser algo como esto:

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
+r/vD34mUADO1P4/AQAA//8=

Las opciones para establecer es RegexOptions.ExplicitCapture . El grupo de captura que está buscando es ELEMENTNAME . Si el ERROR grupo de captura no está vacío, hubo un error de análisis y el Regex se detuvo.

Si tiene problemas para reconvertirlo a una expresión regular legible para humanos, esto debería ayudar:

static string FromBase64(string str)
{
    byte[] byteArray = Convert.FromBase64String(str);

    using (var msIn = new MemoryStream(byteArray))
    using (var msOut = new MemoryStream()) {
        using (var ds = new DeflateStream(msIn, CompressionMode.Decompress)) {
            ds.CopyTo(msOut);
        }

        return Encoding.UTF8.GetString(msOut.ToArray());
    }
}

Si no está seguro, no, no estoy bromeando (pero quizás esté mintiendo). Funcionará. He construido toneladas de pruebas unitarias para probarlo, e incluso he usado (parte de) las pruebas de conformidad . Es un tokenizador, no un analizador completo, por lo que solo dividirá el XML en sus componentes. No analizará / integrará las DTD.

Oh ... si quieres el código fuente de la expresión regular, con algunos métodos auxiliares:

regex para tokenizar un xml o el regex llano completo


No escuches a estos chicos. Puede analizar totalmente las gramáticas libres de contexto con expresiones regulares si divide la tarea en partes más pequeñas. Puede generar el patrón correcto con un script que haga cada uno de estos en orden:

  1. Resuelve el problema de la detención.
  2. Cuadrar un círculo.
  3. Calcule el problema del vendedor ambulante en O (log n) o menos. Si es más que eso, te quedarás sin memoria RAM y el motor se bloqueará.
  4. El patrón será bastante grande, así que asegúrate de tener un algoritmo que comprima los datos aleatorios sin pérdidas.
  5. Casi allí, simplemente divide todo por cero. Pan comido.

No he terminado la última parte, pero sé que me estoy acercando. Sigue lanzando CthulhuRlyehWgahnaglFhtagnException s por alguna razón, así que lo CthulhuRlyehWgahnaglFhtagnException a VB 6 y lo CthulhuRlyehWgahnaglFhtagnException On Error Resume Next . Actualizaré con el código una vez que investigue esta extraña puerta que se acaba de abrir en la pared. Hmm

El PS Pierre de Fermat también descubrió cómo hacerlo, pero el margen que estaba escribiendo no era lo suficientemente grande para el código.


Si bien es cierto que pedir expresiones regulares para analizar HTML arbitrario es como pedirle a un principiante que escriba un sistema operativo, a veces es apropiado analizar un conjunto de HTML conocido y limitado .

Si tiene un pequeño conjunto de páginas HTML de las que desea raspar los datos y luego rellenarlos en una base de datos, las expresiones regulares pueden funcionar bien. Por ejemplo, recientemente quise obtener los nombres, partidos y distritos de los Representantes federales de Australia, que obtuve del sitio web del Parlamento. Este fue un trabajo limitado, de una sola vez.

Los Regexes funcionaron bien para mí y fueron muy rápidos de configurar.


Si bien las respuestas que no puede analizar HTML con expresiones regulares son correctas, no se aplican aquí. El OP solo quiere analizar una etiqueta HTML con expresiones regulares, y eso es algo que se puede hacer con una expresión regular.

La expresión regular sugerida es incorrecta, sin embargo:

<([a-z]+) *[^/]*?>

Si agrega algo a la expresión regular, al realizar el seguimiento se puede forzar para que coincida con cosas tontas como <a >> , [^/] es demasiado permisivo. También tenga en cuenta que <space>*[^/]* es redundante, porque [^/]* también puede hacer coincidir espacios.

Mi sugerencia seria

<([a-z]+)[^>]*(?<!/)>

Donde (?<! ... ) es (en las expresiones regulares de Perl) el look-behind negativo. Lee "a <, luego una palabra, luego cualquier cosa que no sea una>, la última de las cuales puede no ser una /, seguida de>".

Tenga en cuenta que esto permite cosas como <a/ > (al igual que la expresión regular original), por lo que si desea algo más restrictivo, debe crear una expresión regular para hacer coincidir los pares de atributos separados por espacios.


Tratar:

<([^\s]+)(\s[^>]*?)?(?<!/)>

Es similar al tuyo, pero el último >no debe ser después de una barra, y también acepta h1.


Descargo de responsabilidad : use un analizador si tiene la opción. Dicho esto ...

Esta es la expresión regular que uso (!) Para hacer coincidir las etiquetas HTML:

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>

Puede que no sea perfecto, pero corrí este código a través de un montón de HTML. Tenga en cuenta que incluso <a name="badgenerator""> cosas extrañas como <a name="badgenerator""> , que aparecen en la web.

Supongo que para que no coincida con las etiquetas autocontenidas, querrías usar el look-behind negativo de Kobi :

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

o simplemente combinar si y si no.

Para downvoters: Este es un código de trabajo de un producto real. Dudo que alguien que lea esta página tenga la impresión de que es socialmente aceptable usar expresiones regulares en HTML.

Advertencia : debo tener en cuenta que esta expresión regular aún se descompone en presencia de bloques CDATA, comentarios, elementos de estilo de comandos y estilo de escritura. La buena noticia es que puede deshacerse de aquellos que usan un regex ...


Aunque no es adecuado y efectivo usar expresiones regulares para ese propósito, a veces las expresiones regulares brindan soluciones rápidas para problemas de coincidencia simples y, en mi opinión, no es tan horrible utilizar expresiones regulares para trabajos triviales.

Hay una publicación de blog definitiva sobre cómo combinar elementos internos de HTML escritos por Steven Levithan.


Me parece que estás intentando hacer coincidir las etiquetas sin una "/" al final. Prueba esto:

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>

Si simplemente está tratando de encontrar esas etiquetas (sin ambiciones de análisis) intente esta expresión regular:

/<[^/]*?>/g

Lo escribí en 30 segundos y lo probé aquí: http://gskinner.com/RegExr/

Coincide con los tipos de etiquetas que mencionaste, mientras que ignora los tipos que dijiste que querías ignorar.


Sun Tzu, un antiguo estratega, general y filósofo chino, dijo:

Se dice que si conoces a tus enemigos y te conoces a ti mismo, puedes ganar cien batallas sin una sola pérdida. Si solo te conoces a ti mismo, pero no a tu oponente, puedes ganar o perder. Si no te conoces a ti mismo ni a tu enemigo, siempre te pondrás en peligro.

En este caso, su enemigo es HTML y usted es uno mismo o regex. Incluso podría ser Perl con expresiones regulares irregulares. Saber HTML Conocete a ti mismo.

He compuesto un haiku que describe la naturaleza de HTML.

HTML has
complexity exceeding
regular language.

También he compuesto un haiku que describe la naturaleza de la expresión regular en Perl.

The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>

Como muchas personas ya han señalado, el HTML no es un lenguaje regular, lo que puede dificultar el análisis. Mi solución a esto es convertirlo en un lenguaje regular usando un programa ordenado y luego usar un analizador XML para consumir los resultados. Hay muchas buenas opciones para esto. Mi programa se escribe usando Java con la biblioteca jtidy para convertir el HTML en XML y luego Jaxen en xpath en el resultado.


Cuando necesito extraer rápidamente algo de un documento HTML, uso Tidy para convertirlo a XML y luego uso XPath o XSLT para obtener lo que necesito. En tu caso, algo como esto:

//p/a[@href='foo']

Hay algunos regexes agradables para reemplazar HTML con BBCode here . Para todos los que no lo son, tenga en cuenta que no está intentando analizar HTML completamente, solo para desinfectarlo. Probablemente pueda permitirse matar etiquetas que su simple "analizador" no puede entender.

Por ejemplo:

$store =~ s/http:/http:\/\//gi;
$store =~ s/https:/https:\/\//gi;
$baseurl = $store;

if (!$query->param("ascii")) {
    $html =~ s/\s\s+/\n/gi;
    $html =~ s/<pre(.*?)>(.*?)<\/pre>/\[code]$2\[\/code]/sgmi;
}

$html =~ s/\n//gi;
$html =~ s/\r\r//gi;
$html =~ s/$baseurl//gi;
$html =~ s/<h[1-7](.*?)>(.*?)<\/h[1-7]>/\n\[b]$2\[\/b]\n/sgmi;
$html =~ s/<p>/\n\n/gi;
$html =~ s/<br(.*?)>/\n/gi;
$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]$2\[\/code]/sgmi;
$html =~ s/<b>(.*?)<\/b>/\[b]$1\[\/b]/gi;
$html =~ s/<i>(.*?)<\/i>/\[i]$1\[\/i]/gi;
$html =~ s/<u>(.*?)<\/u>/\[u]$1\[\/u]/gi;
$html =~ s/<em>(.*?)<\/em>/\[i]$1\[\/i]/gi;
$html =~ s/<strong>(.*?)<\/strong>/\[b]$1\[\/b]/gi;
$html =~ s/<cite>(.*?)<\/cite>/\[i]$1\[\/i]/gi;
$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<link(.*?)>//gi;
$html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]$2/gi;
$html =~ s/<ul(.*?)>/\[list]/gi;
$html =~ s/<\/ul>/\[\/list]/gi;
$html =~ s/<div>/\n/gi;
$html =~ s/<\/div>/\n/gi;
$html =~ s/<td(.*?)>/ /gi;
$html =~ s/<tr(.*?)>/\n/gi;

$html =~ s/<img(.*?)src="(.*?)"(.*?)>/\[img]$baseurl\/$2\[\/img]/gi;
$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/$2]$4\[\/url]/gi;
$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/$1]$2\[\/url]/gi;
$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/$1\[\/img]/gi;

$html =~ s/<head>(.*?)<\/head>//sgmi;
$html =~ s/<object>(.*?)<\/object>//sgmi;
$html =~ s/<script(.*?)>(.*?)<\/script>//sgmi;
$html =~ s/<style(.*?)>(.*?)<\/style>//sgmi;
$html =~ s/<title>(.*?)<\/title>//sgmi;
$html =~ s/<!--(.*?)-->/\n/sgmi;

$html =~ s/\/\//\//gi;
$html =~ s/http:\//http:\/\//gi;
$html =~ s/https:\//https:\/\//gi;

$html =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gsi;
$html =~ s/\r\r//gi;
$html =~ s/\[img]\//\[img]/gi;
$html =~ s/\[url=\//\[url=/gi;

He utilizado una herramienta de código abierto llamada HTMLParser antes. Está diseñado para analizar HTML de varias maneras y cumple su cometido bastante bien. Puede analizar HTML como treenode diferente y puede usar fácilmente su API para obtener atributos fuera del nodo. Compruébelo y vea si esto puede ayudarle.


No sé su necesidad exacta para esto, pero si también está utilizando .NET, ¿no podría usar Html Agility Pack ?

Extracto:

Es una biblioteca de códigos .NET que le permite analizar archivos HTML "fuera de la web". El analizador es muy tolerante con el HTML del "mundo real" mal formado.


Quieres que el primero >no vaya precedido por un /. Busque here detalles sobre cómo hacer eso. Se conoce como aspecto negativo por detrás.

Sin embargo, una implementación ingenua de eso terminará coincidiendo <bar/></foo>en este documento de ejemplo

<foo><bar/></foo>

¿Puede proporcionar un poco más de información sobre el problema que está tratando de resolver? ¿Estás iterando a través de etiquetas programáticamente?


Sobre la pregunta de los métodos RegExp para analizar (x) HTML, la respuesta a todos los que hablaron sobre algunos límites es: no has sido entrenado lo suficiente como para gobernar la fuerza de esta poderosa arma, ya que NADIE aquí habló sobre la recursión .

Un colega de RegExp-agnostic me notificó esta discusión, que ciertamente no es la primera en la web sobre este tema antiguo y candente.

Después de leer algunas publicaciones, lo primero que hice fue buscar la cadena "? R" en este hilo. El segundo fue buscar sobre "recursión".
No, vaca sagrada, no se encontró ninguna coincidencia.
Como nadie mencionó el mecanismo principal en el que se basa el analizador, pronto me di cuenta de que nadie entendía el problema.

Si un analizador (x) HTML necesita recursión, un analizador RegExp sin recursión no es suficiente para este propósito. Es una construcción simple.

El arte negro de RegExp es difícil de dominar , así que tal vez haya otras posibilidades que dejamos de lado al probar y probar nuestra solución personal para capturar toda la web con una mano ... Bueno, estoy seguro de ello :)

Aquí está el patrón mágico:

$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";

Solo inténtalo.
Está escrito como una cadena PHP, por lo que el modificador "s" hace que las clases incluyan líneas nuevas.
Aquí hay una nota de muestra en el manual de PHP que escribí en enero: Reference

(Tenga cuidado, en esa nota usé incorrectamente el modificador "m"; debería borrarse, a pesar de que el motor RegExp lo descarta, ya que no se usó ancla ^ o $).

Ahora, podríamos hablar sobre los límites de este método desde un punto de vista más informado:

  1. De acuerdo con la implementación específica del motor RegExp, la recursión puede tener un límite en el número de patrones anidados analizados , pero depende del idioma utilizado.
  2. Aunque el código HTML dañado (x) no genera errores graves, no está saneado .

De todos modos, es solo un patrón RegExp, pero revela la posibilidad de desarrollar muchas implementaciones poderosas.
Escribí este patrón para alimentar el analizador de descenso recursivo de un motor de plantillas que incorporé en mi marco, y los rendimientos son realmente excelentes, tanto en tiempos de ejecución como en uso de memoria (nada que ver con otros motores de plantillas que usan la misma sintaxis).


<?php
$selfClosing = explode(',', 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed');

$html = '
<p><a href="#">foo</a></p>
<hr/>
<br/>
<div>name</div>';

$dom = new DOMDocument();
$dom->loadHTML($html);
$els = $dom->getElementsByTagName('*');
foreach ( $els as $el ) {
    $nodeName = strtolower($el->nodeName);
    if ( !in_array( $nodeName, $selfClosing ) ) {
        var_dump( $nodeName );
    }
}

Salida:

string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"

Básicamente, simplemente defina los nombres de los nodos de elementos que se cierran automáticamente, cargue toda la cadena html en una biblioteca DOM, capture todos los elementos, recorra y filtre los que no se cierran automáticamente y opere sobre ellos.

Estoy seguro de que ya sabes que no deberías usar expresiones regulares para este propósito.





xhtml