Las expresiones regulares coinciden con las etiquetas abiertas, excepto las etiquetas autocontenidas XHTML

1812

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 hacerlo bien. Solo estoy capturando el a-z.

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

Creo que dice:

  • Encuentra un menor que, luego
  • Encuentre (y capture) az una o más veces, luego
  • Encuentra cero o más espacios, luego
  • Encuentra cualquier personaje cero o más veces, codicioso, excepto /, entonces
  • Encuentra un mayor que

¿Tengo ese derecho? Y lo que es más importante, ¿qué opinas?

0
4412

No puede analizar [X] HTML con expresiones regulares. Porque HTML no puede ser analizado por expresiones regulares. Regex no es una herramienta que se pueda utilizar para analizar HTML correctamente. Como he respondido tantas veces antes en preguntas HTML y expresiones regulares aquí, el uso de expresiones regulares no le permitirá consumir HTML. Las expresiones regulares son una herramienta que no es lo suficientemente sofisticada para comprender las construcciones empleadas por HTML. HTML no es un lenguaje regular y, por lo tanto, no se puede analizar mediante expresiones regulares. Las consultas de expresiones regulares no están equipadas para dividir HTML en sus partes significativas. tantas veces, pero no me está afectando. Incluso las expresiones regulares irregulares mejoradas tal como las usa Perl no están a la altura de la tarea de analizar HTML. Nunca me harás quebrar. HTML es un lenguaje de complejidad suficiente que no puede ser analizado por expresiones regulares.Incluso Jon Skeet no puede analizar HTML usando expresiones regulares. Cada vez que intenta analizar HTML con expresiones regulares, el niño impío llora la sangre de vírgenes y los piratas informáticos rusos aprovechan su aplicación web. Analizar HTML con expresiones regulares invoca almas contaminadas al reino de los vivos. HTML y regex van de la mano como el amor, el matrimonio y el infanticidio ritual. El <center> no puede contenerlo es demasiado tarde. La fuerza de las expresiones regulares y el HTML juntos en el mismo espacio conceptual destruirá tu mente como una masilla acuosa. Si analiza HTML con expresiones regulares, está cediendo ante Ellos y sus formas blasfemas que nos condenan a todos a un trabajo inhumano por Aquel cuyo Nombre no se puede expresar en el Plano Multilingüe Básico, él viene. HTML-plus-regexp licuará los nervios del sensible mientras observa, su psique se marchita en el ataque del horror.Los analizadores HTML basados ​​en Rege̿̔̉x son el cáncer que está matando a StackOverflowes demasiado tarde, es demasiado tarde, no podemos salvarnos la transgresión de un niño asegura que la expresión regular consumirá todo el tejido vivo (excepto HTML que no puede, como se profetizó anteriormente) querido señor, ayúdanos, ¿cómo puede alguien sobrevivir a este flagelo usando expresiones regulares para analizar? HTML ha condenado a la humanidad a una eternidad de agujeros de tortura y de seguridad terribles utilizando Rege x como una herramienta para HTML proceso establece una bebida en ch entre este mundo y el reino temor de entidades corruptos (como entidades SGML, pero más corrupto) un mero Glimp se de el mundo de la reg ex analizadores de HTML ins tantly transporte ap conciencia de rogrammer i nto aw ORL d incesante de gritar, que viene, El pestilente sl ithy expresiones regulares infección Wil l devoran HT analizador ML, la aplicación y la existencia de todos los tiempos como Visual Basic sólo peor venga, com es hacer no fi lucha h e viene, HI s UNHOLY Resplandor de stro҉ying toda la iluminación, las etiquetas HTML se escapa de ur yo ojos como líq uido p ain, el canto de regulares exp re análisis de fisión se eXTI nguish las voces de mor hombre Tal desde el sp aquí puedo ver que se puede ver TI es hermoso t él f inal snufFing o f la mentira es del hombre que todo está perdido A LL I SLOST XX e Pony que venga s que com es él co me s t él ich o permeat es al l MI FAC E MI CARA ᵒh dios N o NO NOO O EN Θ parada t que un ̶͑̾̾ * GL eS ͎a̧͈͖r̽̾̈́͒͑en ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ


¿Ha intentado utilizar un analizador XML en su lugar?


Moderator's Note

This post is locked to prevent inappropriate edits to its content. The post looks exactly as it is supposed to look - there are no problems with its content. Please do not flag it for our attention.

3
  • 179
    Kobi: Creo que es hora de que deje el puesto de Asistente para no analizar HTML con Regex Officer. No importa cuántas veces lo digamos, no dejarán de venir todos los días ... incluso cada hora. Es una causa perdida, por la que alguien más puede luchar un poco. Así que continúa, analiza HTML con expresiones regulares, si es necesario. Es solo un código roto, no de vida o muerte. bobince 13 de nov. De 2009 a las 23:18
  • 27
    ¿Es posible usar RegEx para analizar esta respuesta? Chris Porter 17 de nov. De 2009 a las 18:26
  • 2
    Si no puede ver esta publicación, aquí hay una captura de pantalla en todo su esplendor: imgur.com/gOPS2.pngAndrew Keeton 19 de nov. De 2009 a las 14:37
3400
+50

Si bien el HTML arbitrario con solo una expresión regular es imposible, a veces es apropiado usarlo para analizar un conjunto de HTML conocido y limitado .

Si tiene un pequeño conjunto de páginas HTML de las que desea extraer datos y luego guardarlos 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 australianos, que obtuve del sitio web del Parlamento. Este era un trabajo único y limitado.

Las expresiones regulares me funcionaron bien y fueron muy rápidas de configurar.

