RegEx coincide con las etiquetas abiertas, excepto las etiquetas autocontenidas XHTML


1474

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

<([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, luego
  • Encuentra cualquier personaje cero o más veces, codicioso, excepto /, luego
  • Encuentra un mayor que

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

Respuestas:


4417

No puede analizar HTML [X] con regex. Porque HTML no puede ser analizado por regex. Regex no es una herramienta que pueda usarse para analizar HTML correctamente. Como he respondido en preguntas de HTML y expresiones regulares aquí tantas veces antes, 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. muchas veces pero no me está afectando. Incluso las expresiones regulares irregulares mejoradas que usa Perl no están a la altura de analizar HTML. Nunca me harás romper. HTML es un lenguaje de suficiente complejidad que no puede ser analizado por expresiones regulares. Incluso Jon Skeet no puede analizar HTML usando expresiones regulares. Cada vez que intentas analizar HTML con expresiones regulares, el niño impío llora la sangre de las vírgenes y los hackers rusos tiran tu aplicación web. Analizar HTML con expresiones regulares convoca a almas contaminadas en el reino de los vivos. HTML y regex van juntos como el amor, el matrimonio y el infanticidio ritual. El <centro> no puede contenerlo, es demasiado tarde. La fuerza de expresiones regulares y HTML juntas en el mismo espacio conceptual destruirá tu mente como una masilla acuosa. Si analizas HTML con regex, te estás entregando a Ellos y sus formas blasfemas que nos condenan a todos a un trabajo inhumano para Aquel cuyo Nombre no puede expresarse en el Plano Multilingüe Básico, él viene. HTML-plus-regexp licuará las nervios del sensible mientras observas, tu psique se marchita en la embestida del horror.es 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údenos a 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 con fugas fr̶ǫm 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̨̥̫͎̭ͯ̿̔̀ͅ


¿Has intentado usar un analizador XML?


Nota del moderador

Esta publicación está bloqueada para evitar ediciones inapropiadas de su contenido. La publicación se ve exactamente como se supone que debe verse: no hay problemas con su contenido. Por favor, no lo marque para nuestra atención.


179
Kobi: Creo que es hora de que renuncie al puesto de Asistente No analice 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 cual alguien más puede luchar un poco. Continúe, analice HTML con regex, si es necesario. Es solo código roto, no vida o muerte.
bobince

27
¿Es posible usar RegEx para analizar esta respuesta?
Chris Porter

2
Si no puede ver esta publicación, aquí hay una captura de pantalla en toda su gloria: imgur.com/gOPS2.png
Andrew Keeton el

3251

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

Si tiene un pequeño conjunto de páginas HTML de las que desea extraer datos y luego introducirlos en una base de datos, las expresiones regulares podrían funcionar bien. Por ejemplo, hace poco quería obtener los nombres, partidos y distritos de los representantes federales australianos, que obtuve del sitio web del Parlamento. Este era un trabajo limitado, de una sola vez.

Regexes funcionó bien para mí y fue muy rápido de configurar.


131
Además, el raspado de datos formateados con bastante regularidad de documentos grandes será MUCHO más rápido con el uso juicioso de scan & regex que cualquier analizador genérico. Y si se siente cómodo con la codificación de expresiones regulares, es mucho más rápido codificar que codificar xpaths. Y casi con certeza menos frágil a los cambios en lo que está raspando. Así que bleh
Michael Johnston

255
@MichaelJohnston "Menos frágil"? Casi seguro que no. Las expresiones regulares se preocupan por los detalles de formato de texto que un analizador XML puede ignorar silenciosamente. ¿Cambiar entre &foo;codificaciones y CDATAsecciones? ¿Usa un minificador HTML para eliminar todos los espacios en blanco en su documento que el navegador no representa? Un analizador XML no le importará, y tampoco una declaración XPath bien escrita. Un "analizador" basado en expresiones regulares, por otro lado ...
Charles Duffy

41
@CharlesDuffy para un trabajo de una sola vez está bien, y para espacios usamos \ s +
cuántico

68
@xiaomao, de hecho, si tener que conocer todas las trampas y soluciones para obtener una solución del 80% que falla el resto del tiempo "funciona para usted", no puedo detenerlo. Mientras tanto, estoy de mi lado de la cerca usando analizadores que funcionan en el 100% de XML sintácticamente válido.
Charles Duffy

374
Una vez tuve que extraer algunos datos de ~ 10k páginas, todas con la misma plantilla HTML. Estaban llenos de errores de HTML que causaban que los analizadores se ahogaran, 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 hice funcionar en una hora.
Paul A Jungwirth

2039

Creo que la falla aquí es que HTML es una gramática Chomsky Tipo 2 (gramática libre de contexto) y RegEx es una gramática Chomsky Tipo 3 (gramática regular) . Dado que una gramática tipo 2 es fundamentalmente más compleja que una gramática tipo 3 (consulte la jerarquía de Chomsky ), es matemáticamente imposible analizar XML con RegEx.

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


226
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 fin 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

101
En la teoría CS, los lenguajes regulares son un subconjunto estricto de lenguajes libres de contexto, pero las implementaciones de expresiones regulares en lenguajes de programación convencionales son más poderosas. Como describe noulakaz.net/weblog/2007/03/18/… , las llamadas "expresiones regulares" pueden verificar los números primos en unario, que es ciertamente algo que una expresión regular de la teoría CS no puede lograr.
Adam Mihalcin

11
@eyelidlessness: el mismo "solo si" se aplica a todos los CFG, ¿no es así? Es decir, si la entrada (X) HTML no está bien formada, ni siquiera un analizador XML completo funcionará de manera confiable. Tal vez, si da ejemplos de los "errores de sintaxis HTML (X) implementados en los agentes de usuario del mundo real" a los que se refiere, entenderé mejor lo que está obteniendo.
LarsH

82
@AdamMihalcin tiene toda la razón. La mayoría de los motores regex existentes son más potentes que las gramáticas Chomsky Tipo 3 (por ejemplo, coincidencias no codiciosas, refuerzos). Algunos motores regex (como los de Perl) están completos en Turing. Es cierto que incluso esas son herramientas deficientes para analizar HTML, pero este argumento frecuentemente no es la razón.
dubiousjim

27
Esta es la respuesta más "completa y corta" aquí. Lleva a la gente a aprender conceptos básicos de gramáticas y lenguajes formales y es de esperar algunas matemáticas para que no lo hará el tiempo estabas en cosas sin esperanza, como la solución de NP-tareas en tiempo polinómico
mishmashru

1332

No escuches a estos tipos. 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. Resuelve el problema de 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 sin pérdida de datos aleatorios.
  5. Casi allí, solo divide todo por cero. Pan comido.

No he terminado la última parte yo mismo, pero sé que me estoy acercando. Sigue arrojando 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 se acaba de abrir en la pared. Hmm

El PS 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.


80
La división por cero es un problema mucho más fácil que los otros que mencionas. Si usa intervalos, en lugar de la aritmética de coma flotante (que todos deberían ser, pero nadie lo es), puede dividir algo felizmente entre [un intervalo que contenga] cero. El resultado es simplemente un intervalo que contiene más y menos infinito.
rjmunro

148
El pequeño problema de margen de Fermat se ha resuelto con márgenes suaves en el moderno software de edición de texto.
kd4ttc

50
Randall Munroe ha resuelto el pequeño problema de margen de Fermat al establecer el tamaño de
fuente

29
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

10
Pude evitar ese paso pegajoso de dividir por cero usando en cambio trinquetes brownianos producidos por la fusión en frío ... aunque solo funciona cuando elimino la constante cosmológica.
Tim Lehner

1073

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 atrapa cosas extrañas como las <a name="badgenerator"">que aparecen en la web.

Supongo que para que no coincida con las etiquetas autocontenidas, querrás usar el aspecto negativo de Kobi :

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

o simplemente combinar si y si no.

Para downvoters: este es el código de trabajo de un producto real. Dudo que cualquiera 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 todavía se rompe en presencia de bloques CDATA, comentarios y elementos de script y estilo. La buena noticia es que puedes deshacerte de aquellos que usan una expresión regular ...


95
Iría con algo que funcione en cosas sensatas que llorar por no ser universalmente perfecto :-)
prajeesh kumar

55
¿Alguien está usando CDATA dentro de HTML?
Danubian Sailor

16
por lo que en realidad no resuelve el problema de análisis solo con regexp, pero como parte del analizador, esto puede funcionar. PD: producto de trabajo no significa buen código. Sin ofender, pero así es como funciona la programación industrial y obtiene su dinero
mishmashru

32
Sus arranques de expresiones regulares fallan en el muy corto posible HTML, válida: <!doctype html><title><</title>. '<!doctype html><title><</title>'.match(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g)Retornos simples ["<!doctype html>", "<title>", "<</title>"]mientras debería ["<title>", "</title>"].

2
si solo estamos tratando de coincidir y no coincidir con los ejemplos dados, /<.([^r>font>[^>font>*)?>/g funciona :-) // javascript: '<p> <a href = "foo"> <br /> <hr class = "foo" />'.match(/<.([^r>font>[^>font>*)?>/g)
imma

506

Hay personas que le dirán que la Tierra es redonda (o tal vez 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 deberían ser recursivas. Te están limitando. Necesitan subyugarlo, y lo hacen manteniéndolo en la ignorancia.

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

Al igual que Lord Marshal (es que un familiar de la clase Marshal .NET?), He visto el Underverse Pila basado en expresiones regulares-verso y regresó con poderes conocimiento no se puede imaginar. Sí, creo que había uno o dos viejos protegiéndolos, 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 .NET), desinflado y codificado en base64 para que sea más fácil de comprender por su débil mente, debería ser algo como esto:

7L0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28
995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8itn6Po9/3eIue3+Px7/3F
86enJ8+/fHn64ujx7/t7vFuUd/Dx65fHJ6dHW9/7fd/t7fy+73Ye0v+f0v+Pv//JnTvureM3b169
OP7i9Ogyr5uiWt746u+BBqc/8dXx86PP7tzU9mfQ9tWrL18d3UGnW/z7nZ9htH/y9NXrsy9fvPjq
i5/46ss3p4z+x3e8b452f9/x93a2HxIkH44PpgeFyPD6lMAEHUdbcn8ffTP9fdTrz/8rBPCe05Iv
p9WsWF788Obl9MXJl0/PXnwONLozY747+t7x9k9l2z/4vv4kqo1//993+/vf2kC5HtwNcxXH4aOf
LRw2z9/v8WEz2LTZcpaV1TL/4c3h66ex2Xv95vjF0+PnX744PbrOm59ZVhso5UHYME/dfj768H7e
Yy5uQUydDAH9+/4eR11wHbqdfPnFF6cv3ogq/V23t++4z4620A13cSzd7O1s/77rpw+ePft916c7
O/jj2bNnT7e/t/397//M9+ibA/7s6ZNnz76PP0/kT2rz/Ts/s/0NArvziYxVEZWxbm93xsrUfnlm
rASN7Hf93u/97vvf+2Lx/e89L7+/FSXiz4Bkd/hF5mVq9Yik7fcncft9350QCu+efkr/P6BfntEv
z+iX9c4eBrFz7wEwpB9P+d9n9MfuM3yzt7Nzss0/nuJfbra3e4BvZFR7z07pj3s7O7uWJM8eCkme
nuCPp88MfW6kDeH7+26PSTX8vu+ePAAiO4LVp4zIPWC1t7O/8/+pMX3rzo2KhL7+8s23T1/RhP0e
vyvm8HbsdmPXYDVhtpdnAzJ1k1jeufOtUAM8ffP06Zcnb36fl6dPXh2f/F6nRvruyHfMd9rgJp0Y
gvsRx/6/ZUzfCtX4e5hTndGzp5jQo9e/z+s3p1/czAUMlts+P3tz+uo4tISd745uJxvb3/v4ZlWs
mrjfd9SG/swGPD/6+nh+9MF4brTBRmh1Tl5+9eT52ckt5oR0xldPzp7GR8pfuXf5PWJv4nJIwvbH
W3c+GY3vPvrs9zj8Xb/147/n7/b7/+52DD2gsSH8zGDvH9+i9/fu/PftTfTXYf5hB+9H7P1BeG52
MTtu4S2cTAjDizevv3ry+vSNb8N+3+/1po2anj4/hZsGt3TY4GmjYbEKDJ62/pHB+3/LmL62wdsU
1J18+eINzTJr3dMvXr75fX7m+MXvY9XxF2e/9+nTgPu2bgwh5U0f7u/74y9Pnh6/OX4PlA2UlwTn
xenJG8L996VhbP3++PCrV68QkrjveITxr2TIt+lL+f3k22fPn/6I6f/fMqZvqXN/K4Xps6sazUGZ
GeQlar49xEvajzI35VRevDl78/sc/b7f6jkG8Va/x52N4L9lBe/kZSh1hr9fPj19+ebbR4AifyuY
12efv5CgGh9TroR6Pj2l748iYxYgN8Z7pr0HzRLg66FnRvcjUft/45i+pRP08vTV6TOe2N/9jv37
R9P0/5YxbXQDeK5E9R12XdDA/4zop+/9Ht/65PtsDVlBBUqko986WsDoWqvbPD2gH/T01DAC1NVn
3/uZ0feZ+T77fd/GVMkA4KjeMcg6RcvQLRl8HyPaWVStdv17PwHV0bOB9xUh7rfMp5Zu3icBJp25
D6f0NhayHyfI3HXHY6YYCw7Pz17fEFhQKzS6ZWChrX+kUf7fMqavHViEPPKjCf1/y5hukcyPTvjP
mHQCppRDN4nbVFPaT8+ekpV5/TP8g/79mVPo77PT1/LL7/MzL7548+XvdfritflFY00fxIsvSQPS
mvctdYZpbt7vxKRfj3018OvC/hEf/79lTBvM3debWj+b8KO0wP+3OeM2aYHumuCAGonmCrxw9cVX
X1C2d4P+uSU7eoBUMzI3/f9udjbYl/el04dI7s8fan8dWRjm6gFx+NrKeFP+WX0CxBdPT58df/X8
DaWLX53+xFdnr06f/szv++NnX7x8fnb6NAhIwsbPkPS7iSUQAFETvP2Tx8+/Og0Xt/yBvDn9vd/c
etno8S+81QKXptq/ffzKZFZ+4e/743e8zxino+8RX37/k595h5/H28+y7fPv490hQdJ349E+txB3
zPZ5J/jsR8bs/y1j2hh/2fkayOqEmYcej0cXUWMN7QrqBwjDrVZRfyQM3xjj/EgYvo4wfLTZrnVS
ebdKq0XSZJvzajKQDUv1/P3NwbEP7cN5+Odivv9/ysPfhHfkOP6b9Fl+91v7LD9aCvp/+Zi+7lLQ
j0zwNzYFP+/Y6r1NcFeDbfBIo8rug3zS3/3WPumPlN3/y8f0I2X3cz4FP+/Y6htSdr2I42fEuSPX
/ewpL4e9/n1evzn94hb+Plpw2+dnbyh79zx0CsPvbq0lb+UQ/h7xvqPq/Gc24PnR18fzVrp8I57d
mehj7ebk5VdPnp+d3GJOSP189eTsaXyk/JV7l98j4SAZgRxtf7x155PR+O6jz36Pw9/1Wz/+e/5u
v//vbsfQAxobws8M9v7xLXp/785/395ED4nO1wx5fsTeH4LnRva+eYY8rpZUBFb/j/jfm8XAvfEj
4/b/ljF1F9B/jx5PhAkp1nu/+y3n+kdZp/93jWmjJ/M11TG++VEG6puZn593PPejoOyHMQU/79jq
GwrKfpSB+tmcwZ93XPkjZffDmIKfd2z1DSm7bmCoPPmjBNT74XkrVf71I/Sf6wTU7XJA4RB+lIC6
mW1+xN5GWw1/683C5rnj/m364cmr45Pf6/SN9H4Us4LISn355vjN2ZcvtDGT6fHvapJcMISmxc0K
MAD4IyP6/5Yx/SwkP360FvD1VTH191mURr/HUY+2P3I9boPnz7Ju/pHrcWPnP3I9/r/L3sN0v52z
0fEgNrgbL8/Evfh9fw/q5Xf93u/97vvf+2Lx/e89L7+/Fe3iZ37f34P5h178kTfx/5YxfUs8vY26
7/d4/OWbb5++ogn7PX5XzOHtOP3GrsHmqobOVO/8Hh1Gk/TPl198QS6w+rLb23fcZ0fMaTfjsv29
7Zul7me2v0FgRoYVURnf9nZEkDD+H2VDf8hjeq8xff1s6GbButNLacEtefHm9VdPXp++CRTw7/v9
r6vW8b9eJ0+/PIHzs1HHdyKE/x9L4Y+s2f+PJPX/1dbsJn3wrY6wiqv85vjVm9Pnp+DgN8efM5va
j794+eb36Xz3mAf5+58+f3r68s230dRvJcxKn/l//oh3f+7H9K2O0r05PXf85s2rH83f/1vGdAvd
w+qBFqsoWvzspozD77EpXYeZ7yzdfxy0ec+l+8e/8FbR84+Wd78xbvn/qQQMz/J7L++GPB7N0MQa
2vTMBwjDrVI0PxKGb4xxfiQMX0cYPuq/Fbx2C1sU8yEF+F34iNsx1xOGa9t6l/yX70uqmxu+qBGm
AxlxWwVS11O97ULqlsFIUvUnT4/fHIuL//3f9/t9J39Y9m8W/Tuc296yUeX/b0PiHwUeP1801Y8C
j/9vz9+PAo8f+Vq35Jb/n0rAz7Kv9aPA40fC8P+RMf3sC8PP08DjR1L3DXHoj6SuIz/CCghZNZb8
fb/Hf/2+37tjvuBY9vu3jmRvxNeGgQAuaAF6Pwj8/+e66M8/7rwpRNj6uVwXZRl52k0n3FVl95Q+
+fz0KSu73/dtkGDYdvZgSP5uskadrtViRKyal2IKAiQfiW+FI+tET/9/Txj9SFf8SFf8rOuKzagx
+r/vD34mUADO1P4/AQAA//8=

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

Si tiene problemas para reconvertirlo en 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ás seguro, no, NO estoy bromeando (pero tal vez estoy 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 tokens 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 la regex simple


68
Dios mío, es enorme. Mi mayor pregunta es ¿por qué? Te das cuenta de que todos los lenguajes modernos tienen analizadores XML, ¿verdad? Puede hacer todo eso en 3 líneas y asegurarse 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 regex / imperativo híbrido, pero no parece que lo haya hecho. ¿Puedes comprimir datos aleatorios también?
Justin Morgan

113
@Justin, no necesito una razón. Se podía hacer (y no era ilegal / inmoral), así que lo hice. No hay limitaciones para la mente, excepto las que reconocemos (Napoleon Hill) ... ¿Los lenguajes modernos pueden analizar XML? De Verdad? ¡Y pensé que ESO era ilegal! :-)
xanatos

76
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é a todos!
Justin Morgan

31
@Justin Entonces, ¿un analizador Xml está libre de errores por definición, mientras que un Regex no? Porque si un Xml Parser 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 Xml Parser como este Regex intentan analizar todo el "legal "XML. PUEDEN analizar algunos XML "ilegales". Los insectos podrían estrellarlos a ambos. C # XmlReader es seguramente más probado que este Regex.
xanatos

31
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) Mediante el n. ° 1 y el n. ° 2 y el uso de la inducción lógica, es simple demostrar que cualquier programa puede reducirse a una sola línea de código con un error. (de Learning Perl)
Scott Weaver

299

En shell, puede analizar HTML usando sed :

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

Relacionado (por qué no debe usar la coincidencia de expresiones regulares):


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 HTML en general, ni de herramientas de shell de análisis HTML, se trata de analizar HTML a través de expresiones regulares.
Palec

1
No, @ Abdul. Es completamente demostrable (en el sentido matemático) imposible.
Palec

3
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 completas de Turing (equivalentes a las gramáticas Tipo 0). El argumento rompe con este hecho, pero todavía es algo válido en el sentido de que las expresiones regulares nunca fueron capaces de hacer ese trabajo.
Palec

2
Y, por cierto, el chiste al que me refería era el contenido de esta respuesta antes de las ediciones (radicales) de kenorb, específicamente la revisión 4, @Abdul.
Palec

3
Lo curioso es que OP nunca solicitó analizar html usando regex. Pidió hacer coincidir el texto (que resulta ser HTML) usando expresiones regulares. Lo cual es perfectamente razonable.
Paralife

274

Estoy de acuerdo en que la herramienta correcta para analizar XML y especialmente HTML es un analizador y no un motor de expresión regular. Sin embargo, como otros han señalado, 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.

Microsoft en realidad 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 en el sentido de que admite definiciones de grupos de equilibrio .

Por esta razón, creo que PUEDES analizar XML usando expresiones regulares. Sin embargo, tenga en cuenta que debe ser XML válido (los navegadores son muy indulgentes 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 expresión regular actúe como un PDA.

Cita del artículo 1 citado anteriormente:

Motor de expresión regular .NET

Como se describió anteriormente, las construcciones adecuadamente equilibradas no pueden describirse mediante una expresión regular. Sin embargo, el motor de expresión regular .NET proporciona algunas construcciones que permiten reconocer las construcciones equilibradas.

  • (?<group>) - empuja el resultado capturado en la pila de captura con el nombre del grupo.
  • (?<-group>) - saca la captura más superior con el grupo de nombres de la pila de captura.
  • (?(group)yes|no) - coincide con la parte yes si existe un grupo con el grupo de nombres; de lo contrario, no coincide con ninguna parte.

Estas construcciones permiten que una expresión regular .NET emule un PDA restringido al permitir esencialmente versiones simples de las operaciones de pila: push, pop y empty. Las operaciones simples son más o menos equivalentes a incrementar, disminuir y comparar a cero respectivamente. Esto permite que el motor de expresiones regulares .NET reconozca un subconjunto de los lenguajes libres de contexto, en particular los que solo requieren un contador simple. Esto a su vez permite que las expresiones regulares .NET no tradicionales reconozcan construcciones individualmente equilibradas.

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ó el partido:

   <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, realmente disfruté el artículo de Jeff Atwood: Parsing Html The Cthulhu Way . Curiosamente, cita la respuesta a esta pregunta que actualmente tiene más de 4k votos.


18
System.Textno es parte de C #. Es parte de .NET.
John Saunders

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" debe estar \s+, no \s*, a menos que desee que coincida con <ulid = ...;)
C0deH4cker

@ C0deH4cker Tienes razón, la expresión debería tener en \s+lugar de \s*.
Sam

44
No es que realmente lo entienda, pero creo que su expresión regular falla<img src="images/pic.jpg" />
Scheintod el

3
@Scheintod Gracias por el comentario. Actualicé el código. La expresión anterior falló para etiquetas de cierre automático que tenían un /lugar dentro que falló para su <img src="images/pic.jpg" />html.
Sam

258

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


8
@ Kyle: jQuery no analiza XML, utiliza el analizador incorporado del cliente (si lo hay). Por lo tanto, no necesita jQuery para hacerlo, sino tan solo dos líneas de JavaScript antiguo . Si no hay un analizador incorporado, jQuery no ayudará.
RobG

1
@RobG En realidad, jQuery usa el DOM, no el analizador incorporado.
Qix - MONICA FUE MALTRATADA el

11
@ Qix: será mejor que le diga a los autores de la documentación entonces: " jQuery.parseXML usa la función de análisis nativo del navegador ... ". Fuente: jQuery.parseXML ()
RobG

66
Habiendo venido aquí de la pregunta meme ( meta.stackexchange.com/questions/19478/the-many-memes-of-meta/… ), me encanta que una de las respuestas sea 'Use jQuery'
Jorn

221

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, al retroceder 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 expresiones regulares de Perl) la mirada negativa hacia atrás. Se lee "a <, luego una palabra, luego todo lo que no sea un>, el último de los cuales puede no ser un /, seguido 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.


29
+1 para señalar que la pregunta no se trata de analizar HTML (X) completo, sino de hacer coincidir etiquetas abiertas (X) HTML.
LarsH

10
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

@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

2
La respuesta dada aquí fallará cuando un valor de atributo contenga un carácter '>' o '/'.
Martin L

Esto funcionará incorrectamente en HTML que contiene 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 presentada aquí es muy simplista.
JacquesB

183

Tratar:

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

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


107
<a href="foo" title="5> 3 "> ¡Vaya! </a>
Gareth el

21
Eso es muy cierto, y lo pensé, pero asumí que el >símbolo se escapó correctamente a & gt ;.
Kobi el

65
>es válido en un valor de atributo. De hecho, en la serialización 'canonical XML' no debe usar &gt;. (Lo cual no es del todo pertinente, salvo destacar que >en un valor de atributo no es en absoluto una cosa inusual.)
bobince

55
@Kobi: ¿qué significa la marca de exclamación (la que colocaste al final) en una expresión regular?
Marco Demaio

66
@bobince: ¿estás seguro? Ya no entiendo, así que este HTML válido también:<div title="this tag is a <div></div>">hello</div>
Marco Demaio

179

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 ni a ti ni a tu enemigo, siempre te pondrás en peligro.

En este caso, tu enemigo es HTML y tú eres tú o regex. Incluso podrías 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]+)(?:[^>]*[^/]*)?>

