O RegEx corresponde a tags abertas, exceto as tags independentes XHTML


1474

Preciso combinar todas essas tags de abertura:

<p>
<a href="foo">

Mas não estes:

<br />
<hr class="foo" />

Eu vim com isso e queria ter certeza de que estava certo. Estou apenas capturando o a-z.

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

Eu acredito que diz:

  • Encontre um menor que, então
  • Encontre (e capture) az uma ou mais vezes, depois
  • Encontre zero ou mais espaços e, em seguida,
  • Encontre qualquer caractere zero ou mais vezes, ganancioso, exceto /, então
  • Encontre um valor maior que

Eu tenho esse direito? E mais importante, o que você acha?

Respostas:


4417

Você não pode analisar [X] HTML com regex. Porque o HTML não pode ser analisado pelo regex. Regex não é uma ferramenta que pode ser usada para analisar corretamente o HTML. Como já respondi às perguntas sobre HTML e regex aqui tantas vezes antes, o uso do regex não permitirá que você consuma HTML. Expressões regulares são uma ferramenta que não é suficientemente sofisticada para entender as construções empregadas pelo HTML. HTML não é uma linguagem regular e, portanto, não pode ser analisado por expressões regulares. As consultas Regex não estão equipadas para dividir o HTML em suas partes significativas. tantas vezes, mas isso não está me afetando. Mesmo expressões regulares irregulares aprimoradas, usadas pelo Perl, não têm a tarefa de analisar o HTML. Você nunca vai me fazer rachar. HTML é uma linguagem de complexidade suficiente que não pode ser analisada por expressões regulares. Mesmo Jon Skeet não pode analisar HTML usando expressões regulares. Toda vez que você tenta analisar HTML com expressões regulares, a criança profana chora o sangue de virgens, e hackers russos usam seu aplicativo da web. A análise de HTML com regex convoca almas contaminadas para o reino dos vivos. HTML e regex andam juntos como amor, casamento e infanticídio ritual. O <center> não pode aguentar, é tarde demais. A força de regex e HTML juntos no mesmo espaço conceitual destruirá sua mente como uma massa aquosa. Se você analisa HTML com regex, está cedendo a Eles e seus modos blasfemos que nos condenam a um trabalho desumano por Aquele cujo Nome não pode ser expresso no Plano Multilíngue Básico, ele chega. O HTML-mais-regexp irá liquefazer os sentimentos do senciente enquanto você observa, sua psique murcha no ataque de horror.É tarde demais É tarde demais Não podemos ser salvos A trangessão de uma criança garante que o regex consumirá todo o tecido vivo (exceto o HTML que não pode, como profetizado anteriormente) Caro senhor, ajude-nos como alguém pode sobreviver a esse flagelo usando o regex para analisar O HTML condenou a humanidade a uma eternidade de terríveis torturas e brechas de segurança, usando o rege x como uma ferramenta para processar o HTML estabelece uma brecha entre este mundo e o mundo das terríveis entidades corrompidas (como entidades SGML, mas mais corruptas), um mero vislumbre de o mundo da reg ex analisadores para HTML irá ins tantly transporte ap consciência de rogrammer i nto aw orl d de incessante gritando, ele vem, O pestilento sl wil ithy regex-infecção l devorar sua HT ML analisador, aplicação e existência de todos os tempos, como Visual Basic só que pior vier, com es não fi ght h e vem, oi s Unholy Radiance de stro҉ying toda a iluminação, HTML tags de vazamento fr̶ǫm yo ur olhos como liq uid p ain, a canção de Regular exp re análise ssion vai EXTI nguish as vozes de mor homem tal do sp aqui eu posso vê-lo você pode ver ele é bonito t ele f inal snuffing o f a mentira é do homem tudo está perdido a LL I SLOST th e pônei ele vir s ele COM es ele co me s t ele ich ou permeat es al l MEU FAC E MINHA CARA ᵒh deus n o NO NOO O ON Θ parada t ele um * ̶͑̾̾ GL ES ͎a̧͈͖r̽̾̈́͒͑en ot Rè̡͊͠͝aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ


Você já tentou usar um analisador XML?


Nota do Moderador

Esta postagem está bloqueada para evitar edições inadequadas no seu conteúdo. A postagem parece exatamente como deveria - não há problemas com seu conteúdo. Por favor, não sinalize para a nossa atenção.


179
Kobi: Acho que é hora de sair do cargo de Assistant Don't Parse HTML With Regex Officer. Não importa quantas vezes o digamos, eles não param de chegar todos os dias ... a cada hora mesmo. É uma causa perdida, com a qual alguém pode lutar um pouco. Então continue, analise HTML com regex, se necessário. É apenas código quebrado, não vida e morte.
bobince

27
É possível usar o RegEx para analisar esta resposta?
22430 Chris Porter

2
Se você não pode ver este post, aqui está uma screencapture dele em toda sua glória: imgur.com/gOPS2.png
Andrew Keeton

3251

Embora o HTML arbitrário com apenas uma regex seja impossível, às vezes é apropriado usá-los para analisar um conjunto limitado e conhecido de HTML.

Se você tiver um pequeno conjunto de páginas HTML das quais deseja coletar dados e depois colocar em um banco de dados, as expressões regulares poderão funcionar bem. Por exemplo, recentemente queria obter os nomes, partidos e distritos dos representantes federais australianos, que saí do site do Parlamento. Este foi um trabalho limitado e único.

Regexes funcionou muito bem para mim e foram muito rápidos de configurar.


131
Além disso, a digitalização regular de dados formatados de documentos grandes será MUITO mais rápida com o uso criterioso de digitalização e regex do que qualquer analisador genérico. E se você se sentir confortável com as regexes de codificação, é muito mais rápido codificar do que codificar xpaths. E quase certamente menos frágil a mudanças no que você está raspando. Então borra.
Michael Johnston