31
  • 142
    Además, extraer datos formateados con bastante regularidad de documentos grandes será MUCHO más rápido con un uso juicioso de escaneo y expresiones regulares que cualquier analizador genérico. Y si se siente cómodo codificando expresiones regulares, codifique mucho más rápido que codificando xpaths. Y es casi seguro que sea menos frágil a los cambios en lo que está raspando. Tan bleh. Michael Johnston 17/04/12 a las 20:47
  • 287
    @MichaelJohnston ¿"Menos frágil"? Es casi seguro que no. Las expresiones regulares se preocupan por los detalles del formato de texto que un analizador XML puede ignorar silenciosamente. ¿Cambiar entre &foo;codificaciones y CDATAsecciones? ¿Utiliza un minificador de HTML para eliminar todos los espacios en blanco de su documento que el navegador no muestra? A un analizador XML no le importará, ni tampoco una declaración XPath bien escrita. Un "analizador" basado en expresiones regulares, por otro lado ...Charles Duffy 11/07/12 a las 16:03
  • 42
    @CharlesDuffy para un trabajo de una sola vez está bien, y para los espacios usamos \ s +quantum 12/07/12 a las 13:50
  • 73
    @xiaomao de hecho, si tener que conocer todas las trampas y soluciones para obtener una solución al 80% que falla el resto del tiempo "funciona para ti", no puedo detenerte. Mientras tanto, estoy en mi lado de la cerca usando analizadores que funcionan en el 100% de XML sintácticamente válido. Charles Duffy 12/07/12 a las 16:07
  • 405
    Una vez tuve que extraer algunos datos de ~ 10k páginas, todas con la misma plantilla HTML. Estaban plagados de errores HTML que causaban que los analizadores se atascaran, y todo su estilo estaba en línea o con <font>etc .: sin clases o ID para ayudar a navegar el DOM. Después de luchar todo el día con el enfoque "correcto", finalmente cambié a una solución de expresiones regulares y la tuve funcionando en una hora. Paul A Jungwirth 7 de septiembre de 2012 a las 7:14
2161

Creo que el defecto aquí es que HTML es una gramática Chomsky Tipo 2 (gramática libre de contexto) y una expresión regular es una gramática Chomsky Tipo 3 (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 ), es matemáticamente imposible analizar XML con una expresión regular.

Pero muchos lo intentarán, y algunos incluso afirmarán haber tenido éxito, pero hasta que otros encuentren la falla y lo arruinen por completo.

12
  • 247
    El OP solicita analizar un subconjunto muy limitado de XHTML: etiquetas de inicio. Lo que hace que (X) HTML sea un CFG es su potencial para tener elementos entre las etiquetas de inicio y final de otros elementos (como en una regla gramatical A -> s A e). (X) HTML no tiene esta propiedad dentro de una etiqueta de inicio: una etiqueta de inicio no puede contener otras etiquetas de inicio. El subconjunto que el OP está tratando de analizar no es un CFG. LarsH 2 de marzo de 2012 a las 8:43
  • 111
    En la teoría de CS, los lenguajes regulares son un subconjunto estricto de lenguajes libres de contexto, pero las implementaciones de expresiones regulares en los lenguajes de programación convencionales son más poderosas. Como describe noulakaz.net/weblog/2007/03/18/… , las llamadas "expresiones regulares" pueden buscar números primos en unario, lo que ciertamente es algo que una expresión regular de la teoría de CS no puede lograr. Adam Mihalcin 19 de marzo de 2012 a las 23:50
  • 15
    @eyelidlessness: el mismo "solo si" se aplica a todos los CFG, ¿no es así? Es decir, si la entrada HTML (X) no está bien formada, ni siquiera un analizador XML completo funcionará de manera confiable. Tal vez si da ejemplos de los "(X) errores de sintaxis HTML implementados en agentes de usuario del mundo real" a los que se refiere, entenderé mejor a qué se refiere. LarsH 22 de mayo de 2012 a las 5:09
  • 88
    @AdamMihalcin tiene toda la razón. La mayoría de los motores de expresiones regulares existentes son más potentes que las gramáticas de Chomsky Tipo 3 (por ejemplo, coincidencias no codiciosas, backrefs). Algunos motores de expresiones regulares (como el de Perl) son Turing completo. Es cierto que incluso esas son herramientas deficientes para analizar HTML, pero este argumento tan citado no es la razón. dubiousjim 31 de mayo de 2012 a las 13:44
  • 28
    Esta es la respuesta más "completa y breve" aquí. Lleva a las personas a aprender los conceptos básicos de las gramáticas y los idiomas formales y, con suerte, algunas matemáticas para que no pierdan el tiempo en cosas desesperadas como resolver tareas NP en tiempo polinomialmishmashru 19/04/2013 a las 12:15
1406

No escuches a estos chicos. Que está en completo puede analizar gramáticas libres de contexto con expresiones regulares si se rompe 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. Resuelva el problema de la detención.
  2. Cuadrar un círculo.
  3. Resuelva el problema del vendedor ambulante en O (log n) o menos. Si es más que eso, se quedará sin RAM y el motor se bloqueará.
  4. El patrón será bastante grande, así que asegúrese de tener un algoritmo que comprima datos aleatorios sin pérdidas.
  5. Ya casi está, solo divida todo por cero. Pan comido.

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

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

21
  • 88
    La división por cero es un problema mucho más fácil que los otros que mencionas. Si usa intervalos, en lugar de aritmética de punto flotante simple (que todos deberían ser pero nadie lo es), felizmente puede dividir algo entre [un intervalo que contenga] cero. El resultado es simplemente un intervalo que contiene más y menos infinito. rjmunro 14/06/12 a las 10:53
  • 161
    El pequeño problema de los márgenes de Fermat se ha resuelto con márgenes suaves en el software moderno de edición de texto. kd4ttc 1 de marzo de 2013 a las 20:24
  • 57
    El pequeño problema de margen de Fermat ha sido resuelto por Randall Munroe estableciendo el tamaño de fuente en cero: xkcd.com/1381heltonbiker 16/10/2014 a las 19:55
  • 36
    Para su información: El problema de Fermat ha hecho sido resuelto en 1995 , y sólo se tarda matemáticos 358 años para hacerlo. jmiserez 22/01/15 a las 18:40
  • 12
    Pude evitar ese paso pegajoso de dividir por cero utilizando en su lugar trinquetes brownianos producidos por la fusión fría ... aunque solo funciona cuando elimino la constante cosmológica. Tim Lehner 9 de marzo de 2016 a las 18:52
1111

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

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

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

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

Supongo que para que no coincida con las etiquetas autocontenidas, querrás usar la búsqueda negativa de Kobi :

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

o simplemente combinar si y si no.