153
<?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, solo defina los nombres de nodo de elemento que se cierran automáticamente, cargue toda la cadena html en una biblioteca DOM, tome todos los elementos, repita y filtre los que no se cierran automáticamente y opere en ellos.

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


1
Si se trata de XHTML real, agregue getElementsByTagName con NSy especifique el espacio de nombres.
meder omuraliev

148

No sé cuál es su necesidad exacta de esto, pero si también está usando .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 HTML malformado del "mundo real".


137

Desea que el primero >no sea precedido por a /. Mire aquí para obtener detalles sobre cómo hacerlo. Se conoce como mirar hacia 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ás iterando a través de etiquetas programáticamente?


1
Sí, estoy seguro. Determinar todas las etiquetas que están abiertas actualmente, luego comparar eso con las etiquetas cerradas en una matriz separada. RegEx me duele el cerebro.
Jeff

122

El W3C explica el análisis en forma de pseudo regexp:
Enlace W3C

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.


55
Esa no es una forma de expresión de psuedo, es una forma EBNF, como se especifica aquí: especificación XML, apéndice 6
Rob G

106

Si necesita esto para PHP:

Las funciones DOM de PHP no funcionarán correctamente a menos que tenga el formato XML correcto. No importa cuán 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 , así que no puedo comentar sobre su utilidad.

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