255
@MichaelJohnston "Menos frágil"? Quase certamente não. Regexes se preocupam com detalhes de formatação de texto que um analisador XML pode ignorar silenciosamente. Alternando entre &foo;codificações e CDATAseções? Usando um minificador HTML para remover todos os espaços em branco do documento que o navegador não renderiza? Um analisador XML não se importa, e nem uma declaração XPath bem escrita. Um "analisador" baseado em regex, por outro lado ...
Charles Duffy

41
@CharlesDuffy para um trabalho de uma só vez é ok, e para os espaços que usamos \ s +
quantum

68
@xiaomao de fato, se precisar conhecer todas as dicas e soluções alternativas para obter uma solução de 80% que falhe o resto do tempo "funciona para você", não posso impedi-lo. Enquanto isso, estou do meu lado da barreira usando analisadores que funcionam em 100% do XML sintaticamente válido.
Charles Duffy

374
Uma vez tive que extrair alguns dados de ~ 10k páginas, todas com o mesmo modelo HTML. Eles estavam cheios de erros de HTML que causavam o estrangulamento dos analisadores, e todo o seu estilo era inline ou com <font>etc .: sem classes ou IDs para ajudar a navegar no DOM. Depois de lutar o dia inteiro com a abordagem "certa", eu finalmente mudei para uma solução regex e a trabalhei em uma hora.
Paul A Jungwirth

2039

Eu acho que a falha aqui é que HTML é uma gramática Chomsky Tipo 2 (gramática livre de contexto) e RegEx é uma gramática Chomsky Tipo 3 (gramática regular) . Como uma gramática do tipo 2 é fundamentalmente mais complexa do que uma gramática do tipo 3 (consulte a hierarquia de Chomsky ), é matematicamente impossível analisar o XML com o RegEx.

Mas muitos tentarão, alguns até reivindicarão sucesso - mas até outros encontrarem a falha e estragar tudo.


226
O OP está pedindo para analisar um subconjunto muito limitado de tags XHTML: start. O que torna (X) HTML um CFG é seu potencial de ter elementos entre as tags de início e fim de outros elementos (como em uma regra gramatical A -> s A e). (X) HTML não possui essa propriedade em uma tag inicial: uma tag inicial não pode conter outras tags iniciais. O subconjunto que o OP está tentando analisar não é um CFG.
precisa saber é o seguinte

101
Na teoria do CS, as linguagens regulares são um subconjunto estrito das linguagens sem contexto, mas as implementações de expressões regulares nas linguagens de programação convencionais são mais poderosas. Como o noulakaz.net/weblog/2007/03/18/… descreve, as chamadas "expressões regulares" podem procurar números primos em unário, o que certamente é algo que uma expressão regular da teoria do CS não pode realizar.
Adam Mihalcin 19/03/12

11
@eyelidlessness: o mesmo "somente se" se aplica a todos os CFGs, não é? Ou seja, se a entrada HTML (X) não for bem formada, nem mesmo um analisador XML completo funcionará de maneira confiável. Talvez se você der exemplos dos "erros de sintaxe (X) HTML implementados nos agentes do mundo real" aos quais você está se referindo, entenderei o que você está melhorando.
LarsH

82
@AdamMihalcin está exatamente certo. A maioria dos mecanismos regex existentes são mais poderosos que as gramáticas Chomsky Tipo 3 (por exemplo, correspondência não-gananciosa, refexs). Alguns mecanismos regex (como o Perl) são completos em Turing. É verdade que mesmo essas são ferramentas ruins para analisar HTML, mas esse argumento frequentemente citado não é o motivo.
dubiousjim

27
Esta é a resposta mais "completa e curta" aqui. Isso leva as pessoas a aprender noções básicas de gramáticas e linguagens formais, e esperemos que alguma matemática para que eles não vão tempo estavas em coisas sem esperança como resolver NP-tarefas em tempo polinomial
mishmashru

1332

Não dê ouvidos a esses caras. Você pode analisar totalmente gramáticas sem contexto com regex se você dividir a tarefa em partes menores. Você pode gerar o padrão correto com um script que execute cada uma delas em ordem:

  1. Resolva o problema de parada.
  2. Quadrado um círculo.
  3. Elabore o problema do vendedor ambulante em O (log n) ou menos. Se for mais do que isso, você ficará sem memória RAM e o mecanismo travará.
  4. O padrão será muito grande, portanto, verifique se você possui um algoritmo que comprima sem perdas dados aleatórios.
  5. Quase lá - apenas divida a coisa toda por zero. Mole-mole.

Ainda não terminei a última parte, mas sei que estou chegando perto. Ele continua jogando CthulhuRlyehWgahnaglFhtagnExceptions por algum motivo, então eu vou portá-lo para o VB 6 e usá-lo On Error Resume Next. Vou atualizar o código assim que investigar essa porta estranha que acabou de se abrir na parede. Hmm.

PS Pierre de Fermat também descobriu como fazê-lo, mas a margem em que ele estava escrevendo não era grande o suficiente para o código.


80
Dividir por zero é um problema muito mais fácil do que os outros mencionados. Se você usar intervalos, em vez da aritmética simples de ponto flutuante (que todos deveriam ser, mas ninguém é), você pode dividir algo felizmente por [um intervalo contendo] zero. O resultado é simplesmente um intervalo contendo mais e menos infinito.
rjmunro

148
O pequeno problema de margem da Fermat foi resolvido por margens suaves no moderno software de edição de texto.
Kd4ttc 01/03

50
Pequeno problema margem de Fermat foi resolvido por Randall Munroe, definindo o fontsize a zero: xkcd.com/1381
heltonbiker

29
FYI: O problema de Fermat foi realmente resolvido em 1995 , e levou apenas matemáticos 358 anos para fazer isso.
precisa saber é

10
Consegui contornar esse passo pegajoso de dividir por zero usando catracas brownianas produzidas por fusão a frio ... embora funcione apenas quando removo a constante cosmológica.
Tim Lehner

1073