Para votantes negativos: 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 señalar que esta expresión regular todavía se descompone en presencia de bloques CDATA, comentarios y elementos de estilo y script. La buena noticia es que puede deshacerse de los que usan una expresión regular ...

9
  • 109
    Iría con algo que funcione en cosas cuerdas que llorar por no ser universalmente perfecto :-)prajeesh kumar 10 de mayo de 2012 a las 3:44
  • 57
    ¿Alguien está usando CDATA dentro de HTML? Danubian Sailor 2 de marzo de 2013 a las 7:51
  • 21
    por lo que en realidad no resuelve el problema de análisis con expresiones regulares solo, pero como parte del analizador, esto puede funcionar. PD: un producto que funcione no significa un buen código. No te ofendas, pero así es como funciona la programación industrial y obtiene su dineromishmashru 19/04/2013 a las 12:18
  • 37
    Sus arranques de expresiones regulares fallan en el muy corto posible HTML, válida: <!doctype html><title><</title>. Simples '<!doctype html><title><</title>'.match(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g)rendimientos ["<!doctype html>", "<title>", "<</title>"], mientras que deberían ["<title>", "</title>"]. user1180790 1 de mayo de 2014 a las 16:48
  • 2
    si solo estamos tratando de hacer coincidir y no coincidir con los ejemplos dados, /<.([^r>][^>]*)?>/g funciona :-) // javascript: '<p> <a href = "foo"> <br /> <hr class = "foo" />'.match(/<.([^r>][^>]*)?>/g)imma 22 de mayo de 2014 a las 16:14
513

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

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

Puedes vivir en su realidad o tomar la pastilla roja.

Al igual que Lord Marshal (¿es pariente de la clase Marshal .NET?), He visto el Verso Regex basado en la pila de Underverse y he regresado con conocimientos de poderes que no puedes imaginar. Sí, creo que había uno o dos Antiguos protegiéndolos, pero estaban viendo fútbol en la televisión, así que no fue difícil.

Creo que el caso de XML es bastante simple. La expresión regular (en la sintaxis .NET), desinflada y codificada en base64 para que sea más fácil de comprender por su mente débil, debería ser algo como esto:

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

Las opciones para configurar son RegexOptions.ExplicitCapture. El grupo de captura que busca es ELEMENTNAME. Si el grupo de captura ERRORno está vacío, entonces hubo un error de análisis y la expresión regular se detuvo.

Si tiene problemas para reconvertirlo a una expresión regular legible por 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 creado 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 tokens de componentes. No analizará / integrará 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 plano completo

31
  • 72
    Buen Dios, es enorme. Mi mayor pregunta es ¿por qué? Te das cuenta de que todos los lenguajes modernos tienen analizadores XML, ¿verdad? Puedes hacer todo eso en 3 líneas y estar seguro de que funcionará. Además, ¿también te das cuenta de que la expresión regular pura es probablemente incapaz de hacer ciertas cosas? A menos que haya creado un analizador de código imperativo / regex híbrido, pero no parece que lo haya hecho. ¿También puedes comprimir datos aleatorios? Justin Morgan 8 de marzo de 2011 a las 15:23
  • 121
    @Justin No necesito una razón. Se podía hacer (y no era ilegal / inmoral), así que lo he hecho. No hay limitaciones para la mente, excepto las que reconocemos (Napoleon Hill) ... ¿Los lenguajes modernos pueden analizar XML? ¿En serio? ¡Y pensé que ESO era ilegal! :-)xanatos 8 de marzo de 2011 a las 15:31
  • 83
    Señor, estoy convencido. Voy a usar este código como parte del núcleo de mi máquina de movimiento perpetuo. ¿Pueden creer que esos tontos de la oficina de patentes siguen rechazando mi solicitud? Bueno, les mostraré. ¡Les mostraré todos! Justin Morgan 8 de marzo de 2011 a las 17:55
  • 31
    @Justin Entonces, ¿un analizador Xml está, por definición, libre de errores, mientras que un Regex no lo es? Porque si un analizador Xml no está libre de errores por definición, podría haber un xml que lo bloquee y volvemos al paso 0. Digamos esto: tanto el analizador Xml como este Regex intentan poder analizar todos los "XML. PUEDEN analizar algunos XML "ilegales". Los errores podrían bloquearlos a ambos. C # XmlReader seguramente está más probado que este Regex. xanatos 9 de marzo de 2011 a las 15:08
  • 34
    No, nada está libre de errores: 1) Todos los programas contienen al menos un error. 2) Todos los programas contienen al menos una línea de código fuente innecesario. 3) Con los números 1 y 2 y utilizando la inducción lógica, es muy sencillo demostrar que cualquier programa puede reducirse a una sola línea de código con un error. (de Learning Perl)Scott Weaver 16/02/12 a las 0:53
305

En shell, puede analizar HTML usando sed :

  1. Turing.sed
  2. Escribir analizador HTML (tarea)
  3. ???
  4. ¡Lucro!

Relacionado (por qué no debería usar la coincidencia de expresiones regulares):

7
  • 3
    Me temo que no entendiste el chiste, @kenorb. Por favor, lea la pregunta y la respuesta aceptada una vez más. No se trata de herramientas de análisis de HTML en general, ni de herramientas de shell de análisis de HTML, se trata de analizar HTML a través de expresiones regulares. Palec 13/10/2015 a las 8:12
  • 1
    No, @Abdul. Es completamente, demostrablemente (en el sentido matemático) imposible. Palec 24 de marzo de 2017 a las 13:24
  • 4
    Sí, esa respuesta lo resume bien, @Abdul. Tenga en cuenta que, sin embargo, las implementaciones de expresiones regulares no son realmente expresiones regulares en el sentido matemático; tienen construcciones que las hacen más fuertes, a menudo Turing-completo (equivalente a las gramáticas Tipo 0). Sin embargo, el argumento rompe con este hecho, pero sigue siendo algo válido en el sentido de que las expresiones regulares nunca debieron ser capaces de hacer tal trabajo. Palec 24 de marzo de 2017 a las 14:24
  • 2
    Y, por cierto, la broma a la que me referí fue el contenido de esta respuesta antes de las ediciones (radicales) de kenorb, específicamente la revisión 4, @Abdul. Palec 24 mar 2017 a las 14:26
  • 6
    Lo curioso es que OP nunca pidió analizar html usando expresiones regulares. Pidió hacer coincidir el texto (que resulta ser HTML) usando expresiones regulares. Lo cual es perfectamente razonable. Paralife 29/03/18 a las 15:29