Para Python y Java, se publicaron enlaces similares.

Para los downvoters: solo escribí mi clase cuando los analizadores XML no pudieron soportar el uso real. El voto negativo religioso simplemente evita que se publiquen respuestas útiles: mantenga las cosas dentro de la perspectiva de la pregunta, por favor.


95

Aquí está la solución:

<?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. <hr />
  2. <br/>
  3. <br>

También ingresé etiquetas con:

  1. un atributo
  2. más de un atributo
  3. atributos cuyo valor está unido a comillas simples o comillas dobles
  4. atributos que contienen comillas simples cuando el delimitador es una comilla doble y viceversa
  5. atributos "poco pretenciosos" con un espacio antes del símbolo "=", después y tanto antes como después.

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, un ajuste fino nos trae el siguiente patrón:

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

</EDIT>

Comprender 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 PHP. Esta sub-expresión se captura con paréntesis para que pueda ser referenciada nuevamente para analizar el cierre del atributo, es por eso que es muy importante.
    7. el valor del atributo, igualado por 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 todo menos lo que sigue a esta sub-expresión
    8. Aquí viene la diversión: la parte \ 4 es un operador de referencia inversa , que se refiere a una sub-expresión definida anteriormente en el patrón, en este caso, me refiero a la cuarta sub-expresión, que es el primer delimitador de atributos 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. Luego, dado que una etiqueta puede terminar con un espacio en blanco antes del símbolo ">", cero o más espacios en blanco coinciden con el subpatrón \ s *.
  4. La etiqueta para que coincida puede terminar con un simple símbolo ">", o un posible cierre XHTML, que utiliza la barra inclinada antes: (/> |>). La barra oblicua se escapa, por supuesto, ya que coincide con el delimitador de 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 de HTML que se escape.


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 sin comillas, es decir <p id=10>.
ridgerunner

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 caso de atributos no citados o atributos sin valor.
Emanuele Del Grande