Isenção de responsabilidade : use um analisador, se tiver a opção. Dito isto...

Este é o regex que eu uso (!) Para corresponder às tags HTML:

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

Pode não ser perfeito, mas eu executei esse código através de um muito HTML. Note que ele até captura coisas estranhas como <a name="badgenerator"">, que aparecem na web.

Acho que, para não corresponder às tags independentes, você pode querer usar o look-behind negativo de Kobi :

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

ou apenas combine se e se não estiver.

Para downvoters: Este é um código de trabalho de um produto real. Duvido que alguém que leia esta página tenha a impressão de que é socialmente aceitável usar expressões regulares em HTML.

Advertência : devo observar que esse regex ainda se decompõe na presença de blocos CDATA, comentários e elementos de script e estilo. A boa notícia é que você pode se livrar daqueles que usam um regex ...


95
Eu iria com algo que funciona em coisas são do que chorar sobre não ser universalmente perfeito :-)
prajeesh Kumar

55
Alguém está usando CDATA dentro de HTML?
Danubian Sailor

16
portanto, você não resolve o problema de análise apenas com regexp, mas como parte do analisador, isso pode funcionar. PS: produto de trabalho não significa bom código. Sem ofensa, mas esta é a forma como a programação industrial trabalha e recebe seu dinheiro
mishmashru

32
Seus começos regex falhar no muito curto HTML possível, válido: <!doctype html><title><</title>. '<!doctype html><title><</title>'.match(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g)Retornos simples ["<!doctype html>", "<title>", "<</title>"]enquanto deveria ["<title>", "</title>"].

2
se estivermos apenas tentando corresponder e não corresponder aos exemplos apresentados, /<.([^r>.la- >^^>**?>/g funciona :-) // javascript: '<p> <a href = "foo"> <br /> <hr class = "foo" />'.match(/<.([^r>[">>**)?>/g)
imma

506

Há pessoas que lhe dirão que a Terra é redonda (ou talvez a Terra seja um esferóide oblato, se quiserem usar palavras estranhas). Eles estão mentindo.

Há pessoas que lhe dirão que as expressões regulares não devem ser recursivas. Eles estão limitando você. Eles precisam subjugar você e o fazem mantendo-o na ignorância.

Você pode viver na realidade deles ou tomar a pílula vermelha.

Como Lord Marshal (ele é um parente da classe Marshal .NET?), Eu vi o Regex-Verse com base na pilha underverse e retornei com conhecimentos de poderes que você não pode imaginar. Sim, acho que havia um ou dois antigos protegendo-os, mas eles estavam assistindo futebol na TV, então não foi difícil.

Eu acho que o caso XML é bastante simples. O RegEx (na sintaxe .NET), desinflado e codificado em base64 para facilitar a compreensão por sua mente débil, deve ser algo como isto:

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

As opções a serem definidas são RegexOptions.ExplicitCapture. O grupo de captura que você está procurando é ELEMENTNAME. Se o grupo de capturaERROR não estiver vazio, ocorreu um erro de análise e o Regex parou.

Se você tiver problemas para convertê-lo em um regex legível por humanos, isso deve ajudar:

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());
    }
}

Se você não tiver certeza, não, não estou brincando (mas talvez esteja mentindo). Vai funcionar. Criei toneladas de testes de unidade para testá-lo e até usei (parte) dos testes de conformidade . Como um tokenizador, não um analisador completo, ele só dividirá o XML em seus tokens de componente. Não analisará / integrará DTDs.

Ah ... se você quiser o código fonte da regex, com alguns métodos auxiliares:

regex para tokenizar um xml ou o regex comum completo


68
Bom Deus, é enorme. Minha maior pergunta é por que? Você percebe que todas as linguagens modernas têm analisadores XML, certo? Você pode fazer tudo isso em três linhas e ter certeza de que funcionará. Além disso, você também percebe que a regex pura é comprovadamente incapaz de fazer certas coisas? A menos que você tenha criado um analisador híbrido de código regular / imperativo, mas ele não parece com o que você criou. Você também pode comprimir dados aleatórios?
Justin Morgan

113
@ Justin, não preciso de um motivo. Isso poderia ser feito (e não era ilegal / imoral), então eu fiz. Não há limitações para a mente, exceto aquelas que reconhecemos (Napoleon Hill) ... As linguagens modernas podem analisar XML? Mesmo? E eu pensei que isso era ilegal! :-)
xanatos

76
Senhor, estou convencido. Vou usar esse código como parte do kernel da minha máquina de movimento perpétuo - você pode acreditar que aqueles tolos no escritório de patentes continuam rejeitando meu pedido? Bem, eu vou mostrar a eles. Eu vou mostrar a todos!
Justin Morgan

31
@ Justin Então, um analisador de XML é, por definição, livre de erros, enquanto um Regex não é? Como se um Analisador de XML não estiver livre de bugs, por definição, pode haver um xml que o interrompe e voltaremos à etapa 0. Digamos o seguinte: o Analisador de XML e este Regex tentam analisar todos os "itens legais" "XML. Eles podem analisar algum XML "ilegal". Os erros podem travar os dois. O C # XmlReader certamente é mais testado que este Regex.
Xanatos

31
Não, nada está livre de bugs: 1) Todos os programas contêm pelo menos um bug. 2) Todos os programas contêm pelo menos uma linha de código fonte desnecessário. 3) Nos números 1 e 2 e usando indução lógica, é simples provar que qualquer programa pode ser reduzido a uma única linha de código com um bug. (from Learning Perl)
Scott Weaver

299

No shell, você pode analisar o HTML usando sed :

  1. Turing.sed
  2. Gravar analisador HTML (lição de casa)
  3. ???
  4. Lucro!

Relacionado (por que você não deve usar a correspondência de regex):


3
Receio que você não tenha entendido a piada, @kenorb. Por favor, leia a pergunta e a resposta aceita mais uma vez. Não se trata de ferramentas de análise de HTML em geral, nem de ferramentas de shell de análise de HTML, mas de análise de HTML por meio de expressões regulares.
Palec