283

Estoy de acuerdo en que la herramienta adecuada para analizar XML y especialmente HTML es un analizador y no un motor de expresión regular. Sin embargo, como han señalado otros, a veces usar una expresión regular es más rápido, más fácil y hace el trabajo si conoce el formato de datos.

En realidad, Microsoft tiene una sección de Mejores prácticas para expresiones regulares en .NET Framework y habla específicamente sobre Considerar la fuente de entrada .

Las expresiones regulares tienen limitaciones, pero ¿ha considerado lo siguiente?

El marco .NET es único cuando se trata de expresiones regulares, ya que admite definiciones de grupos de equilibrio .

Por esta razón, creo que PUEDE analizar XML usando expresiones regulares. Sin embargo, tenga en cuenta que debe ser XML válido (los navegadores son muy tolerantes con HTML y permiten una sintaxis XML incorrecta dentro de HTML ). Esto es posible ya que la "Definición de grupo de equilibrio" permitirá que el motor de expresiones regulares actúe como una PDA.

Cita del artículo 1 citado anteriormente:

.NET Regular Expression Engine

As described above properly balanced constructs cannot be described by a regular expression. However, the .NET regular expression engine provides a few constructs that allow balanced constructs to be recognized.

  • (?<group>) - pushes the captured result on the capture stack with the name group.
  • (?<-group>) - pops the top most capture with the name group off the capture stack.
  • (?(group)yes|no) - matches the yes part if there exists a group with the name group otherwise matches no part.

These constructs allow for a .NET regular expression to emulate a restricted PDA by essentially allowing simple versions of the stack operations: push, pop and empty. The simple operations are pretty much equivalent to increment, decrement and compare to zero respectively. This allows for the .NET regular expression engine to recognize a subset of the context-free languages, in particular the ones that only require a simple counter. This in turn allows for the non-traditional .NET regular expressions to recognize individual properly balanced constructs.

Considere la siguiente expresión regular:

(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
   <!-- .*? -->                  |
   <[^>]*/>                      |
   (?<opentag><(?!/)[^>]*[^/]>)  |
   (?<-opentag></[^>]*[^/]>)     |
   [^<>]*
)*
(?(opentag)(?!))

Usa las banderas:

  • Linea sola
  • IgnorePatternWhitespace (no es necesario si contrae regex y elimina todos los espacios en blanco)
  • IgnoreCase (no es necesario)

Expresión regular explicada (en línea)

(?=<ul\s+id="matchMe"\s+type="square"\s*>) # match start with <ul id="matchMe"...
(?>                                        # atomic group / don't backtrack (faster)
   <!-- .*? -->                 |          # match xml / html comment
   <[^>]*/>                     |          # self closing tag
   (?<opentag><(?!/)[^>]*[^/]>) |          # push opening xml tag
   (?<-opentag></[^>]*[^/]>)    |          # pop closing xml tag
   [^<>]*                                  # something between tags
)*                                         # match as many xml tags as possible
(?(opentag)(?!))                           # ensure no 'opentag' groups are on stack

Puede probar esto en A Better .NET Regular Expression Tester .

Usé la fuente de muestra de:

<html>
<body>
<div>
   <br />
   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>
</div>
</body>
</html>

Esto encontró la coincidencia:

   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>

aunque en realidad salió así:

<ul id="matchMe" type="square">           <li>stuff...</li>           <li>more stuff</li>           <li>               <div>                    <span>still more</span>                    <ul>                         <li>Another &gt;ul&lt;, oh my!</li>                         <li>...</li>                    </ul>               </div>           </li>        </ul>

Por último, disfruté mucho el artículo de Jeff Atwood: Analizando Html The Cthulhu Way . Curiosamente, cita la respuesta a esta pregunta que actualmente tiene más de 4k votos.