¿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

77
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 referidos 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 poetas blah-blah por aquí, todavía no recibí ninguna argumentación inteligente, aparte de unos cientos de menos en 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.
Emanuele Del Grande

@ridgerunner, por supuesto, tu comentario fue inteligente y bienvenido.
Emanuele Del Grande

91

Siempre que 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 su caso, algo como esto:

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

89

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


84

Me gusta analizar HTML con expresiones regulares. No intento analizar HTML idiota que está roto deliberadamente. 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 fragmento de texto en cada línea. Las líneas se pueden procesar aún más con otras herramientas de texto y scripts, como grep , sed , Perl, etc. Ni siquiera estoy bromeando :) Disfruta.

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

Apuesto a que me votarán por esto.

División HTML


Contra mi expectativa, esto obtuvo algunos votos positivos, por lo que sugeriré algunas expresiones regulares mejores:

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

Son buenos para XML / XHTML.

Con pequeñas variaciones, puede hacer frente a HTML desordenado ... o convertir el HTML -> XHTML primero.


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


35
"No intento analizar HTML idiota que está roto deliberadamente". ¿Cómo sabe tu código la diferencia?
Kevin Panko

Bueno, no importa mucho si el HTML está roto o no. La cosa aún dividirá HTML en etiquetas y texto. Lo único que podría estropearlo es si las personas incluyen caracteres <o> sin escape en el texto o los atributos. En la práctica, mi pequeño divisor HTML funciona bien. No necesito una enorme monstruosidad repleta de heurísticas. ¡Las soluciones simples no son para todos ...!
Sam Watkins