1
Não, @Abdul. É completamente, comprovadamente (no sentido matemático) impossível.
Palec

3
Sim, essa resposta resume bem, @Abdul. Observe que, no entanto, as implementações de regex não são realmente expressões regulares no sentido matemático - elas têm construções que as tornam mais fortes, muitas vezes completas em Turing (equivalente às gramáticas do Tipo 0). O argumento rompe com esse fato, mas ainda é um tanto válido no sentido de que as expressões regulares nunca foram feitas para serem capazes de fazer esse trabalho.
Palec 24/03

2
A propósito, a piada a que me referi foi o conteúdo dessa resposta antes das edições (radicais) de kenorb, especificamente a revisão 4, @Abdul.
Palec 24/03

3
O engraçado é que o OP nunca pediu para analisar o html usando o regex. Ele pediu para corresponder ao texto (que é HTML) usando o regex. O que é perfeitamente razoável.
Paralife

274

Concordo que a ferramenta certa para analisar XML e especialmente HTML é um analisador e não um mecanismo de expressão regular. No entanto, como outros já apontaram, algumas vezes o uso de um regex é mais rápido, fácil e o trabalho é feito se você souber o formato dos dados.

A Microsoft, na verdade, possui uma seção de Práticas recomendadas para expressões regulares no .NET Framework e fala especificamente sobre Considerar a fonte de entrada .

Expressões regulares têm limitações, mas você considerou o seguinte?

A estrutura .NET é única quando se trata de expressões regulares, pois oferece suporte a definições de grupo de balanceamento .

Por esse motivo, acredito que você PODE analisar o XML usando expressões regulares. Observe, no entanto, que ele deve ser XML válido (os navegadores perdoam muito o HTML e permitem uma sintaxe XML ruim dentro do HTML ). Isso é possível, pois a "Definição do grupo de balanceamento" permitirá que o mecanismo de expressão regular atue como um PDA.

Citação do artigo 1 citado acima:

Mecanismo de expressão regular do .NET

Conforme descrito acima, construções adequadamente balanceadas não podem ser descritas por uma expressão regular. No entanto, o mecanismo de expressão regular do .NET fornece algumas construções que permitem que construções equilibradas sejam reconhecidas.

  • (?<group>) - envia o resultado capturado na pilha de captura com o grupo de nomes.
  • (?<-group>) - exibe a captura mais superior com o grupo de nomes fora da pilha de captura.
  • (?(group)yes|no) - corresponde à parte yes, se existir um grupo com o nome group, caso contrário não corresponde a nenhuma parte.

Essas construções permitem que uma expressão regular do .NET emule um PDA restrito, permitindo essencialmente versões simples das operações da pilha: push, pop e empty. As operações simples são praticamente equivalentes a incrementar, decrementar e comparar a zero, respectivamente. Isso permite que o mecanismo de expressão regular do .NET reconheça um subconjunto das linguagens sem contexto, em particular as que exigem apenas um contador simples. Isso, por sua vez, permite que expressões regulares do .NET não tradicionais reconheçam construções individuais adequadamente balanceadas.

Considere a seguinte expressão regular:

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

Use as bandeiras:

  • Única linha
  • IgnorePatternWhitespace (não é necessário se você recolher regex e remover todo o espaço em branco)
  • IgnoreCase (não necessário)

Expressão regular explicada (em linha)