5
  • 18
    System.Textno es parte de C #. Es parte de .NET. John Saunders 02/02/12 a las 19:07
  • 8
    En la primera línea de su expresión regular ( (?=<ul\s*id="matchMe"\s*type="square"\s*>) # match start with <ul id="matchMe"...), entre "<ul" e "id" debería estar \s+, no \s*, a menos que desee que coincida con <ulid = ...;)C0deH4cker 6 de julio de 2012 a las 2:49
  • @ C0deH4cker Tienes razón, la expresión debería tener en \s+lugar de \s*. Sam 6/07/12 a las 22:33
  • 4
    No es que realmente lo entienda, pero creo que tu expresión regular falla en<img src="images/pic.jpg" /> ... Scheintod 27 de septiembre de 2013 a las 17:05
  • 3
    @Scheintod Gracias por el comentario. Actualicé el código. La expresión anterior falló para las etiquetas de cierre automático que tenían un /lugar dentro que falló para su <img src="images/pic.jpg" />html. Sam 27 de septiembre de 2013 a las 19:00
261

Sugiero usar QueryPath para analizar XML y HTML en PHP. Es básicamente la misma sintaxis que jQuery, solo que está en el lado del servidor.

4
  • 9
    @ Kyle — jQuery no analiza XML, usa el analizador integrado del cliente (si hay uno). Por lo tanto, no necesita jQuery para hacerlo, sino tan solo dos líneas de JavaScript antiguo . Si no hay un analizador integrado, jQuery no ayudará. RobG 31/10/2013 a las 6:25
  • 2
    @RobG En realidad, jQuery usa el DOM, no el analizador incorporado. Qix - MONICA WAS MISTREATED 22 de septiembre de 2014 a las 3:49
  • 12
    @ Qix: será mejor que le digas a los autores de la documentación: " jQuery.parseXML usa la función de análisis nativa del navegador ... ". Fuente: jQuery.parseXML ()RobG 22 de septiembre de 2014 a las 5:01
  • 6
    Habiendo venido aquí desde la pregunta del meme ( meta.stackexchange.com/questions/19478/the-many-memes-of-meta/… ), me encanta que una de las respuestas sea 'Use jQuery'Jorn 1/04/2016 a las 21:09
227

Si bien las respuestas de 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.

Sin embargo, la expresión regular sugerida es incorrecta:

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

Si agrega algo a la expresión regular, retrocediendo puede verse obligado a coincidir con cosas tontas como <a >>, [^/]es demasiado permisivo. También tenga en cuenta que <space>*[^/]*es redundante, porque [^/]*también puede coincidir con espacios.

Mi sugerencia seria

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

¿Dónde (?<! ... )está (en las expresiones regulares de Perl) la mirada negativa hacia atrás? Dice "una <, luego una palabra, luego cualquier cosa que no sea>, 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 que coincida con los pares de atributos separados por espacios.

6
  • 29
    +1 por señalar que la pregunta no se trata de analizar HTML completo (X), se trata de hacer coincidir las etiquetas abiertas de HTML (X). LarsH 8 de septiembre de 2012 a las 2:26
  • 11
    Algo más que la mayoría de las respuestas parecen ignorar, es que un analizador HTML puede usar expresiones regulares en su implementación para partes de HTML, y me sorprendería si la mayoría de los analizadores no hicieran esto. Thayne 26/03/15 a las 19:15
  • @Thayne Exactamente. Al analizar etiquetas individuales, una expresión regular es la herramienta adecuada para el trabajo. Es bastante ridículo que uno tenga que desplazarse hasta la mitad de la página para encontrar una respuesta razonable. La respuesta aceptada es incorrecta porque mezcla lexing y parsing. kasperd 22/11/15 a las 10:26
  • 3
    La respuesta dada aquí fallará cuando un valor de atributo contenga un carácter '>' o '/'. Martin L 21/04/2016 a las 8:14
  • 1
    Esto funcionará incorrectamente en HTML que contenga comentarios o secciones de CData. Tampoco funcionará correctamente si un atributo entre comillas contiene un >carácter. Estoy de acuerdo con lo que OP sugiere que se puede hacer con una expresión regular, pero la que se presenta aquí es demasiado simplista. JacquesB 30/07/2017 a las 10:14
186

Tratar:

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

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

8
  • 111
    <a href="foo" title="5> 3 "> Vaya </a>Gareth 13 de nov. De 2009 a las 23:11
  • 21
    Eso es muy cierto, y lo pensé, pero asumí que el >símbolo se escapó correctamente a & gt ;. Kobi 13 de nov. De 2009 a las 23:16
  • 67
    >es válido en un valor de atributo. De hecho, en la serialización 'XML canónico' no debe utilizar &gt;. (Lo cual no es del todo relevante, excepto para enfatizar que >en un atributo el valor no es nada inusual)bobince 14 de nov. De 2009 a las 0:15
  • 5
    @Kobi: ¿Qué significa la marca de exlamación (la que colocaste hacia el final) en una expresión regular? Marco Demaio 30/04/11 a las 17:16
  • 6
    @bobince: ¿estás seguro? Ya no entiendo, así que este HTML también es válido:<div title="this tag is a <div></div>">hello</div>Marco Demaio 30/04/11 a las 17:31
182

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

It is said that if you know your enemies and know yourself, you can win a hundred battles without a single loss. If you only know yourself, but not your opponent, you may win or may lose. If you know neither yourself nor your enemy, you will always endanger yourself.

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

He compuesto un haiku que describe la naturaleza del HTML.

HTML has
complexity exceeding
regular language.

También he compuesto un haiku que describe la naturaleza de las expresiones regulares en Perl.

The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
0
156
<?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 );
    }
}

Producción:

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 los elementos que se cierran automáticamente, cargue la cadena html completa en una biblioteca DOM, tome todos los elementos, recorra y filtre los que no se cierran automáticamente y opere sobre ellos.

Estoy seguro de que ya sabe a estas alturas que no debería usar expresiones regulares para este propósito.

1
  • 1
    Si está tratando con XHTML real, agregue getElementsByTagName NSy especifique el espacio de nombres. meder omuraliev 15 '09 a las 14:39
149

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

Extracto:

It is a .NET code library that allows you to parse "out of the web" HTML files. The parser is very tolerant with "real world" malformed HTML.

1
  • CodePlex cerró (pero este está en el archivo de CodePlex). ¿Quizás actualizar? Peter Mortensen 14/08/20 a las 15:47
138

Quieres que el primero >no esté precedido por un /. Busque aquí los detalles sobre cómo hacerlo. Se lo conoce como mirar atrás negativo.

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á iterando a través de etiquetas programáticamente?

1
  • 1
    Sí, seguro que lo soy. Determinando todas las etiquetas que están abiertas actualmente, luego compárelas con las etiquetas cerradas en una matriz separada. La expresión regular me duele el cerebro. Jeff 13 de nov. De 2009 a las 23:04
123

El W3C explica el análisis sintáctico en una forma pseudoregexp:
W3C Link

Siga los enlaces para var QName, Sy Attributepara obtener una imagen más clara.
En base a eso, puede crear una expresión regular bastante buena para manejar cosas como quitar etiquetas.

1
107

Si necesita esto para PHP:

Las funciones PHP DOM no funcionarán correctamente a menos que tenga el formato XML adecuado. No importa cuánto mejor sea su uso para el resto de la humanidad.

simplehtmldom es bueno, pero lo encontré un poco defectuoso, y tiene bastante memoria [Se bloqueará en páginas grandes].

Nunca he usado querypath , por lo que no puedo comentar sobre su utilidad.

Otro para probar es mi DOMParser, que es muy ligero en recursos y lo he estado usando felizmente durante un tiempo. Simple de aprender y poderoso.

Para Python y Java, se publicaron enlaces similares.

Para los votantes negativos: solo escribí mi clase cuando los analizadores XML demostraron ser incapaces de soportar el uso real. Las votaciones negativas religiosas solo evitan que se publiquen respuestas útiles; mantenga las cosas dentro de la perspectiva de la pregunta, por favor.

0
97

Esta es la solucion:

<?php
// here's the pattern:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*(\/>|>)/';