Agregué algunas expresiones regulares más simples para extraer etiquetas, texto y atributos, para XML / XHTML.
Sam Watkins,

(obtener atributos error 1) /(\w+)="(.*?)"/asume comillas dobles. Perderá valores en comillas simples. En html versión 4 y anteriores, se permite el valor sin comillas, si es una palabra simple.
David Andersson

(obtener atributos, error 2) /(\w+)="(.*?)"/puede coincidir falsamente con el 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á en el texto ordinario o en comentarios html.
David Andersson

74

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


1
htmlawed es otro proyecto PHP que analiza HTML para filtrar, convertir, etc. ¡Tiene un buen código si puede resolverlo!
user594694

No, no puede analizar HTML con regex. Pero para algunos subconjuntos, puede funcionar.
mirabilos

71

Hay algunas expresiones regulares agradables para reemplazar HTML con BBCode aquí . Para todos los que no lo dicen, tenga en cuenta que no está tratando de analizar HTML completamente, solo para desinfectarlo. Probablemente puede 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;

15
No hagas esto. Por favor.
maletor

68

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

Un colega independiente de RegExp 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 "recursividad".
No, vaca sagrada, no se ha encontrado ninguna coincidencia.
Como nadie mencionó el mecanismo principal en el que se basa un analizador sintáctico, pronto me di cuenta de que nadie entendió el punto.

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

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

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