(?=<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

Você pode tentar isso em A Better .NET Regular Expression Tester .

Eu usei a fonte de amostra 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>

Isso encontrou a correspondência:

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

embora realmente tenha saído assim:

<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 fim, gostei muito do artigo de Jeff Atwood: Parsing Html The Cthulhu Way . Engraçado o suficiente, ele cita a resposta a essa pergunta que atualmente tem mais de 4k votos.


18
System.Textnão faz parte do c #. Faz parte do .NET.
John Saunders

8
Na primeira linha do seu regex ( (?=<ul\s*id="matchMe"\s*type="square"\s*>) # match start with <ul id="matchMe"...), entre "<ul" e "id" deve ser \s+, não \s*, a menos que você quer que ele corresponder <ulid = ...;)
C0deH4cker

@ C0deH4cker Você está correto, a expressão deveria ter em \s+vez de \s*.
Sam

4
Não que eu realmente entenda, mas acho que o seu regex falha<img src="images/pic.jpg" />
#

3
@Scheintod Obrigado pelo comentário. Eu atualizei o código. A expressão anterior falhou para as tags de fechamento automático que tinham um /lugar dentro que falhou no seu <img src="images/pic.jpg" />html.
Sam

258

Sugiro usar o QueryPath para analisar XML e HTML em PHP. É basicamente a mesma sintaxe do jQuery, mas é do lado do servidor.


8
@ Kyle - o jQuery não analisa XML, ele usa o analisador embutido do cliente (se houver). Portanto, você não precisa do jQuery para fazer isso, mas apenas duas linhas de JavaScript antigo simples . Se não houver um analisador embutido, o jQuery não ajudará.
RobG

1
@RobG Na verdade, o jQuery usa o DOM, não o analisador embutido.
Qix - MONICA FOI ERRADA

11
@ Qix - é melhor informar aos autores da documentação: " jQuery.parseXML usa a função de análise nativa do navegador ... ". Fonte: jQuery.parseXML ()
RobG

6
Tendo vindo aqui da pergunta do meme ( meta.stackexchange.com/questions/19478/the-many-memes-of-meta/… ), eu amo que uma das respostas seja 'Use jQuery'
Jorn

221

Embora as respostas que você não pode analisar HTML com regexes estejam corretas, elas não se aplicam aqui. O OP só deseja analisar uma tag HTML com expressões regulares, e isso é algo que pode ser feito com uma expressão regular.

A regex sugerida está errada, no entanto:

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

Se você adicionar algo ao regex, ao voltar atrás, ele poderá ser forçado a corresponder a coisas tolas como <a >>, [^/]é permissivo demais. Observe também que <space>*[^/]*é redundante, porque [^/]*também pode corresponder a espaços.

Minha sugestão seria

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

Onde (?<! ... )está (em expressões regulares do Perl) o aspecto negativo. Ele lê "a <, então uma palavra, então qualquer coisa que não seja>, a última das quais pode não ser a /, seguida por>".

Observe que isso permite coisas como <a/ >(assim como o regex original); portanto, se você quiser algo mais restritivo, precisará criar um regex para corresponder aos pares de atributos separados por espaços.


29
+1 por observar que a pergunta não é sobre a análise de HTML completo (X), é sobre correspondência de tags abertas em HTML (X).
LarsH # 8/12

10
Outra coisa que a maioria das respostas parece ignorar é que um analisador HTML pode muito bem usar expressões regulares em sua implementação para partes do HTML, e eu ficaria surpreso se a maioria dos analisadores não fizesse isso.
Thayne 26/03

@Thayne Exatamente. Ao analisar tags individuais, uma expressão regular é a ferramenta certa para o trabalho. É bastante ridículo que seja preciso rolar até a metade da página para encontrar uma resposta razoável. A resposta aceita está incorreta porque mistura lexing e análise.
kasperd

2
A resposta fornecida aqui falhará quando um valor de atributo contiver um caractere '>' ou '/'.
Martin L

Isso funcionará incorretamente no HTML que contém comentários ou seções CData. Também não funcionará corretamente se um atributo entre aspas contiver um >caractere. Concordo que o OP sugere que pode ser feito com uma regex, mas a apresentada aqui é muito simplista.
JacquesB

183

Tentar:

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

É semelhante ao seu, mas o último >não deve ser após uma barra e também aceita h1.


107
<a href="foo" title="5> 3 "> Opa </a>
Gareth

21
Isso é muito verdade, e eu pensei sobre isso, mas presumi que o >símbolo foi corretamente escapado para o & gt ;.
Kobi

65
>é válido em um valor de atributo. De fato, na serialização 'XML canônico' você não deve usar &gt;. (Que não é totalmente relevante, exceto para enfatizar que >em um valor de atributo não é de todo uma coisa incomum.)
bobince

5
@Kobi: o que o ponto de exclamação (aquele que você colocou até o final) significa em uma regexp?
Marco Demaio

6
@ Bobince: você tem certeza? Eu não entendo mais, então também é este HTML válido:<div title="this tag is a <div></div>">hello</div>
Marco Demaio

179

Sun Tzu, um antigo estrategista, general e filósofo chinês, disse:

Dizem que se você conhece seus inimigos e se conhece, pode vencer cem batalhas sem uma única perda. Se você apenas conhece a si mesmo, mas não o seu oponente, poderá ganhar ou perder. Se você não conhece a si mesmo nem a seu inimigo, sempre estará em perigo.

Nesse caso, seu inimigo é HTML e você é você ou regex. Você pode até ser Perl com regex irregular. Conheça HTML. Conheça a si mesmo.

Eu compus um haiku descrevendo a natureza do HTML.

HTML has
complexity exceeding
regular language.

Também compus um haiku descrevendo a natureza da regex no 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 );
    }
}

Resultado:

string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"

Basicamente, apenas defina os nomes dos nós dos elementos que se fecham automaticamente, carregue toda a cadeia html em uma biblioteca DOM, pegue todos os elementos, faça um loop e filtre os que não estão se fechando automaticamente e operam neles.

Tenho certeza que você já sabe que não deve usar regex para esse fim.


1
Se você estiver lidando com XHTML real, anexe getElementsByTagName NSe especifique o espaço para nome.
meder omuraliev

148

Eu não sei sua necessidade exata disso, mas se você também estiver usando o .NET, não poderia usar o Html Agility Pack ?

Excerto:

É uma biblioteca de códigos .NET que permite analisar arquivos HTML "fora da web". O analisador é muito tolerante com HTML malformado "mundo real".


137

Você deseja que o primeiro >não seja precedido por a /. Olha aqui detalhes sobre como fazer isso. É conhecido como lookbehind negativo.

No entanto, uma implementação ingênua disso acabará correspondendo <bar/></foo>neste documento de exemplo

<foo><bar/></foo>

Você pode fornecer um pouco mais de informação sobre o problema que está tentando resolver? Você está iterando pelas tags programaticamente?


1
Sim, eu tenho certeza. Determinando todas as tags que estão abertas no momento, compare-as com as tags fechadas em uma matriz separada. RegEx dói meu cérebro.
1113 Jeff Jeff

122

O W3C explica a análise em um formato pseudo-regexp:
W3C Link

Siga os links var para QName,S e Attributepara obter uma imagem mais clara.
Com base nisso, você pode criar uma regexp muito boa para lidar com coisas como remover tags.


5
Esse não é um formulário psuedo regexp, é um formulário EBNF, conforme especificado aqui: especificação XML, apêndice 6
Rob G

106

Se você precisar disso para PHP:

O DOM do PHP funções não funcionarão corretamente, a menos que sejam XML formatado corretamente. Não importa quão melhor seja o seu uso para o resto da humanidade.

simplehtmldom é bom, mas eu achei um pouco complicado, e é bastante memória pesada [Vai travar em páginas grandes.]

Eu nunca usei querypath , por isso não posso comentar sobre sua utilidade.

Outro a tentar é o meu DOMParser que é muito leve em relação aos recursos e estou usando felizmente há algum tempo. Simples de aprender e poderoso.

Para Python e Java, links semelhantes foram publicados.

Para os que recusaram - eu escrevi minha classe apenas quando os analisadores XML se mostraram incapazes de suportar o uso real. A votação religiosa apenas impede que respostas úteis sejam publicadas - mantenha as coisas dentro da perspectiva da pergunta, por favor.


95

Aqui está a solução:

<?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 testá-lo profundamente, digitei as tags de fechamento automático da string, como:

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

Também inseri tags com:

  1. um atributo
  2. mais de um atributo
  3. atributos cujo valor é vinculado entre aspas simples ou aspas duplas
  4. atributos que contêm aspas simples quando o delimitador é uma aspas dupla e vice-versa
  5. "despretty" atribui um espaço antes do símbolo "=", depois dele e antes e depois dele.

Se você encontrar algo que não funcione na prova de conceito acima, estou disponível na análise do código para melhorar minhas habilidades.

<EDIT> Esqueci que a pergunta do usuário era evitar a análise de tags de fechamento automático. Nesse caso, o padrão é mais simples, transformando-se no seguinte:

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

O @ridgerunner usuário notou que o padrão não permite que atributos não cotadas ou atributos sem valor . Nesse caso, um ajuste fino nos traz o seguinte padrão:

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

</EDIT>

Compreendendo o padrão

Se alguém estiver interessado em aprender mais sobre o padrão, forneço uma linha:

  1. a primeira subexpressão (\ w +) corresponde ao nome da tag
  2. a segunda subexpressão contém o padrão de um atributo. É composto por:
    1. um ou mais espaços em branco \ s +
    2. o nome do atributo (\ w +)
    3. zero ou mais espaços em branco \ s * (é possível ou não, deixando espaços em branco aqui)
    4. o símbolo "="
    5. novamente, zero ou mais espaços em branco
    6. o delimitador do valor do atributo, aspas simples ou duplas ('| "). No padrão, a aspas simples é escapada porque coincide com o delimitador de string PHP. Essa sub-expressão é capturada com parênteses para que possa ser referenciada novamente para analisar o fechamento do atributo, é por isso que é muito importante.
    7. o valor do atributo, correspondido por quase qualquer coisa: (. *?); nessa sintaxe específica, usando a correspondência gananciosa (o ponto de interrogação após o asterisco), o mecanismo RegExp permite um operador semelhante ao "olhar à frente", que corresponde a qualquer coisa, exceto o que segue esta subexpressão
    8. aqui está a graça: a parte \ 4 é um operador de referência anterior , que se refere a uma subexpressão definida anteriormente no padrão; neste caso, estou me referindo à quarta subexpressão, que é o primeiro delimitador de atributo encontrado
    9. zero ou mais espaços em branco \ s *
    10. a subexpressão do atributo termina aqui, com a especificação de zero ou mais ocorrências possíveis, fornecidas pelo asterisco.
  3. Então, como uma tag pode terminar com um espaço em branco antes do símbolo ">", zero ou mais espaços em branco são correspondidos com o subpadrão \ s *.
  4. A tag a corresponder pode terminar com um simples símbolo ">" ou um possível fechamento XHTML, que utiliza a barra antes dela: (/> |>). A barra é, obviamente, escapada, uma vez que coincide com o delimitador da expressão regular.

Dica pequena: para analisar melhor esse código, é necessário examinar o código-fonte gerado, pois não forneci caracteres especiais HTML escapando.


12
Não corresponde a tags válidas com atributos sem valor, ou seja <option selected>. Também não corresponde a tags válidas com valores de atributos não citados, ou seja <p id=10>.
ridgerunner

1
@ridgerunner: Muito obrigado pelo seu comentário. Nesse caso, o padrão deve mudar um pouco: $ pattern = '/ <(\ w +) (\ s + (\ w +) (\ s * \ = \ s * (\' | "|) (. *?) \\ 5 \ s *)) * \ s *> / '; eu testei e obras em caso de atributos não cotados ou atributos sem valor?.
Emanuele Del Grande

Que tal um espaço antes do nome da tag: < a href="http://wtf.org" >tenho certeza de que é legal, mas você não combina.
Floris

7
NÃO, desculpe, os espaços em branco antes de um nome de tag são ilegais. Além de ter "muita certeza", por que você não fornece algumas evidências de sua objeção? Aqui estão os meus, w3.org/TR/xml11/#sec-starttags referido no XML 1.1, e você pode encontrar o mesmo para HTML 4, 5 e XHTML, pois uma validação do W3C também alertaria se você fizesse um teste. Como muitos outros poetas blá-blá por aqui, ainda não recebi nenhuma argumentação inteligente, além de algumas centenas de menos para minhas respostas, para demonstrar onde meu código falha de acordo com as regras de contrato especificadas na pergunta. Eu apenas os receberia.
Emanuele Del Grande

@ridgerunner é claro que seu comentário foi inteligente e bem-vindo.
Emanuele Del Grande

91

Sempre que preciso extrair rapidamente algo de um documento HTML, uso o Tidy para convertê-lo em XML e depois XPath ou XSLT para obter o que eu preciso. No seu caso, algo como isto:

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

89

Eu usei uma ferramenta de código aberto chamada HTMLParser antes. Ele foi projetado para analisar o HTML de várias maneiras e serve muito bem ao objetivo. Ele pode analisar o HTML como um código de árvore diferente e você pode facilmente usar sua API para obter atributos fora do nó. Confira e veja se isso pode ajudá-lo.


84

Eu gosto de analisar HTML com expressões regulares. Não tento analisar HTML idiota que está deliberadamente quebrado. Este código é meu analisador principal (edição 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

É chamado htmlsplit, divide o HTML em linhas, com uma tag ou parte do texto em cada linha. As linhas podem ser processadas ainda mais com outras ferramentas de texto e scripts, como grep , sed , Perl, etc. Eu nem estou brincando :) Aproveite.

É simples o suficiente para rejeitar meu script Perl slurp-everything-first em uma boa coisa de streaming, se você deseja processar enormes páginas da web. Mas não é realmente necessário.

Aposto que vou ser votado por isso.

Divisão HTML


Contra a minha expectativa, isso teve alguns votos positivos, então vou sugerir algumas expressões regulares melhores:

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

Eles são bons para XML / XHTML.

Com pequenas variações, ele pode lidar com HTML confuso ... ou converter o HTML -> XHTML primeiro.


A melhor maneira de escrever expressões regulares é no estilo Lex / Yacc , não como linhas opacas ou monstruosidades comentadas com várias linhas. Ainda não fiz isso aqui; esses mal precisam disso.


35
"Eu não tento analisar HTML idiota que está deliberadamente quebrado." Como seu código sabe a diferença?
22611 Kevin Panko

Bem, não importa muito se o HTML está quebrado ou não. A coisa ainda dividirá o HTML em tags e texto. A única coisa que poderia atrapalhar é se as pessoas incluírem caracteres ou caracteres sem escape <ou> em texto ou atributos. Na prática, meu minúsculo divisor de HTML funciona bem. Não preciso de um enorme calço de monstruosidade cheio de heurísticas. Soluções simples não são para todos ...!
Sam Watkins

Eu adicionei alguns regexps mais simples para extrair tags, texto e atributos, para XML / XHTML.
Sam Watkins

(obter atributos bug 1) /(\w+)="(.*?)"/assume aspas duplas. Ele perderá valores entre aspas simples. Na versão html 4 e no valor anterior não citado é permitido, se for uma palavra simples.
David Andersson

(obter atributos, bug 2) /(\w+)="(.*?)"/pode corresponder falsamente ao texto que se parece com um atributo dentro de um atributo, por exemplo <img title="Nope down='up' for aussies" src="..." />. Se aplicado globalmente, também corresponderá a essas coisas no texto comum ou nos comentários html.
David Andersson

74

Aqui está um analisador baseado em PHP que analisa HTML usando algum regex ímpio. Como autor deste projeto, posso dizer que é possível analisar HTML com regex, mas não é eficiente. Se você precisar de uma solução do lado do servidor (como eu fiz para o meu plugin wp-Typography WordPress ), isso funcionará.


1
htmlawed é outro projeto PHP que analisa HTML para filtrar, converter etc. Tem algum código interessante, se você puder descobrir!
User594694

Não, você não pode analisar HTML com regex. Mas para alguns subconjuntos, pode funcionar.
mirabilos

71

Existem algumas boas expressões para substituir o HTML pelo BBCode aqui . Para todos os que pensam mal, observe que ele não está tentando analisar completamente o HTML, apenas para higienizá-lo. Ele provavelmente pode se dar ao luxo de eliminar tags que seu simples "analisador" não consegue entender.

Por exemplo:

$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
Não faça isso. Por favor.
maletor

68

Sobre a questão dos métodos RegExp para analisar (x) HTML, a resposta para todos que falaram sobre alguns limites é: você não foi treinado o suficiente para governar a força dessa arma poderosa, já que NINGUÉM aqui falou sobre recursão .

Um colega independente de RegExp me notificou essa discussão, que certamente não é a primeira na web sobre esse tópico antigo e popular.

Depois de ler algumas postagens, a primeira coisa que fiz foi procurar a string "? R" neste segmento. O segundo foi pesquisar sobre "recursão".
Não, vaca sagrada, nenhuma correspondência encontrada.
Como ninguém mencionou o mecanismo principal no qual um analisador é construído, logo percebi que ninguém entendia o ponto.

Se um analisador HTML (x) precisar de recursão, um analisador RegExp sem recursão não será suficiente para esse fim. É uma construção simples.

A arte negra do RegExp é difícil de dominar , portanto, talvez haja outras possibilidades que deixamos de fora ao testar e testar nossa solução pessoal para capturar toda a web em uma mão ... Bem, tenho certeza disso :)

