Parece que cada pregunta en stackoverflow donde el autor de la pregunta está utilizando expresiones regulares para obtener cierta información de HTML inevitablemente tendrá una "respuesta" que dice no usar expresiones regulares para analizar HTML.
Por qué no? Soy consciente de que existen analizadores HTML "reales" entre comillas, como Beautiful Soup , y estoy seguro de que son potentes y útiles, pero si solo estás haciendo algo simple, rápido o sucio, ¿por qué? ¿te molestas en usar algo tan complicado cuando unas pocas declaraciones de expresiones regulares funcionarán bien?
Además, ¿hay algo fundamental que no entiendo sobre la expresión regular que los convierte en una mala opción para analizar en general?
regex
html-parsing
ntownsend
fuente
fuente
Respuestas:
El análisis completo de HTML no es posible con expresiones regulares, ya que depende de la coincidencia de la etiqueta de apertura y cierre que no es posible con expresiones regulares.
Las expresiones regulares solo pueden coincidir con los lenguajes regulares, pero HTML es un lenguaje libre de contexto y no un lenguaje regular (como señaló @StefanPochmann, los lenguajes regulares también están libres de contexto, por lo que no necesariamente significa no regular). Lo único que puede hacer con expresiones regulares en HTML es la heurística, pero eso no funcionará en todas las condiciones. Debería ser posible presentar un archivo HTML que coincida incorrectamente con cualquier expresión regular.
fuente
Para regexp rápido y sucio, estará bien. Pero lo fundamental es saber que es imposible construir una expresión regular que analice correctamente HTML.
La razón es que las expresiones regulares no pueden manejar expresiones anidadas arbitrariamente. Consulte ¿Se pueden usar expresiones regulares para unir patrones anidados?
fuente
(De http://htmlparsing.com/regexes )
Supongamos que tiene un archivo HTML en el que intenta extraer URL de etiquetas <img>.
Entonces escribes una expresión regular como esta en Perl:
En este caso, de
$url
hecho contendráhttp://example.com/whatever.jpg
. Pero, ¿qué sucede cuando comienzas a obtener HTML como este?o
o
o
o comienzas a obtener falsos positivos de
Parece tan simple, y podría ser simple para un archivo único e inmutable, pero para cualquier cosa que vaya a hacer con datos HTML arbitrarios, las expresiones regulares son solo una receta para futuros dolores de cabeza.
fuente
Dos razones rápidas:
En cuanto a la idoneidad de las expresiones regulares para el análisis en general: no son adecuadas. ¿Alguna vez has visto los tipos de expresiones regulares que necesitarías para analizar la mayoría de los idiomas?
fuente
En cuanto al análisis, las expresiones regulares pueden ser útiles en la etapa de "análisis léxico" (lexer), donde la entrada se divide en tokens. Es menos útil en la etapa real de "construir un árbol de análisis".
Para un analizador de HTML, esperaría que solo acepte HTML bien formado y eso requiere capacidades fuera de lo que puede hacer una expresión regular (no pueden "contar" y asegurarse de que un número dado de elementos de apertura estén equilibrados por el mismo número de elementos de cierre).
fuente
Debido a que hay muchas maneras de "fastidiar" HTML que los navegadores tratarán de una manera bastante liberal, pero tomaría bastante esfuerzo reproducir el comportamiento liberal del navegador para cubrir todos los casos con expresiones regulares, por lo que su expresión regular inevitablemente fallará en alguna especial casos, y eso posiblemente introduciría serias brechas de seguridad en su sistema.
fuente
El problema es que la mayoría de los usuarios que hacen una pregunta que tiene que ver con HTML y expresiones regulares hacen esto porque no pueden encontrar una expresión regular propia que funcione. Entonces uno tiene que pensar si todo sería más fácil cuando se usa un analizador DOM o SAX o algo similar. Están optimizados y construidos con el propósito de trabajar con estructuras de documentos similares a XML.
Claro, hay problemas que se pueden resolver fácilmente con expresiones regulares. Pero el énfasis se encuentra fácilmente .
Si solo desea encontrar todas las URL que parecen
http://.../
estar bien con expresiones regulares. Pero si desea encontrar todas las URL que están en un elemento a que tiene la clase 'mylink', probablemente sea mejor usar un analizador apropiado.fuente
Las expresiones regulares no se diseñaron para manejar una estructura de etiqueta anidada, y en el mejor de los casos es complicado (en el peor de los casos, imposible) manejar todos los casos extremos posibles que se obtienen con HTML real.
fuente
Creo que la respuesta está en la teoría de la computación. Para que un lenguaje sea analizado usando regex debe ser por definición "regular" ( enlace ). HTML no es un lenguaje regular ya que no cumple con una serie de criterios para un lenguaje regular (mucho que ver con los muchos niveles de anidamiento inherentes en el código html). Si está interesado en la teoría de la computación, le recomendaría este libro.
fuente
Esta expresión recupera atributos de elementos HTML. Soporta:
(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)
Compruébelo usted mismo . Funciona mejor con las banderas "gisx", como en la demostración.
fuente
<script>
etiqueta.HTML / XML se divide en marcado y contenido. Regex solo es útil haciendo un análisis de etiqueta léxica. Supongo que podrías deducir el contenido. Sería una buena opción para un analizador SAX. Las etiquetas y el contenido se pueden entregar a una función definida por el usuario donde se puede realizar un seguimiento de la anidación / cierre de elementos.
En cuanto al análisis de las etiquetas, puede hacerse con expresiones regulares y usarse para quitar etiquetas de un documento.
Durante años de pruebas, he encontrado el secreto de la forma en que los navegadores analizan las etiquetas, tanto bien como mal formadas.
Los elementos normales se analizan con esta forma:
El núcleo de estas etiquetas usa esta expresión regular
Notarás esto
[^>]?
como una de las alternancias. Esto coincidirá con citas desequilibradas de etiquetas mal formadas.También es la raíz más simple de todo mal para las expresiones regulares. La forma en que se usa provocará un golpe para satisfacer su codicioso contenedor cuantificado que debe coincidir.
Si se usa de forma pasiva, nunca hay un problema. Pero, si obliga a que algo coincida al intercalarlo con un par de atributo / valor deseado, y no proporciona la protección adecuada contra el retroceso, es una pesadilla fuera de control.
Esta es la forma general de las etiquetas antiguas simples. Observe que
[\w:]
representa el nombre de la etiqueta? En realidad, los caracteres legales que representan el nombre de la etiqueta son una lista increíble de caracteres Unicode.Continuando, también vemos que simplemente no puede buscar una etiqueta específica sin analizar TODAS las etiquetas. Quiero decir que podrías, pero tendría que usar una combinación de verbos como (* SKIP) (* FAIL) pero aún así todas las etiquetas deben analizarse.
La razón es que la sintaxis de la etiqueta puede estar oculta dentro de otras etiquetas, etc.
Por lo tanto, para analizar pasivamente todas las etiquetas, se necesita una expresión regular como la siguiente. Este particular también coincide con contenido invisible .
A medida que nuevos HTML o xml o cualquier otro desarrollen nuevas construcciones, simplemente agréguelo como una de las alternancias.
Nota de la página web: nunca he visto una página web (o xhtml / xml) con la que haya
tenido problemas. Si encuentras uno, házmelo saber.
Nota de rendimiento: es rápido. Este es el analizador de etiquetas más rápido que he visto
(puede haber más rápido, quién sabe).
Tengo varias versiones específicas También es excelente como raspador
(si eres del tipo práctico).
Completa expresión regular cruda
<(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\s+(?>"[\S\s]*?"|'[\S\s]*?'|(?:(?!/>)[^>])?)+)?\s*>)[\S\s]*?</\1\s*(?=>))|(?:/?[\w:]+\s*/?)|(?:[\w:]+\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)+\s*/?)|\?[\S\s]*?\?|(?:!(?:(?:DOCTYPE[\S\s]*?)|(?:\[CDATA\[[\S\s]*?\]\])|(?:--[\S\s]*?--)|(?:ATTLIST[\S\s]*?)|(?:ENTITY[\S\s]*?)|(?:ELEMENT[\S\s]*?))))>
Aspecto formateado
fuente
"Depende" sin embargo. Es cierto que las expresiones regulares no pueden y no pueden analizar HTML con verdadera precisión, por todas las razones dadas aquí. Sin embargo, si las consecuencias de equivocarse (como no manejar etiquetas anidadas) son menores, y si las expresiones regulares son muy convenientes en su entorno (como cuando está pirateando Perl), continúe.
Supongamos que está analizando páginas web que enlazan a su sitio, tal vez las encontró con una búsqueda de enlaces de Google, y desea una forma rápida de tener una idea general del contexto que rodea su enlace. Está intentando ejecutar un pequeño informe que podría alertarlo para vincular el correo no deseado, algo así.
En ese caso, analizar mal algunos de los documentos no será un gran problema. Nadie más que usted verá los errores, y si tiene mucha suerte, habrá pocos suficientes para que pueda hacer un seguimiento individualmente.
Supongo que estoy diciendo que es una compensación. A veces, implementar o usar un analizador correcto, por más fácil que sea, podría no valer la pena si la precisión no es crítica.
Solo ten cuidado con tus suposiciones. Puedo pensar en algunas formas en que el acceso directo regexp puede ser contraproducente si está tratando de analizar algo que se mostrará en público, por ejemplo.
fuente
Definitivamente, hay casos en los que usar una expresión regular para analizar parte de la información de HTML es la forma correcta de hacerlo: depende mucho de la situación específica.
El consenso anterior es que, en general, es una mala idea. Sin embargo, si se conoce la estructura HTML (y es poco probable que cambie), sigue siendo un enfoque válido.
fuente
Tenga en cuenta que si bien el HTML en sí no es regular, las partes de una página que está viendo podrían ser regulares.
Por ejemplo, es un error que las
<form>
etiquetas se aniden; Si la página web funciona correctamente,<form>
sería razonable utilizar una expresión regular para obtener una .Recientemente hice un poco de raspado web usando solo selenio y expresiones regulares. Llegué con la suya porque los datos que quería era poner en una
<form>
, y pusieron en un formato de tabla simple (de modo que pudiera contar con<table>
,<tr>
y<td>
como no anidados - que en realidad es muy poco común). En cierto grado, las expresiones regulares eran incluso casi necesarias, porque parte de la estructura a la que necesitaba acceder estaba delimitada por comentarios. (Beautiful Soup puede darle comentarios, pero hubiera sido difícil agarrar<!-- BEGIN -->
y<!-- END -->
bloquear usando Beautiful Soup).Sin embargo, si tuviera que preocuparme por las tablas anidadas, ¡mi enfoque simplemente no hubiera funcionado! Hubiera tenido que recurrir a Beautiful Soup. Incluso entonces, sin embargo, a veces puedes usar una expresión regular para agarrar el fragmento que necesitas y luego profundizar desde allí.
fuente
En realidad, el análisis HTML con expresiones regulares es perfectamente posible en PHP. Solo tiene que analizar toda la cadena hacia atrás usando
strrpos
para buscar<
y repetir la expresión regular a partir de ahí usando especificadores no greedy cada vez para superar las etiquetas anidadas. No es lujoso y terriblemente lento en cosas grandes, pero lo usé para mi propio editor de plantillas personales para mi sitio web. En realidad no estaba analizando HTML, pero hice algunas etiquetas personalizadas para consultar las entradas de la base de datos para mostrar tablas de datos (mi<#if()>
etiqueta podría resaltar entradas especiales de esta manera). No estaba preparado para un analizador XML en solo un par de etiquetas auto creadas (con datos muy distintos de XML) aquí y allá.Entonces, aunque esta pregunta está considerablemente muerta, todavía aparece en una búsqueda en Google. Lo leí y pensé "desafío aceptado" y terminé de arreglar mi código simple sin tener que reemplazarlo todo. Decidí ofrecer una opinión diferente a cualquiera que busque una razón similar. Además, la última respuesta se publicó hace 4 horas, por lo que sigue siendo un tema candente.
fuente
<tag >
) ¿Consideró las etiquetas de cierre comentadas? (Por ejemplo,<tag> <!-- </tag> -->
) ¿Consideraste CDATA? ¿Consideró las etiquetas de caso inconsistente? (Ej.<Tag> </tAG>
) ¿Consideraste esto también?También probé con una expresión regular para esto. Es sobre todo útil para encontrar fragmentos de contenido emparejados con la siguiente etiqueta HTML, y no busca etiquetas cercanas coincidentes , pero recogerá etiquetas cercanas. Haga rodar una pila en su propio idioma para verificarlos.
Usar con las opciones 'sx'. 'g' también si te sientes con suerte:
Este está diseñado para Python (podría funcionar para otros lenguajes, no lo he probado, usa miradas positivas, miradas negativas y referencias posteriores con nombre). Apoya:
<div ...>
</div>
<!-- ... -->
<![CDATA[ ... ]]>
<div .../>
<input checked>
<div style='...'>
<div style="...">
<a title='John\'s Story'>
(esto no es HTML válido, pero soy un buen tipo)
<a href = '...'>
También es bastante bueno sobre no activar etiquetas mal formadas, como cuando se olvida una
<
o>
.Si su sabor regex admite capturas repetidas con nombre, entonces usted es dorado, pero Python
re
no (sé que regex sí, pero necesito usar Python vainilla). Esto es lo que obtienes:content
- Todo el contenido hasta la próxima etiqueta. Podrías dejar esto fuera.markup
- La etiqueta completa con todo lo que contiene.comment
- Si es un comentario, el contenido del comentario.cdata
- Si es un<![CDATA[...]]>
, el contenido de CDATA.close_tag
- Si es una etiqueta cercana (</div>
), el nombre de la etiqueta.tag
- Si es una etiqueta abierta (<div>
), el nombre de la etiqueta.attributes
- Todos los atributos dentro de la etiqueta. Use esto para obtener todos los atributos si no obtiene grupos repetidos.attribute
- Repetido, cada atributo.attribute_name
- Repetido, cada nombre de atributo.attribute_value
- Repetido, cada valor de atributo. Esto incluye las comillas si fue citado.is_self_closing
- Esto es/
si es una etiqueta de cierre automático, de lo contrario nada._q
y_v
- Ignorar estos; se usan internamente para referencias posteriores.Si su motor regex no admite capturas repetidas con nombre, hay una sección llamada que puede usar para obtener cada atributo. Simplemente ejecute esa expresión regular en el
attributes
grupo para obtener cada unoattribute
,attribute_name
yattribute_value
fuera de él.Demostración aquí: https://regex101.com/r/mH8jSu/11
fuente
Las expresiones regulares no son lo suficientemente potentes para un lenguaje como HTML. Claro, hay algunos ejemplos en los que puedes usar expresiones regulares. Pero en general no es apropiado para el análisis.
fuente
Sabes ... hay mucha mentalidad de que NO PUEDES hacerlo y creo que todos en ambos lados de la cerca tienen razón y están equivocados. Usted PUEDE hacerlo, pero se necesita un poco más que un simple procesamiento de ejecutar una expresión regular en contra de ella. Tome esto (escribí esto dentro de una hora) como ejemplo. Asume que el HTML es completamente válido, pero dependiendo del idioma que esté utilizando para aplicar la expresión regular mencionada anteriormente, podría corregir el HTML para asegurarse de que tenga éxito. Por ejemplo, eliminar etiquetas de cierre que no deberían estar allí:
</img>
por ejemplo. Luego, agregue la barra diagonal de cierre HTML única a los elementos que les faltan, etc.Lo usaría en el contexto de escribir una biblioteca que me permitiera realizar una recuperación de elementos HTML similar a la de JavaScript
[x].getElementsByTagName()
, por ejemplo. Simplemente uniría la funcionalidad que escribí en la sección DEFINE de la expresión regular y la usaría para entrar dentro de un árbol de elementos, uno a la vez.Entonces, ¿será esta la respuesta final al 100% para validar HTML? No. Pero es un comienzo y con un poco más de trabajo, se puede hacer. Sin embargo, intentar hacerlo dentro de una ejecución de expresiones regulares no es práctico ni eficiente.
fuente