(Tenga cuidado, en esa nota utilicé erróneamente el modificador "m"; debe borrarse, a pesar de que el motor RegExp lo descarta, ya que no se utilizó el anclaje ^ 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 HTML (x) corrupto no genera errores graves, no se desinfecta .

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


35
Pondré esto en el bin "Regex que no permite mayor que en los atributos". Verifíquelo con <input value = "is 5> 3?" />
Gareth el

68
Si pones algo así en el código de producción, es probable que el encargado te dispare. Un jurado nunca lo condenaría.
aehiilrs

30
Las expresiones regulares no pueden funcionar porque, por definición, no son recursivas. Agregar un operador recursivo a las expresiones regulares básicamente hace un CFG solo con 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á repleto de funcionalidades extrañas?
Welbog

16
Mi objeción no es una funcionalidad, sino una inversión de tiempo. El problema con RegEx es que cuando publicas el cutsey, 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) probar cada posible permutación de entrada. Y una vez que superas todo eso, cuando el encargado de la búsqueda se da cuenta o valida el código, no puede simplemente mirarlo y ver que es correcto. Tienen que diseccionar la expresión y esencialmente volver a probarla de nuevo ...
Oorang

15
... saber que es bueno. Y eso sucederá incluso con personas que son buenas con la expresión regular. Y honestamente sospecho que la abrumadora mayoría de la gente no lo sabrá bien. Entonces, tomas una de las pesadillas de mantenimiento más notorias y la combinas con la recurrencia, 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 programadores malos puedan mantener sin romper la base del código. Sé que vale la pena codificar con el mínimo común denominador. Pero contratar un talento excelente es difícil, y a menudo ...
Oorang

