¿Cómo funcionan los análisis HTML si no utilizan expresiones regulares?

96

Veo preguntas todos los días que preguntan cómo analizar o extraer algo de alguna cadena HTML y la primera respuesta / comentario es siempre "¡No use RegEx para analizar HTML, no sea que sienta la ira!" (esa última parte a veces se omite).

Esto es bastante confuso para mí, siempre pensé que, en general, la mejor manera de analizar cualquier cadena complicada es usar una expresión regular. Entonces, ¿cómo funciona un analizador de HTML? ¿No usa expresiones regulares para analizar.

Un argumento particular para usar una expresión regular es que no siempre existe una alternativa de análisis (como JavaScript, donde DOMDocument no es una opción disponible universalmente). jQuery, por ejemplo, parece manejarse bien usando una expresión regular para convertir una cadena HTML en nodos DOM.

No estoy seguro de si usar CW o no, es una pregunta genuina que quiero que se responda y que no pretende ser un hilo de discusión.

Andy E
fuente
Reetiquetado para agregar análisis y análisis html - @Andy E, espero que esté de acuerdo con usted - pensé que sería útil.
JXG
@JXG: Eso está bien para mí, gracias :-)
Andy E

Respuestas:

65

Por lo general, mediante el uso de un tokenizador. El borrador de la especificación HTML5 tiene un algoritmo extenso para manejar "HTML del mundo real".

Quentin
fuente
1
Buen hallazgo ... para citar "Para manejar estos casos, los analizadores tienen un nivel de anidación de secuencias de comandos, que debe establecerse inicialmente en cero, y un indicador de pausa del analizador, que debe establecerse inicialmente en falso". - En otras palabras, debe iterarlo usted mismo y tener mucha lógica personalizada: P
Timothy Khouri
1
Voto a favor. Es mejor enfatizar la complejidad algorítmica en lugar de alguna tecnología.
Arnis Lapsa
1
Iterarlo usted mismo con mucha lógica personalizada no es una gran idea. Utilice una biblioteca que admita el algoritmo estándar si puede. por ejemplo, search.cpan.org/~tobyink/HTML-HTML5-Parser-0.03/lib/HTML/HTML5/… / code.google.com/p/html5lib
Quentin
8
El principal problema con los analizadores de HTML es que al encontrar un error, no está bien escupir "Parse error" y dejarlo así. Entras en el modo peculiaridades e intentas distinguir lo mejor que puedes del desorden que has encontrado, incluidas las etiquetas que no coinciden, el estilo [{]} entrelazado y todo tipo de rarezas, tratando de que el resultado se vea lo mejor posible y lo inevitable el fracaso es el menos doloroso ... esto no es algo que pueda hacer con expresiones regulares.
SF.
7
@Timothy K: 'Nota: Debido a la forma en que este algoritmo hace que los elementos cambien a los padres, se lo ha denominado el "algoritmo de la agencia de adopción" (en contraste con otros posibles algoritmos para lidiar con contenido mal anidado, que incluía el "algoritmo de incesto", el "algoritmo del asunto secreto" y el "algoritmo de Heisenberg").
JXG
133

Entonces, ¿cómo funciona un analizador de HTML? ¿No usa expresiones regulares para analizar?

Bueno no.

Si regresa en su cerebro a un curso de teoría de la computación, si tomó uno, o un curso de compiladores, o algo similar, puede recordar que existen diferentes tipos de lenguajes y modelos computacionales. No estoy calificado para entrar en todos los detalles, pero puedo revisar algunos de los puntos principales con usted.

El tipo más simple de lenguaje y computación (para estos propósitos) es un lenguaje regular. Estos pueden generarse con expresiones regulares y reconocerse con autómatas finitos. Básicamente, eso significa que las cadenas de "análisis" en estos lenguajes utilizan el estado, pero no la memoria auxiliar. Ciertamente, HTML no es un lenguaje común. Si lo piensa, la lista de etiquetas se puede anidar de forma arbitraria y profunda. Por ejemplo, las tablas pueden contener tablas y cada tabla puede contener muchas etiquetas anidadas. Con expresiones regulares, es posible que pueda elegir un par de etiquetas, pero ciertamente no nada anidado arbitrariamente.

Un lenguaje simple clásico que no es regular tiene paréntesis correctamente emparejados. Por más que lo intente, nunca podrá construir una expresión regular (o un autómata finito) que siempre funcione. Necesita memoria para realizar un seguimiento de la profundidad de anidación.

Una máquina de estado con una pila de memoria es la siguiente fortaleza del modelo computacional. A esto se le llama autómata push-down y reconoce los lenguajes generados por gramáticas libres de contexto. Aquí, podemos reconocer los paréntesis que coinciden correctamente; de ​​hecho, una pila es el modelo de memoria perfecto para ello.