Aqui está o padrão mágico:

$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";

Apenas tente.
Ele é escrito como uma string PHP, então o modificador "s" faz com que as classes incluam novas linhas.
Aqui está um exemplo de nota no manual do PHP que escrevi em janeiro: Referência

(Cuidado, nessa nota, usei incorretamente o modificador "m"; ele deve ser apagado, apesar de ser descartado pelo mecanismo RegExp, pois não foi usada nenhuma ancoragem ^ ou $).

Agora, poderíamos falar sobre os limites desse método de um ponto de vista mais informado:

  1. de acordo com a implementação específica do mecanismo RegExp, a recursão pode ter um limite no número de padrões aninhados analisados , mas depende do idioma usado
  2. embora o HTML corrompido (x) não leve a erros graves, ele não é higienizado .

De qualquer forma, é apenas um padrão RegExp, mas revela a possibilidade de desenvolver muitas implementações poderosas.
Eu escrevi esse padrão para alimentar o analisador de descida recursiva de um mecanismo de modelo que construí em minha estrutura, e os desempenhos são realmente ótimos, tanto em tempos de execução quanto no uso de memória (nada a ver com outros mecanismos de modelo que usam a mesma sintaxe).


35
Vou colocar isso no bin "Regex que não permite valores maiores que em atributos". Compare com <input value = "is 5> 3?" />
Gareth

68
Se você colocar algo assim no código de produção, provavelmente será atingido pelo mantenedor. Um júri nunca o condenaria.
precisa saber é

30
Expressões regulares não podem funcionar porque, por definição, não são recursivas. Adicionar um operador recursivo a expressões regulares basicamente cria um CFG apenas com uma sintaxe mais fraca. Por que não usar algo projetado para ser recursivo em primeiro lugar, em vez de inserir violentamente a recursão em algo que já está repleto de funcionalidades estranhas?
Welbog 6/07/10

16
Minha objeção não é de funcionalidade, é de um tempo investido. O problema com o RegEx é que, quando você publica os liners cutsey little one, parece que você fez algo com mais eficiência ("Veja uma linha de código!"). E é claro que ninguém menciona a meia hora (ou 3) que eles gastaram com suas folhas de dicas e (espero) testar todas as permutações possíveis de entrada. E uma vez que você supera tudo isso quando o mantenedor descobre ou valida o código, ele não pode simplesmente olhar para ele e ver se está certo. O tem que dissecar a expressão e, essencialmente, testar novamente tudo ...
Oorang