62

Como muchas personas ya han señalado, el HTML no es un lenguaje normal que puede dificultar el análisis. Mi solución a esto es convertirlo en un lenguaje normal 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.


61
<\s*(\w+)[^/>]*>

Las partes explicaron:

<: 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 no duele, supongo. Si tiene curiosidad, use ([a-zA-Z0-9] +) en su lugar.

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

>: clausura >

NO RELACIONADO

Y a los tipos que subestiman las expresiones regulares que dicen que son tan poderosas como los idiomas normales:

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

¡Referencia inversa FTW !


@ GlitchMr, ese era su punto. Las expresiones regulares modernas no son técnicamente regulares, ni hay ninguna razón para que lo sean.
alanaktion

3
@alanaktion: Las expresiones regulares "modernas" (léase: con extensiones Perl) no pueden coincidir dentro 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 y coincide con todo dentro del O(MN)tiempo.
Konrad Borowski

56

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 probé aquí: http://gskinner.com/RegExr/

Coincide con los tipos de etiquetas que mencionó, mientras que ignora los tipos que dijo que quería ignorar.


2
Creo que te refieres en \/>lugar de \\>.
Justin Morgan

No, \>eso es lo que quise decir; Nunca quise editar la expresión regular de mi publicación original.
Lonnie Best

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

