RegEx coincide con las etiquetas abiertas, excepto las etiquetas autocontenidas XHTML
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?
Respuestas
No puede analizar [X] HTML con expresiones regulares. Porque HTML no puede ser analizado por regex. 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 Regex 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 para que no se pueda analizar mediante 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 en el reino de los vivos. HTML y regex van de la mano como el amor, el matrimonio y el infanticidio ritual. El <center> no puede retener, 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 terrible tortura y agujeros de seguridad. El uso de rege x como herramienta para procesar HTML establece una brecha entre este mundo y el temible reino de las entidades corruptas (como las entidades SGML, pero más corruptas), un mero atisbo 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, él 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 iluminación, etiquetas HTML se escapa de ur yo ojos como líq uido p ain, el canto de los regulares exp re
sión de análisis
se EXTI nguish las voces de mor hombre Tal desde el sp aquí puedo ver que se puede ver que lo que es hermoso t él f inal snuf
Fing o f la mentira s del hombre todo está perdido A LL I SL OST XX e Pony que venga s que com
es co
me
st é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 de Es n ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ
͎a̧͈͖r̽̾̈́͒͑e
¿Ha intentado utilizar un analizador XML en su lugar?
Nota del moderador
Esta publicación está bloqueada para evitar ediciones inapropiadas en su contenido. La publicación se ve exactamente como se supone que debe verse; no hay problemas con su contenido. No lo marque para llamar nuestra atención.
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, lo cual obtuve del sitio web del Parlamento. Este fue un trabajo único y limitado.
Las expresiones regulares funcionaron bien para mí y fueron muy rápidas de configurar.
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.
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 hace cada uno de estos en orden:
- Resuelva el problema de la detención.
- Cuadrar un círculo.
- 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á.
- El patrón será bastante grande, así que asegúrese de tener un algoritmo que comprima datos aleatorios sin pérdidas.
- Ya casi está, solo divide todo por cero. Pan comido.
No he terminado la última parte, pero sé que me estoy acercando. Sigue lanzando CthulhuRlyehWgahnaglFhtagnException
s por alguna razón, así que 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.
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 cualquiera que lea esta página tendrá la impresión de que es socialmente aceptable usar expresiones regulares en HTML.
Advertencia : Debo señalar que esta expresión regular aún 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 ...
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 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. 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 está buscando es ELEMENTNAME
. Si el grupo de captura ERROR
no está vacío, se produjo un error de análisis y se detuvo la expresión regular.
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 la expresión regular simple completa
En shell, puede analizar HTML usando sed :
- Turing.sed
- Escribir analizador HTML (tarea)
- ???
- ¡Lucro!
Relacionado (por qué no debería usar la coincidencia de expresiones regulares):
- Si le gustan tanto las expresiones regulares, ¿por qué no se casa con ellas?
- Expresiones regulares: ahora tienes dos problemas
- Hackear el desinfectante HTML de stackoverflow.com
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 .
- Consulte Coincidencia de construcciones equilibradas con expresiones regulares .NET
- Consulte Expresiones regulares de .NET: Regex y coincidencia equilibrada
- Consulte los documentos de Microsoft sobre 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 tolerantes con HTML y permiten una sintaxis XML incorrecta dentro de HTML ). Esto es posible porque 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:
Motor de expresión regular de .NET
Como se describió anteriormente, las construcciones adecuadamente equilibradas no se pueden describir mediante una expresión regular. Sin embargo, el motor de expresión regular de .NET proporciona algunas construcciones que permiten reconocer construcciones equilibradas.
(?<group>)
: inserta el resultado capturado en la pila de captura con el grupo de nombres.(?<-group>)
: saca la captura más alta con el grupo de nombres fuera de la pila de capturas.(?(group)yes|no)
- coincide con la parte sí si existe un grupo con el nombre del grupo, de lo contrario no coincide con ninguna parte.Estas construcciones permiten que una expresión regular .NET emule una PDA restringida al permitir esencialmente versiones simples de las operaciones de pila: empujar, abrir y vaciar. Las operaciones simples son prácticamente equivalentes a incrementar, disminuir y comparar a cero respectivamente. Esto permite que el motor de expresiones regulares .NET reconozca un subconjunto de lenguajes libres de contexto, en particular los que solo requieren un simple contador. Esto, a su vez, permite que las expresiones regulares .NET no tradicionales reconozcan construcciones individuales debidamente 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 >ul<, 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 >ul<, 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 >ul<, oh my!</li> <li>...</li> </ul> </div> </li> </ul>
Por último, disfruté mucho 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.
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.
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 hacia atrás negativa? Se lee "a <, luego una palabra, luego cualquier cosa 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.
Tratar:
<([^\s]+)(\s[^>]*?)?(?<!/)>
Es similar al tuyo, pero el último >
no debe ser después de una barra, y también acepta h1
.
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 se conoce a sí mismo ni a su enemigo, siempre se pondrá en peligro.
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]+)(?:[^>]*[^/]*)?>
<?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.
No sé 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ódigo .NET que le permite analizar archivos HTML "fuera de la web". El analizador es muy tolerante con HTML mal formado del "mundo real".
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?
El W3C explica el análisis sintáctico en una forma pseudoregexp:
W3C Link
Siga los enlaces para var QName
, S
y Attribute
para obtener una imagen más clara.
En base a eso, puede crear una expresión regular bastante buena para manejar cosas como quitar etiquetas.
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 por 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. La votación negativa solo impide que se publiquen respuestas útiles; mantenga las cosas dentro de la perspectiva de la pregunta, por favor.
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:
- <h />
- <br/>
- <br>
También ingresé etiquetas con:
- un atributo
- más de un atributo
- atributos cuyo valor está enlazado entre comillas simples o dobles
- atributos que contienen comillas simples cuando el delimitador es una comilla doble y viceversa
- 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:
- la primera subexpresión (\ w +) coincide con el nombre de la etiqueta
- la segunda subexpresión contiene el patrón de un atributo. Está compuesto por:
- uno o más espacios en blanco \ s +
- el nombre del atributo (\ w +)
- cero o más espacios en blanco \ s * (es posible o no, dejando espacios en blanco aquí)
- el símbolo "="
- de nuevo, cero o más espacios en blanco
- 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 subexpresión se captura entre paréntesis para que se pueda hacer referencia nuevamente para analizar el cierre del atributo, por eso es muy importante.
- el valor del atributo, emparejado 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 cualquier cosa menos lo que sigue a esta subexpresión
- aquí viene la diversión: 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
- cero o más espacios en blanco \ s *
- la subexpresión de atributo termina aquí, con la especificación de cero o más ocurrencias posibles, dada por el asterisco.
- 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 *.
- 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 escape de caracteres especiales HTML.
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']
I used a open source tool called HTMLParser before. It's designed to parse HTML in various ways and serves the purpose quite well. It can parse HTML as different treenode and you can easily use its API to get attributes out of the node. Check it out and see if this can help you.
I like to parse HTML with regular expressions. I don't attempt to parse idiot HTML that is deliberately broken. This code is my main parser (Perl edition):
$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g; s/^ ?\n//s; s/ $//s; print
It's called htmlsplit, splits the HTML into lines, with one tag or chunk of text on each line. The lines can then be processed further with other text tools and scripts, such as grep, sed, Perl, etc. I'm not even joking :) Enjoy.
It is simple enough to rejig my slurp-everything-first Perl script into a nice streaming thing, if you wish to process enormous web pages. But it's not really necessary.
HTML Split
Some better regular expressions:
/(<.*?>|[^<]+)\s*/g # Get tags and text
/(\w+)="(.*?)"/g # Get attibutes
They are good for XML / XHTML.
With minor variations, it can cope with messy HTML... or convert the HTML -> XHTML first.
The best way to write regular expressions is in the Lex / Yacc style, not as opaque one-liners or commented multi-line monstrosities. I didn't do that here, yet; these ones barely need it.
Here is a PHP based parser that parses HTML using some ungodly regex. As the author of this project, I can tell you it is possible to parse HTML with regex, but not efficient. If you need a server-side solution (as I did for my wp-Typography WordPress plugin), this works.
There are some nice regexes for replacing HTML with BBCode here. For all you nay-sayers, note that he's not trying to fully parse HTML, just to sanitize it. He can probably afford to kill off tags that his simple "parser" can't understand.
For example:
$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;
About the question of the regular expression methods to parse (x)HTML, the answer to all of the ones who spoke about some limits is: you have not been trained enough to rule the force of this powerful weapon, since nobody here spoke about recursion.
A regular expression-agnostic colleague notified me this discussion, which is not certainly the first on the web about this old and hot topic.
After reading some posts, the first thing I did was looking for the "?R" string in this thread. The second was to search about "recursion".
No, holy cow, no match found. Since nobody mentioned the main mechanism a parser is built onto, I was soon aware that nobody got the point.
If an (x)HTML parser needs recursion, a regular expression parser without recursion is not enough for the purpose. It's a simple construct.
The black art of regular expressions is hard to master, so maybe there are further possibilities we left out while trying and testing our personal solution to capture the whole web in one hand... Well, I am sure about it :)
Here's the magic pattern:
$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";
Just try it. It's written as a PHP string, so the "s" modifier makes classes include newlines.
Here's a sample note on the PHP manual I wrote in January: Reference
(Take care. In that note I wrongly used the "m" modifier; it should be erased, notwithstanding it is discarded by the regular expression engine, since no ^
or $
anchoring was used).
Now, we could speak about the limits of this method from a more informed point of view:
- according to the specific implementation of the regular expression engine, recursion may have a limit in the number of nested patterns parsed, but it depends on the language used
- although corrupted, (x)HTML does not drive into severe errors. It is not sanitized.
Anyhow, it is only a regular expression pattern, but it discloses the possibility to develop of a lot of powerful implementations.
I wrote this pattern to power the recursive descent parser of a template engine I built in my framework, and performances are really great, both in execution times or in memory usage (nothing to do with other template engines which use the same syntax).
As many people have already pointed out, HTML is not a regular language which can make it very difficult to parse. My solution to this is to turn it into a regular language using a tidy program and then to use an XML parser to consume the results. There are a lot of good options for this. My program is written using Java with the jtidy library to turn the HTML into XML and then Jaxen to xpath into the result.
<\s*(\w+)[^/>]*>
The parts explained:
<
: Starting character
\s*
: It may have whitespaces before the tag name (ugly, but possible).
(\w+)
: tags can contain letters and numbers (h1). Well, \w
also matches '_', but it does not hurt I guess. If curious, use ([a-zA-Z0-9]+) instead.
[^/>]*
: Anything except >
and /
until closing >
>
: Closing >
UNRELATED
And to the fellows, who underestimate regular expressions, saying they are only as powerful as regular languages:
anbanban which is not regular and not even context free, can be matched with ^(a+)b\1b\1$
Backreferencing FTW!
If you're simply trying to find those tags (without ambitions of parsing) try this regular expression:
/<[^/]*?>/g
I wrote it in 30 seconds, and tested here: http://gskinner.com/RegExr/
It matches the types of tags you mentioned, while ignoring the types you said you wanted to ignore.
It seems to me you're trying to match tags without a "/" at the end. Try this:
<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
It's true that when programming it's usually best to use dedicated parsers and APIs instead of regular expressions when dealing with HTML, especially if accuracy is paramount (e.g., if your processing might have security implications). However, I don’t ascribe to a dogmatic view that XML-style markup should never be processed with regular expressions. There are cases when regular expressions are a great tool for the job, such as when making one-time edits in a text editor, fixing broken XML files, or dealing with file formats that look like but aren’t quite XML. There are some issues to be aware of, but they're not insurmountable or even necessarily relevant.
A simple regex like <([^>"']|"[^"]*"|'[^']*')*>
is usually good enough, in cases such as those I just mentioned. It's a naive solution, all things considered, but it does correctly allow unencoded >
symbols in attribute values. If you're looking for, e.g., a table
tag, you could adapt it as </?table\b([^>"']|"[^"]*"|'[^']*')*>
.
Just to give a sense of what a more "advanced" HTML regex would look like, the following does a fairly respectable job of emulating real-world browser behavior and the HTML5 parsing algorithm:
</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)
The following matches a fairly strict definition of XML tags (although it doesn't account for the full set of Unicode characters allowed in XML names):
<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>
Granted, these don't account for surrounding context and a few edge cases, but even such things could be dealt with if you really wanted to (e.g., by searching between the matches of another regex).
At the end of the day, use the most appropriate tool for the job, even in the cases when that tool happens to be a regex.
Although it's not suitable and effective to use regular expressions for that purpose sometimes regular expressions provide quick solutions for simple match problems and in my view it's not that horrbile to use regular expressions for trivial works.
There is a definitive blog post about matching innermost HTML elements written by Steven Levithan.