// a string to parse:
$string = 'Hello, try clicking <a href="#paragraph">here</a>
    <br/>and check out.<hr />
    <h2>title</h2>
    <a name ="paragraph" rel= "I\'m an anchor"></a>
    Fine, <span title=\'highlight the "punch"\'>thanks<span>.
    <div class = "clear"></div>
    <br>';

// let's get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER);

// print the result:
print_r($matches[0]);
?>

Para probarlo profundamente, ingresé en la cadena etiquetas de cierre automático como:

  1. <h />
  2. <br/>
  3. <br>

También ingresé etiquetas con:

  1. un atributo
  2. más de un atributo
  3. atributos cuyo valor está enlazado entre comillas simples o dobles
  4. atributos que contienen comillas simples cuando el delimitador es una comilla doble y viceversa
  5. Atributos "unpretty" con un espacio antes del símbolo "=", después de él y antes y después de él.

Si encuentra algo que no funciona en la prueba de concepto anterior, estoy disponible para analizar el código para mejorar mis habilidades.

<EDIT> Olvidé que la pregunta del usuario era evitar el análisis de etiquetas de cierre automático. En este caso el patrón es más simple, convirtiéndose en esto:

$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';

El usuario @ridgerunner notó que el patrón no permite atributos sin comillas o atributos sin valor . En este caso una puesta a punto nos trae el siguiente patrón:

$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';

</EDIT>

Entendiendo el patrón

Si alguien está interesado en aprender más sobre el patrón, proporciono alguna línea:

  1. la primera subexpresión (\ w +) coincide con el nombre de la etiqueta
  2. la segunda subexpresión contiene el patrón de un atributo. Está compuesto por:
    1. uno o más espacios en blanco \ s +
    2. el nombre del atributo (\ w +)
    3. cero o más espacios en blanco \ s * (es posible o no, dejando espacios en blanco aquí)
    4. el símbolo "="
    5. de nuevo, cero o más espacios en blanco
    6. el delimitador del valor del atributo, una comilla simple o doble ('| "). En el patrón, la comilla simple se escapa porque coincide con el delimitador de cadena de PHP. Esta subexpresión se captura entre paréntesis para que pueda ser referenciada nuevamente para analizar el cierre del atributo, por eso es muy importante.
    7. el valor del atributo, emparejado con casi cualquier cosa: (. *?); en esta sintaxis específica, utilizando la coincidencia codiciosa (el signo de interrogación después del asterisco), el motor RegExp habilita un operador similar a "mirar hacia adelante", que coincide con cualquier cosa menos lo que sigue a esta subexpresión
    8. aquí viene lo divertido: la parte \ 4 es un operador de referencia inversa , que se refiere a una subexpresión definida antes en el patrón, en este caso, me refiero a la cuarta subexpresión, que es el primer delimitador de atributo encontrado
    9. cero o más espacios en blanco \ s *
    10. la subexpresión de atributo termina aquí, con la especificación de cero o más ocurrencias posibles, dada por el asterisco.
  3. Entonces, dado que una etiqueta puede terminar con un espacio en blanco antes del símbolo ">", cero o más espacios en blanco se corresponden con el subpatrón \ s *.
  4. La etiqueta para hacer coincidir puede terminar con un simple símbolo ">", o un posible cierre XHTML, que hace uso de la barra antes de él: (/> |>). Por supuesto, la barra inclinada se escapa ya que coincide con el delimitador de la expresión regular.

Pequeño consejo: para analizar mejor este código es necesario mirar el código fuente generado ya que no proporcioné ningún carácter especial HTML que se escape.

6
  • 12
    No coincide con etiquetas válidas que tienen atributos sin valor, es decir <option selected>. Tampoco coincide con etiquetas válidas con valores de atributo no citados, es decir <p id=10>. ridgerunner 25/07/11 a las 15:01
  • 1
    @ridgerunner: Muchas gracias por tu comentario. En ese caso, el patrón debe cambiar un poco: $ patrón = '/ <(\ w +) (\ s + (\ w +) (\ s * \ = \ s * (\' | "|) (. *?) \\ 5 \ s *)) * \ s *> / ';?. lo probé y funciona en el caso de atributos no cotizados o atributos sin valoryodabar 25/07/11 a las 16:41
  • ¿Qué tal un espacio antes del nombre de la etiqueta < a href="http://wtf.org" >? Estoy bastante seguro de que es legal, pero no coincide. Floris 5 de octubre de 2013 a las 4:58
  • 7
    NO lo siento, los espacios en blanco antes de un nombre de etiqueta son ilegales. Más allá de estar "bastante seguro", ¿por qué no proporciona algunas evidencias de su objeción? Aquí están los míos, w3.org/TR/xml11/#sec-starttags se refería a XML 1.1, y puede encontrar lo mismo para HTML 4, 5 y XHTML, ya que una validación de W3C también advertiría si realiza una prueba. Como muchos otros bla-bla-poetas por aquí, todavía no recibí ninguna argumentación inteligente, aparte de algunos cientos de puntos negativos a mis respuestas, para demostrar dónde falla mi código de acuerdo con las reglas del contrato especificadas en la pregunta. Solo les daría la bienvenida. yodabar 6 oct 2013 a las 18:03
  • @ridgerunner, por supuesto, su comentario fue inteligente y bienvenido. yodabar 6/10/2013 a las 18:09
92

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

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

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

88

Me gusta analizar HTML con expresiones regulares. No intento analizar HTML idiota que está deliberadamente roto. Este código es mi analizador principal (edición Perl):

$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g;
s/^ ?\n//s; s/ $//s; print

Se llama htmlsplit , divide el HTML en líneas, con una etiqueta o un fragmento de texto en cada línea. Las líneas pueden luego ser procesadas con otras herramientas de texto y scripts, como grep , sed , Perl, etc. Ni siquiera estoy bromeando :) Disfruta.

Es lo suficientemente simple como para volver a ajustar mi script de Perl slurp-everything-first en una buena transmisión, si desea procesar páginas web enormes. Pero no es realmente necesario.

División HTML


Algunas expresiones regulares mejores:

/(<.*?>|[^<]+)\s*/g    # Get tags and text
/(\w+)="(.*?)"/g       # Get attibutes

Son buenos para XML / XHTML.

Con variaciones menores, puede hacer frente a HTML desordenado ... o convertir HTML -> XHTML primero.


La mejor manera de escribir expresiones regulares es en el estilo Lex / Yacc , no como frases de una sola línea opacas o monstruosidades de varias líneas comentadas. No hice eso aquí, todavía; estos apenas lo necesitan.

9
  • 43
    "No intento analizar HTML idiota que está deliberadamente roto". ¿Cómo sabe tu código la diferencia? Kevin Panko 26/07/11 a las 20:38
  • 3
    (obtener atributos error 1) /(\w+)="(.*?)"/asume comillas dobles. Perderá valores entre comillas simples. En la versión 4 de html y anteriores, se permite un valor sin comillas, si es una palabra simple. David Andersson 11 de septiembre de 2016 a las 8:23
  • 2
    (error de obtención de atributos 2) /(\w+)="(.*?)"/puede coincidir falsamente con texto que parece un atributo dentro de un atributo, por ejemplo <img title="Nope down='up' for aussies" src="..." />. Si se aplica globalmente, también coincidirá con esas cosas en texto ordinario o en comentarios html. David Andersson 11 de septiembre de 2016 a las 8:28
  • 2
    (error de obtención de atributos 3) /(\w+)="(.*?)"/Se deben permitir espacios en blanco opcionales alrededor del signo igual. David Andersson 11 de septiembre de 2016 a las 8:42
  • 2
    (Error de división html 1) s/>/>\n/gDado que ">" está permitido en los datos, esto puede dividir líneas de texto y confundir el procesamiento posterior. David Andersson 11 de septiembre de 2016 a las 9:09
74

Aquí hay un analizador basado en PHP ( archivado ) que analiza HTML usando algunas expresiones regulares impías. Como autor de este proyecto, puedo decirles que es posible analizar HTML con expresiones regulares, pero no es eficiente. Si necesita una solución del lado del servidor (como hice para mi complemento de WordPress wp-Typography ), esto funciona.

2
  • 1
    htmlawed es otro proyecto PHP que analiza HTML para filtrar, convertir, etc. ¡Tiene un buen código si puedes resolverlo! user594694 12 de mayo de 2011 a las 19:22
  • No, no puede analizar HTML con expresiones regulares. Pero para algunos subconjuntos, puede funcionar. mirabilos 5 de diciembre de 2014 a las 17:07
70

Hay algunas expresiones regulares agradables para reemplazar HTML con BBCode aquí . Para todos los detractores, tenga en cuenta que no está tratando de analizar completamente HTML, solo de desinfectarlo. Probablemente pueda darse el lujo de eliminar 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;
1
  • 20
    No hagas esto. Por favor. maletor 3 de septiembre de 2015 a las 2:12
70

Sobre la cuestión de los métodos de expresión regular para analizar (x) HTML, la respuesta a todos los que hablaron sobre algunos límites es: no has sido lo suficientemente entrenado para dominar la fuerza de esta poderosa arma, ya que nadie aquí habló de recursividad. .

Un colega independiente de las expresiones regulares me notificó esta discusión, que ciertamente no es la primera en la web sobre este viejo y candente tema.

Después de leer algunas publicaciones, lo primero que hice fue buscar la cadena "? R" en este hilo. El segundo fue buscar sobre "recursividad".

No, vaca sagrada, no se encontró ninguna coincidencia. Como nadie mencionó el mecanismo principal en el que se basa un analizador, pronto me di cuenta de que nadie entendía el punto.

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

El arte negro de las expresiones regulares es difícil de dominar , por lo que tal vez hay más posibilidades que dejamos de lado mientras probamos y probamos nuestra solución personal para capturar toda la web con una mano ... Bueno, estoy seguro :)

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 nuevas líneas.

Aquí hay una nota de muestra sobre el manual de PHP que escribí en enero: Referencia

(Cuidado. En esa nota utilicé erróneamente el modificador "m"; debería ser borrado, a pesar de que el motor de expresión regular lo descarta, ya que no se usó anclaje ^o no $).

Ahora, podríamos hablar de 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 de expresiones regulares, la recursividad puede tener un límite en el número de patrones anidados analizados , pero depende del lenguaje utilizado.
  2. aunque está dañado, (x) HTML no conduce a errores graves. No está desinfectado .

De todos modos, es solo un patrón de expresión regular, pero revela la posibilidad de desarrollar muchas implementaciones poderosas.

Escribí este patrón para alimentar el analizador de descenso recursivo de un motor de plantilla que construí en mi marco, y el rendimiento es realmente excelente, tanto en los tiempos de ejecución como en el uso de la memoria (nada que ver con otros motores de plantilla que usan la misma sintaxis).

11
  • 38
    Pondré esto en el contenedor "Regex que no permite atributos mayores que en". Compruébelo con <input value = "is 5> 3?" />Gareth 5/07/10 a las 16:24
  • 73
    Si pones algo así en el código de producción, probablemente el mantenedor te disparará. Un jurado nunca lo condenaría. aehiilrs 5/07/10 a las 16:33
  • 32
    Las expresiones regulares no pueden funcionar porque, por definición, no son recursivas. Agregar un operador recursivo a las expresiones regulares básicamente hace que un CFG solo tenga una sintaxis más pobre. ¿Por qué no usar algo diseñado para ser recursivo en primer lugar en lugar de insertar violentamente la recursividad en algo que ya está desbordado de funciones extrañas? Welbog 6/07/10 a las 18:38
  • 19
    Mi objeción no es de funcionalidad, es de tiempo invertido. El problema con RegEx es que cuando publicas los liners de Cutsey Little, parece que hiciste algo más eficientemente ("¡Mira una línea de código!"). Y, por supuesto, nadie menciona la media hora (o 3) que pasaron con su hoja de trucos y (con suerte) probando cada posible permutación de entrada. Y una vez que superas todo eso, cuando el responsable de mantenimiento va a averiguar o validar el código, no pueden simplemente mirarlo y ver que es correcto. Tienen que diseccionar la expresión y esencialmente volver a probarla de nuevo ...Oorang 10/07/10 a las 15:11
  • dieciséis
    ... saber que es bueno. Y eso sucederá incluso con personas que son buenas con las expresiones regulares. Y, sinceramente, sospecho que la inmensa mayoría de la gente no lo sabrá bien. Así que tomas una de las pesadillas de mantenimiento más notorias y la combinas con la recursividad, que es la otra pesadilla de mantenimiento, y creo que lo que realmente necesito en mi proyecto es alguien un poco menos inteligente. El objetivo es escribir código que los malos programadores puedan mantener sin romper la base del código. Sé que es descabellado codificar al mínimo denominador común. Pero contratar talentos excelentes es difícil, y a menudo ...Oorang 10/07/10 a las 15:17