A veces escapo innecesariamente cuando no estoy seguro de si algo tiene un carácter especial o no. He editado la respuesta; Funciona igual pero más conciso.
Lonnie Best

Mirando esto ahora, no sé por qué pensé que querías decir \/, ya que eso haría exactamente lo contrario de los requisitos. Tal vez pensé que estabas ofreciendo un patrón de filtro negativo.
Justin Morgan

54

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

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

8
Esto no funciona. Para la entrada '<xa = "<b>" /> <y>' las coincidencias son x e y, aunque x termina.
ceving

51

Es cierto que, cuando se programa, generalmente es mejor usar analizadores y API dedicados en lugar de expresiones regulares cuando se trata de HTML, especialmente si la precisión es primordial (por ejemplo, si su procesamiento podría tener implicaciones de seguridad). Sin embargo, no atribuyo a una vista dogmática que el marcado de estilo XML nunca debe procesarse con expresiones regulares. Hay casos en que las expresiones regulares son una gran herramienta para el trabajo, como cuando se realizan ediciones únicas en un editor de texto, se corrigen archivos XML rotos o se tratan formatos de archivo que se ven pero no son 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 serí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 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 tales cosas podrían tratarse si realmente quisiera (por ejemplo, buscando entre las coincidencias de otra expresión regular).

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


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 simples de coincidencia 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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.