Parece que todas as perguntas no stackoverflow em que o solicitante está usando o regex para obter algumas informações do HTML terão inevitavelmente uma "resposta" que diz para não usar o regex para analisar o HTML.
Por que não? Estou ciente de que existem analisadores HTML "reais" entre aspas, como Beautiful Soup , e tenho certeza de que são poderosos e úteis, mas se você está apenas fazendo algo simples, rápido ou sujo, por que se preocupa em usar algo tão complicado quando algumas instruções regex funcionarão bem?
Além disso, existe algo fundamental que eu não entendo sobre o regex que os torna uma má escolha para analisar em geral?
regex
html-parsing
Ntownsend
fonte
fonte
Respostas:
A análise HTML inteira não é possível com expressões regulares, pois depende da correspondência entre a abertura e a tag de fechamento, o que não é possível com as expressões regulares.
Expressões regulares podem corresponder apenas a linguagens regulares, mas HTML é uma linguagem livre de contexto e não uma linguagem regular (como o @StefanPochmann apontou, as linguagens regulares também são livres de contexto, portanto, sem contexto não significa necessariamente não regular). A única coisa que você pode fazer com regexps em HTML é heurística, mas isso não funcionará em todas as condições. Deve ser possível apresentar um arquivo HTML que corresponderá incorretamente a qualquer expressão regular.
fonte
Para quickexnirty, o regexp se sairá bem. Mas o fundamental é saber que é impossível construir um regexp que analise corretamente o HTML.
O motivo é que os regexps não podem manipular expressões aninhadas arbitrariamente. Consulte Expressões regulares podem ser usadas para corresponder a padrões aninhados?
fonte
(Em http://htmlparsing.com/regexes )
Digamos que você tenha um arquivo HTML onde está tentando extrair URLs das tags <img>.
Então você escreve um regex como este no Perl:
Nesse caso,
$url
conterá de fatohttp://example.com/whatever.jpg
. Mas o que acontece quando você começa a obter HTML assim:ou
ou
ou
ou você começa a receber falsos positivos de
Parece tão simples e pode ser simples para um único arquivo imutável, mas para qualquer coisa que você esteja fazendo com dados HTML arbitrários, as expressões regulares são apenas uma receita para futuras mágoas.
fonte
Duas razões rápidas:
Quanto à adequação de expressões regulares para análise em geral: elas não são adequadas. Você já viu os tipos de expressões regulares necessárias para analisar a maioria dos idiomas?
fonte
No que diz respeito à análise, expressões regulares podem ser úteis no estágio "análise lexical" (lexer), onde a entrada é dividida em tokens. É menos útil no estágio "construir uma árvore de análise" real.
Para um analisador de HTML, espero que ele aceite apenas HTML bem formado e que exija recursos fora do que uma expressão regular pode fazer (eles não podem "contar" e garantir que um determinado número de elementos de abertura seja equilibrado pelo mesmo número) de elementos de fechamento).
fonte
Como existem muitas maneiras de "estragar" o HTML que os navegadores tratam de uma maneira bastante liberal, seria necessário bastante esforço para reproduzir o comportamento liberal do navegador para cobrir todos os casos com expressões regulares, portanto sua regex falhará inevitavelmente em alguns itens especiais. casos, e isso possivelmente introduziria sérias falhas de segurança em seu sistema.
fonte
O problema é que a maioria dos usuários que fazem uma pergunta relacionada ao HTML e à regex o fazem porque não conseguem encontrar uma própria regex que funcione. Então é preciso pensar se tudo seria mais fácil ao usar um analisador DOM ou SAX ou algo semelhante. Eles são otimizados e construídos com o objetivo de trabalhar com estruturas de documentos semelhantes a XML.
Claro, existem problemas que podem ser resolvidos facilmente com expressões regulares. Mas a ênfase está na facilidade .
Se você quiser apenas encontrar todos os URLs que parecem
http://.../
bons com os regexps. Mas se você deseja encontrar todos os URLs que estão em um elemento a que tem a classe 'mylink', provavelmente é melhor usar um analisador apropriado.fonte
As expressões regulares não foram projetadas para lidar com uma estrutura de marca aninhada e, na melhor das hipóteses, é complicado (na pior das hipóteses, impossível) lidar com todos os casos de borda possíveis que você obtém com HTML real.
fonte
Eu acredito que a resposta está na teoria da computação. Para que um idioma seja analisado usando regex, ele deve ser por definição "regular" ( link ). O HTML não é uma linguagem comum, pois não atende a vários critérios para uma linguagem regular (muito a ver com os vários níveis de aninhamento inerentes ao código html). Se você estiver interessado na teoria da computação, eu recomendaria este livro.
fonte
Essa expressão recupera atributos de elementos HTML. Suporta:
(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)
Confira . Funciona melhor com os sinalizadores "gisx", como na demonstração.
fonte
<script>
tag.HTML / XML é dividido em marcação e conteúdo. Regex é útil apenas na análise de tags lexicais. Eu acho que você poderia deduzir o conteúdo. Seria uma boa escolha para um analisador SAX. Tags e conteúdo podem ser entregues a uma função definida pelo usuário, onde o aninhamento / fechamento de elementos pode ser mantido.
Na medida em que apenas analisa as tags, isso pode ser feito com regex e usado para retirar as tags de um documento.
Ao longo de anos de testes, descobri o segredo da maneira como os navegadores analisam as tags, bem e mal formadas.
Os elementos normais são analisados com este formulário:
O núcleo dessas tags usa esse regex
Você notará isso
[^>]?
como uma das alternâncias. Isso corresponderá a aspas desequilibradas de tags mal formadas.É também a mais raiz de todos os males às expressões regulares. A maneira como é usada aciona um bump-along para satisfazer seu recipiente quantificado ganancioso e que deve corresponder.
Se usado passivamente, nunca há um problema Mas, se você forçar a correspondência de algo, intercalando-o com um par de atributo / valor desejado e não fornecer proteção adequada contra retorno, é um pesadelo fora de controle.
Essa é a forma geral para tags antigas simples. Observe a
[\w:]
representação do nome da tag? Na realidade, os caracteres legais que representam o nome da marca são uma lista incrível de caracteres Unicode.Continuando, também vemos que você não pode procurar uma tag específica sem analisar TODAS as tags. Quero dizer que você poderia, mas teria que usar uma combinação de verbos como (* SKIP) (* FAIL), mas ainda assim todas as tags precisam ser analisadas.
O motivo é que a sintaxe das tags pode estar oculta dentro de outras tags, etc.
Portanto, para analisar passivamente todas as tags, é necessário um regex como o abaixo. Este em particular também combina com conteúdo invisível .
À medida que o novo HTML ou xml ou qualquer outro desenvolver novas construções, adicione-o como uma das alternativas.
Nota da página da Web - nunca vi uma página da Web (ou xhtml / xml) com a qual
havia problemas. Se você encontrar um, me avise.
Nota de desempenho - É rápido. Este é o analisador de tags mais rápido que eu já vi
(pode ser mais rápido, quem sabe).
Eu tenho várias versões específicas. Também é excelente como raspador
(se você é do tipo hands-on).
Regex bruto completo
<(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\s+(?>"[\S\s]*?"|'[\S\s]*?'|(?:(?!/>)[^>])?)+)?\s*>)[\S\s]*?</\1\s*(?=>))|(?:/?[\w:]+\s*/?)|(?:[\w:]+\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)+\s*/?)|\?[\S\s]*?\?|(?:!(?:(?:DOCTYPE[\S\s]*?)|(?:\[CDATA\[[\S\s]*?\]\])|(?:--[\S\s]*?--)|(?:ATTLIST[\S\s]*?)|(?:ENTITY[\S\s]*?)|(?:ELEMENT[\S\s]*?))))>
Aparência formatada
fonte
"Depende" embora. É verdade que as expressões regulares não podem e não podem analisar HTML com precisão verdadeira, por todos os motivos apresentados aqui. Se, no entanto, as conseqüências de errar (como não manipular tags aninhadas) são menores e se as expressões regulares são super convenientes no seu ambiente (como quando você está invadindo o Perl), vá em frente.
Suponha que você esteja, oh, talvez analisando páginas da web com links para o seu site - talvez você as tenha encontrado com uma pesquisa no link do Google - e deseje uma maneira rápida de ter uma idéia geral do contexto ao redor do seu link. Você está tentando gerar um pequeno relatório que pode alertá-lo para vincular spam, algo assim.
Nesse caso, interpretar incorretamente alguns dos documentos não será um grande problema. Ninguém, exceto você, verá os erros e, se tiver muita sorte, haverá poucos o suficiente para que você possa acompanhar individualmente.
Acho que estou dizendo que é uma troca. Às vezes, implementar ou usar um analisador correto - por mais fácil que seja - pode não valer a pena se a precisão não for crítica.
Apenas tenha cuidado com suas suposições. Posso pensar em algumas maneiras pelas quais o atalho regexp pode sair pela culatra se você estiver tentando analisar algo que será mostrado em público, por exemplo.
fonte
Definitivamente, existem casos em que o uso de uma expressão regular para analisar algumas informações do HTML é o caminho correto a seguir - depende muito da situação específica.
O consenso acima é que, em geral, é uma má ideia. No entanto, se a estrutura HTML for conhecida (e provavelmente não será alterada), ainda será uma abordagem válida.
fonte
Lembre-se de que, embora o próprio HTML não seja regular, partes da página que você está visualizando podem ser regulares.
Por exemplo, é um erro que as
<form>
tags sejam aninhadas; se a página da web estiver funcionando corretamente, o uso de uma expressão regular para obter um<form>
seria completamente razoável.Recentemente, fiz algumas raspagens na Web usando apenas Selenium e expressões regulares. I conseguiu acabar com ela porque os dados que eu queria era colocar em um
<form>
, e colocar em um formato de tabela simples (de modo que eu pudesse contar com<table>
,<tr>
e<td>
para ser não-aninhados - que na verdade é altamente incomum). Em certo grau, expressões regulares eram quase necessárias, porque parte da estrutura que eu precisava acessar era delimitada por comentários. (A Beautiful Soup pode fazer comentários, mas teria sido difícil agarrar<!-- BEGIN -->
e<!-- END -->
bloquear usando a Beautiful Soup.)Se eu tivesse que me preocupar com tabelas aninhadas, no entanto, minha abordagem simplesmente não teria funcionado! Eu teria que recorrer à Beautiful Soup. Mesmo assim, às vezes, você pode usar uma expressão regular para agarrar o pedaço necessário e, em seguida, detalhar a partir daí.
fonte
Na verdade, a análise de HTML com regex é perfeitamente possível no PHP. Você só precisa analisar a cadeia inteira de trás
strrpos
para frente para localizar<
e repetir a regex de lá, usando especificadores não agradáveis a cada vez para superar as tags aninhadas. Não é chique e muito lento em coisas grandes, mas eu o usei para o meu próprio editor de modelos pessoal para o meu site. Na verdade, eu não estava analisando HTML, mas algumas tags personalizadas que fiz para consultar entradas do banco de dados para exibir tabelas de dados (minha<#if()>
tag poderia destacar entradas especiais dessa maneira). Eu não estava preparado para usar um analisador XML em apenas algumas tags criadas automaticamente (com dados muito não XML) aqui e ali.Portanto, mesmo que essa pergunta esteja consideravelmente morta, ela ainda aparece em uma pesquisa no Google. Eu li e pensei "desafio aceito" e terminei de corrigir meu código simples sem precisar substituir tudo. Decidiu oferecer uma opinião diferente para quem procura por um motivo semelhante. Além disso, a última resposta foi postada há 4 horas, por isso esse ainda é um tópico importante.
fonte
<tag >
) Você considerou tags de fechamento comentadas? (Por exemplo,<tag> <!-- </tag> -->
) Você considerou o CDATA? Você considerou tags de casos inconsistentes? (Por exemplo,<Tag> </tAG>
) Você considerou isso também?Eu tentei minha mão em um regex para isso também. É principalmente útil para encontrar partes do conteúdo emparelhadas com a próxima tag HTML, e não procura por tags fechadas correspondentes , mas seleciona as tags fechadas. Role uma pilha no seu próprio idioma para verificá-las.
Use com as opções 'sx'. 'g' também se você estiver com sorte:
Este foi desenvolvido para Python (pode funcionar para outras linguagens, ainda não o testou, usa visões positivas, visões negativas e referências remotas). Apoia:
<div ...>
</div>
<!-- ... -->
<![CDATA[ ... ]]>
<div .../>
<input checked>
<div style='...'>
<div style="...">
<a title='John\'s Story'>
(esse HTML não é realmente válido, mas eu sou um cara legal)
<a href = '...'>
Também é muito bom não acionar tags malformadas, como quando você esquece um
<
ou>
.Se o seu sabor regex suporta capturas nomeadas repetidas, você é dourado, mas o Python
re
não (eu sei que o regex sim, mas preciso usar o baunilha Python). Aqui está o que você recebe:content
- Todo o conteúdo até a próxima tag. Você poderia deixar isso de fora.markup
- A tag inteira com tudo nela.comment
- Se for um comentário, o conteúdo do comentário.cdata
- Se for um<![CDATA[...]]>
, o conteúdo do CDATA.close_tag
- Se for uma marca fechada (</div>
), o nome da marca.tag
- Se for uma tag aberta (<div>
), o nome da tag.attributes
- Todos os atributos dentro da tag. Use isso para obter todos os atributos se você não receber grupos repetidos.attribute
- Repetido, cada atributo.attribute_name
- Repetido, cada nome de atributo.attribute_value
- Repetido, cada valor de atributo. Isso inclui as aspas, se foi citado.is_self_closing
- Isso é/
se for uma tag de fechamento automático, caso contrário nada._q
e_v
- ignore-os; eles são usados internamente para referências anteriores.Se o seu mecanismo de expressão regular não suportar capturas nomeadas repetidas, há uma seção chamada que você pode usar para obter cada atributo. Basta executar esse regex no
attributes
grupo para obter cadaattribute
,attribute_name
eattribute_value
fora dele.Demonstração aqui: https://regex101.com/r/mH8jSu/11
fonte
Expressões regulares não são poderosas o suficiente para uma linguagem como HTML. Claro, existem alguns exemplos em que você pode usar expressões regulares. Mas, em geral, não é apropriado para a análise.
fonte
Você sabe ... há muita mentalidade em que você NÃO PODE fazê-lo e acho que todo mundo dos dois lados da cerca está certo e errado. Você pode fazê-lo, mas é preciso um pouco mais de processamento do que apenas executar um regex nele. Tome isso (escrevi isso dentro de uma hora) como exemplo. Ele assume que o HTML é completamente válido, mas, dependendo do idioma que você está usando para aplicar a regex acima mencionada, você pode fazer algumas correções no HTML para garantir que ele seja bem-sucedido. Por exemplo, removendo tags de fechamento que não deveriam estar lá:
</img>
por exemplo. Em seguida, adicione a barra de fechamento HTML única de fechamento aos elementos que estão faltando, etc.Eu usaria isso no contexto de escrever uma biblioteca que me permitiria recuperar elementos HTML semelhantes aos do JavaScript
[x].getElementsByTagName()
, por exemplo. Eu apenas dividia a funcionalidade que escrevi na seção DEFINE do regex e a usava para entrar em uma árvore de elementos, uma de cada vez.Então, essa será a resposta final de 100% para a validação de HTML? Não. Mas é um começo e com um pouco mais de trabalho, isso pode ser feito. No entanto, tentar fazê-lo dentro de uma execução de regex não é prático nem eficiente.
fonte