15
... saber que isso é bom. E isso acontecerá mesmo com pessoas que são boas com regex. E honestamente, suspeito que a esmagadora maioria das pessoas não o conheça bem. Então você pega um dos mais notórios pesadelos de manutenção e o combina com a recursão, que é o outro pesadelo de manutenção, e penso comigo o que realmente preciso no meu projeto é alguém um pouco menos inteligente. O objetivo é escrever código que programadores ruins podem manter sem quebrar a base de código. Eu sei que é irritante codificar para o denominador menos comum. Mas contratar excelentes talentos é difícil, e muitas vezes você ...
Oorang

62

Como muitas pessoas já apontaram, o HTML não é uma linguagem comum, o que pode dificultar a análise. Minha solução para isso é transformá-lo em uma linguagem comum usando um programa organizado e, em seguida, usar um analisador XML para consumir os resultados. Existem muitas boas opções para isso. Meu programa é escrito usando Java com a biblioteca jtidy para transformar o HTML em XML e, em seguida, o Jaxen para xpath no resultado.


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

As partes explicadas:

<: personagem inicial

\s*: pode ter espaços em branco antes do nome da tag (feio, mas possível).

(\w+): as tags podem conter letras e números (h1). Bem, \wtambém corresponde a '_', mas não dói, eu acho. Se curioso, use ([a-zA-Z0-9] +).

[^/>]*: qualquer coisa, exceto >e /até fechar>

>: fechamento >

NÃO RETIRADO

E aos colegas que subestimam as expressões regulares dizendo que são tão poderosos quanto as línguas comuns:

um n ba n ba n que não é regular e nem mesmo livre de contexto, pode ser combinado com^(a+)b\1b\1$

Backreferencing FTW !


@ GlitchMr, esse era o ponto dele. Expressões regulares modernas não são tecnicamente regulares, nem há motivo para elas existirem.
Alanaktion

3
@alanaktion: As expressões regulares "modernas" (leia-se: com extensões Perl) não podem corresponder dentro O(MN)(M sendo comprimento de expressão regular, N sendo comprimento de texto). As referências anteriores são uma das causas disso. A implementação no awk não tem referências anteriores e corresponde a tudo dentro do O(MN)tempo.
21813 Konrad Borowski

56

Se você está simplesmente tentando encontrar essas tags (sem ambições de análise), tente esta expressão regular:

/<[^/]*?>/g

Escrevi em 30 segundos e testei aqui: http://gskinner.com/RegExr/

Ele corresponde aos tipos de tags que você mencionou, enquanto ignora os tipos que você disse que deseja ignorar.


2
Eu acho que você quer dizer em \/>vez de \\>.
Justin Morgan

Não, \>é exatamente o que eu quis dizer; Nunca pretendi editar a expressão regular da minha postagem original.
Lonnie Best

2
Para sua informação, você não precisa escapar de colchetes angulares. Obviamente, não faz mal escapar deles de qualquer maneira, mas observe a confusão que você poderia ter evitado. ;)
Alan Moore

Às vezes, escapei desnecessariamente quando não tenho certeza se algo é de caráter especial ou não. Eu editei a resposta; funciona da mesma maneira, mas é mais conciso.
Lonnie Best

Olhando para isso agora, não sei por que pensei que você quis dizer \/, pois isso faria exatamente o oposto dos requisitos. Talvez eu tenha pensado que você estava oferecendo um padrão de filtro negativo.
Justin Morgan

54

Parece-me que você está tentando combinar tags sem um "/" no final. Tente o seguinte:

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

8
Isso não funciona. Para a entrada '<xa = "<b>" /> <y>', as correspondências são xey, embora x seja finalizado.
ceving 04/04/11

51

É verdade que, ao programar, geralmente é melhor usar analisadores e APIs dedicados em vez de expressões regulares ao lidar com HTML, especialmente se a precisão é primordial (por exemplo, se o seu processamento pode ter implicações de segurança). No entanto, não atribuo a uma visão dogmática de que a marcação no estilo XML nunca deve ser processada com expressões regulares. Há casos em que expressões regulares são uma ótima ferramenta para o trabalho, como ao editar edições únicas em um editor de texto, corrigir arquivos XML quebrados ou lidar com formatos de arquivos que parecem, mas não são, completamente XML. Há alguns problemas a serem observados, mas eles não são intransponíveis ou mesmo necessariamente relevantes.

Uma regex simples como <([^>"']|"[^"]*"|'[^']*')*>geralmente é boa o suficiente, em casos como os que acabei de mencionar. É uma solução ingênua, considerando todas as coisas, mas permite corretamente >símbolos não codificados nos valores dos atributos. Se você estiver procurando, por exemplo, uma tableetiqueta, poderá adaptá-la como </?table\b([^>"']|"[^"]*"|'[^']*')*>.

Apenas para dar uma idéia de como seria uma regex HTML mais "avançada", o seguinte faz um trabalho bastante respeitável de emular o comportamento do navegador do mundo real e o algoritmo de análise HTML5:

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

A seguir, uma definição bastante estrita de tags XML (embora não represente o conjunto completo de caracteres Unicode permitidos nos nomes XML):

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

É verdade que isso não explica o contexto circundante e alguns casos extremos, mas mesmo essas coisas poderiam ser tratadas se você realmente quisesse (por exemplo, pesquisando entre as correspondências de outra regex).

No final do dia, use a ferramenta mais apropriada para o trabalho, mesmo nos casos em que essa ferramenta for uma regex.


49

Embora não seja adequado e eficaz usar expressões regulares para esse fim, às vezes expressões regulares fornecem soluções rápidas para problemas simples de correspondência e, na minha opinião, não é tão horrível usar expressões regulares para trabalhos triviais.

Há um post definitivo no blog sobre a correspondência de elementos HTML mais internos, escritos por Steven Levithan.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.