Bueno, ¿es esto lo suficientemente bueno para HTML? Tristemente no. Tal vez para un XML cuidadosamente validado, de hecho, en el que todas las etiquetas siempre se alinean perfectamente. En HTML del mundo real, puede encontrar fácilmente fragmentos como <b><i>wow!</b></i>. Obviamente, esto no se anida, por lo que para analizarlo correctamente, una pila no es lo suficientemente potente.

El siguiente nivel de computación son los lenguajes generados por gramáticas generales y reconocidos por las máquinas de Turing. En general, se acepta que este es efectivamente el modelo computacional más sólido que existe: una máquina de estado, con memoria auxiliar, cuya memoria se puede modificar en cualquier lugar. Esto es lo que pueden hacer los lenguajes de programación. Este es el nivel de complejidad donde habita HTML.

Para resumir todo aquí en una oración: para analizar HTML general, necesita un lenguaje de programación real, no una expresión regular.

HTML se analiza de la misma forma en que se analizan otros lenguajes: lexing y parsing. El paso de lexing divide el flujo de caracteres individuales en tokens significativos. El paso de análisis ensambla los tokens, utilizando estados y memoria, en un documento lógicamente coherente sobre el que se puede actuar.

JXG
fuente
22

Las expresiones regulares son solo una forma de analizador. Un analizador HTML honesto será significativamente más complicado de lo que se puede expresar en expresiones regulares, utilizando descenso recursivo , predicción y varias otras técnicas para interpretar correctamente el texto. Si realmente quiere entrar en él, puede consultar lex & yacc y herramientas similares.

La prohibición contra el uso de expresiones regulares para el análisis de HTML probablemente debería escribirse más correctamente como: "No use expresiones regulares ingenuas para analizar HTML ..." (para que no sienta la ira) "... y trate los resultados con precaución". Para ciertos objetivos específicos, una expresión regular puede ser perfectamente adecuada, pero debe tener mucho cuidado para conocer las limitaciones de su expresión regular y ser tan cauteloso como sea apropiado para la fuente del texto que está analizando (por ejemplo, si es entrada del usuario, tenga mucho cuidado).

TJ Crowder
fuente
+1, una buena respuesta. Debo admitir que he usado expresiones regulares antes incluso cuando no tenía el control del HTML, pero no en ningún tipo de aplicación publicada públicamente. Yo también "sentí la ira", porque era ingenuo. Pero eso fue hace mucho tiempo :-)
Andy E
6

Analizar HTML es la transformación de un texto lineal en una estructura de árbol. Las expresiones regulares generalmente no pueden manejar estructuras de árbol. La expresión regular que necesita en cada punto para obtener el siguiente token cambia todo el tiempo. Puede usar expresiones regulares en un analizador, pero necesitará un conjunto completo de expresiones regulares para cada posible estado de análisis.

Svante
fuente
2

Si desea tener una solución al 100%: debe escribir su propio código personalizado que repita el HTML carácter por carácter y debe tener una gran cantidad de lógica para determinar si debe detener el nodo actual e iniciar el siguiente.

La razón es que este es HTML válido:

<ul>
<li>One
<li>Two
<li>Three
</ul>

Pero también esto:

<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>

Si está de acuerdo con la "solución al 90%": Entonces, usar un analizador XML para cargar un documento está bien. O usando Regex (aunque el xml es más fácil si luego eres el maestro del contenido).

Timothy Khouri
fuente
4
Un analizador XML es más como una solución al 1%. La cantidad de documentos HTML que están bien formados en XML es pequeña.
Quentin
4
Sí, lo hacen ... no tome "carácter por carácter" literalmente, ya que puede intentar transmitir cosas. Pero mi punto es que tienes que escribir tu propio analizador. Los programadores recién llegados no están acostumbrados a escribir ese tipo de código ... estamos acostumbrados a "HtmlDocumentUtility.Load" y cosas así :)
Timothy Khouri
4
@Andy E: Las expresiones regulares no son mágicas, también funcionan carácter a carácter, como cualquier otro tipo de análisis, o diablos, cualquier otra función de cadena.
Bart van Heukelom
1
Por cierto: su primer ejemplo no es solo "HTML semi-válido". En realidad, es HTML 4.01 Strict válido. Puede utilizar, por ejemplo, el validador W3C para verificar esto. La etiqueta de cierre es oficialmente opcional para <li> (consulte la especificación HTML 4).
sleske
2
@Bart: buen punto, a veces mi cerebro se olvida de toda lógica y piensa que las cosas funcionan por arte de magia.
Andy E