63
<\s*(\w+)[^/>]*>

Las partes explicadas:

<: Personaje inicial

\s*: Puede tener espacios en blanco antes del nombre de la etiqueta (feo, pero posible).

(\w+): las etiquetas pueden contener letras y números (h1). Bueno, \wtambién coincide con '_', pero supongo que no duele. Si tiene curiosidad, utilice ([a-zA-Z0-9] +) en su lugar.

[^/>]*: Cualquier cosa excepto >y /hasta el cierre>

>: Clausura >

NO RELACIONADO

Y a los compañeros, que subestiman las expresiones regulares, diciendo que son tan poderosas como los lenguajes regulares:

a n ba n ba n que no es regular y ni siquiera libre de contexto, se puede combinar con^(a+)b\1b\1$

¡Haciendo referencia a FTW !

4
  • @GlitchMr, ese era su punto. Las expresiones regulares modernas no son técnicamente regulares, ni hay ninguna razón para que lo sean. alanaktion 2 feb 2013 a las 15:45
  • 4
    @alanaktion: Las expresiones regulares "modernas" (léase: con extensiones Perl) no pueden coincidir O(MN)(M es la longitud de la expresión regular, N es la longitud del texto). Las referencias inversas son una de las causas de eso. La implementación en awk no tiene referencias inversas y coincide con todo dentro del O(MN)tiempo. Konrad Borowski 14 feb 2013 a las 16:52
  • 1
    <a href="foo" title="5>3"> Oops </a>(citando a @Gareth, es extraño cómo la gente sigue publicando respuestas con esta deficiencia específica una y otra vez. CDATA es fácil de pasar por alto, pero esto es bastante más básico)Qwertie 17 jul.20 a las 21:59
  • <div title=">">Angel 29 de mayo a las 20:26
61

Como muchas personas ya han señalado, HTML no es un lenguaje normal, 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 está escrito usando Java con la biblioteca jtidy para convertir el HTML en XML y luego Jaxen a xpath en el resultado.

0
55

Si simplemente está tratando de encontrar esas etiquetas (sin ambiciones de análisis), pruebe 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 ignora los tipos que dijiste que querías ignorar.

5
  • 2
    Creo que te refieres en \/>lugar de \\>. Justin Morgan 19/12/2014 a las 17:36
  • No, \>eso es lo que quise decir; Nunca quise editar la expresión regular de mi publicación original. Lonnie Best 29 de mayo de 2016 a las 6:38
  • 2
    Para su información, no necesita escapar de los corchetes angulares. Por supuesto, no hace daño escapar de ellos de todos modos, pero mira la confusión que podrías haber evitado. ;)Alan Moore 29 de mayo de 2016 a las 7:47
  • A veces escapo innecesariamente cuando no estoy seguro de si algo es de carácter especial o no. He editado la respuesta; funciona igual pero más conciso. Lonnie Best 31 de mayo de 2016 a las 7:23
  • Mirando esto ahora, no sé por qué pensé que querías decir \/, ya que eso haría exactamente lo contrario de los requisitos. Quizás pensé que estabas ofreciendo un patrón de filtro negativo. Justin Morgan 1 de junio de 2016 a las 19:14
53

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

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
1
  • 9
    Esto no funciona. Para la entrada '<xa = "<b>" /> <y>', las coincidencias son xey, aunque x está terminado. ceving 4 de mayo de 2011 a las 16:33
52

Es cierto que cuando se programa, generalmente es mejor usar analizadores y API dedicados en lugar de expresiones regulares cuando se trabaja con HTML, especialmente si la precisión es primordial (por ejemplo, si su procesamiento puede tener implicaciones de seguridad). Sin embargo, no me adscribo a una visión dogmática de que el marcado de estilo XML nunca debería procesarse con expresiones regulares. Hay casos en los que las expresiones regulares son una gran herramienta para el trabajo, como cuando se realizan ediciones únicas en un editor de texto, se arreglan archivos XML rotos o se manejan formatos de archivo que se parecen pero que no son del todo XML. Hay algunos problemas a tener en cuenta, pero no son insuperables ni necesariamente relevantes.

Una expresión regular simple como <([^>"']|"[^"]*"|'[^']*')*>suele ser lo suficientemente buena, en casos como los que acabo de mencionar. Es una solución ingenua, considerando todo, pero permite correctamente >símbolos no codificados en los valores de los atributos. Si está buscando, por ejemplo, una tableetiqueta, puede adaptarla como </?table\b([^>"']|"[^"]*"|'[^']*')*>.

Solo para dar una idea de cómo se vería una expresión regular HTML más "avanzada", lo siguiente hace un trabajo bastante respetable al emular el comportamiento del navegador del mundo real y el algoritmo de análisis de HTML5:

</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)

Lo siguiente coincide con una definición bastante estricta de etiquetas XML (aunque no tiene en cuenta el conjunto completo de caracteres Unicode permitidos en los nombres XML):

<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>

Por supuesto, estos no tienen en cuenta el contexto circundante y algunos casos extremos, pero incluso esas cosas podrían resolverse si realmente quisiera (por ejemplo, buscando entre las coincidencias de otra expresión regular).

Al final del día, use la herramienta más apropiada para el trabajo, incluso en los casos en que esa herramienta sea una expresión regular.

0
49

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 usar expresiones regulares para trabajos triviales.

Hay una publicación de blog definitiva sobre la coincidencia de elementos HTML más internos escrita por Steven Levithan.