Pergunta

Eu estou tentando extrair os atributos de uma marca de âncora (<a>). Até agora eu tenho esta expressão:

(?<name>\b\w+\b)\s*=\s*("(?<value>[^"]*)"|'(?<value>[^']*)'|(?<value>[^"'<> \s]+)\s*)+

que funciona para strings como

<a href="test.html" class="xyz">

e (aspas simples)

<a href='test.html' class="xyz">

mas não por uma corda sem aspas:

<a href=test.html class=xyz>

Como posso modificar minha regex fazê-lo funcionar com atributos sem aspas? Ou há uma maneira melhor de fazer isso?

Obrigado!

Update: Obrigado por todos os comentários bons e conselhos até agora. Há uma coisa que eu não mencionei: Eu, infelizmente, tem que patch de código / Modificar não escrita por mim. E não há tempo / dinheiro para reescrever este material a partir do-se inferior.

Foi útil?

Solução

Se você tem um elemento como

<name attribute=value attribute="value" attribute='value'>

este regex poderia ser usado para encontrar sucessivamente cada nome de atributo e valor

(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?

Aplicada em:

<a href=test.html class=xyz>
<a href="test.html" class="xyz">
<a href='test.html' class="xyz">

renderia:

'href' => 'test.html'
'class' => 'xyz'

Nota: Isso não funciona com valores de atributos numéricos por exemplo <div id="1"> não vai funcionar.

Outras dicas

Embora o conselho não para analisar HTML via regexp é válido, aqui está uma expressão que faz muito bem o que você perguntou:

/
   \G                     # start where the last match left off
   (?>                    # begin non-backtracking expression
       .*?                # *anything* until...
       <[Aa]\b            # an anchor tag
    )??                   # but look ahead to see that the rest of the expression
                          #    does not match.
    \s+                   # at least one space
    ( \p{Alpha}           # Our first capture, starting with one alpha
      \p{Alnum}*          # followed by any number of alphanumeric characters
    )                     # end capture #1
    (?: \s* = \s*         # a group starting with a '=', possibly surrounded by spaces.
        (?: (['"])        # capture a single quote character
            (.*?)         # anything else
             \2           # which ever quote character we captured before
        |   ( [^>\s'"]+ ) # any number of non-( '>', space, quote ) chars
        )                 # end group
     )?                   # attribute value was optional
/msx;

"Mas espere", você poderia dizer. "O que sobre os comentários *?!?!" Ok, então você pode substituir o . na seção não-retrocesso com: (. Ele também lida com seções CDATA)

(?:[^<]|<[^!]|<![^-\[]|<!\[(?!CDATA)|<!\[CDATA\[.*?\]\]>|<!--(?:[^-]|-[^-])*-->)
  • Além disso, se você quiser executar uma substituição sob Perl 5.10 (e eu acho que PCRE), você pode colocar \K direita antes do nome do atributo e não precisa se preocupar com a captura de todas as coisas que você quer pular.
resposta

token Mantra: você não deve beliscão / Modificar / colheita / ou produziu html / xml usando expressão regular.

existem também possa condicionais de casos de canto, como \' e \" que devem ser contabilizados. Está muito melhor fora de usar uma adequada DOM Parser, XML Parser, ou uma das muitas outras dezenas de experimentado e ferramentas para testado este trabalho em vez de inventar o seu próprio.

Eu realmente não me importo qual você usa, enquanto a sua reconhecida, testado e você usar um.

my $foo  = Someclass->parse( $xmlstring ); 
my @links = $foo->getChildrenByTagName("a"); 
my @srcs = map { $_->getAttribute("src") } @links; 
# @srcs now contains an array of src attributes extracted from the page. 

Apenas a concordar com todos os outros:. Não analisar HTML usando regexp

Não é possível criar uma expressão que vai escolher atributos para até mesmo um pedaço correto de HTML, não importa todas as variantes possíveis malformados. Seu regexp já praticamente ilegível é mesmo sem tentar lidar com o inválido falta de citações; perseguir ainda mais para o horror de HTML do mundo real e você vai deixar você louca com uma bolha insustentável de expressões não-confiáveis.

Existem bibliotecas existentes para HTML quer ler quebrado, ou corrigi-lo em XHTML que você pode facilmente devorar com um parser XML. Usá-los.

Você não pode usar o mesmo nome para várias capturas. Assim, você não pode usar um quantificador em expressões com capturas nomeadas.

Assim, ou não use capturas nomeadas:

(?:(\b\w+\b)\s*=\s*("[^"]*"|'[^']*'|[^"'<>\s]+)\s+)+

Ou não usar o quantificador sobre esta expressão:

(?<name>\b\w+\b)\s*=\s*(?<value>"[^"]*"|'[^']*'|[^"'<>\s]+)

Isso também não permite valores de atributos como bar=' baz='quux:

foo="bar=' baz='quux"

Bem a desvantagem será que você tem que tirar as aspas esquerda e à direita depois.

PHP (PCRE) e Python

extração de atributos simples ( Veja- de trabalho):

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

Ou com tag de abertura / fechamento de verificação, recuperação nome tag e comentário escapar. Esta expressão prevê não cotadas / citado, / aspas simples, aspas escapou dentro de atributos, os espaços em torno de sinais de igual, número diferente de atributos, marque apenas para atributos dentro de tags e gerenciar cotações diferentes dentro de um valor de atributo. ( vê-lo trabalhando):

(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)

(funciona melhor com as bandeiras "gisx".)


Javascript

Como Javascript expressões regulares não suportam look-behinds, não vai apoiar a maioria dos recursos das expressões anteriores proponho. Mas no caso que poderia atender às necessidades de alguém, você pode tentar esta versão. ( Veja trabalhando ).

(\S+)=[\'"]?((?:(?!\/>|>|"|\'|\s).)+)

splattne,

solução @VonC parte funciona, mas há algum problema se a etiqueta tinha um misto de unquoted e citou

Este funciona com atributos mistos

$pat_attributes = "(\S+)=(\"|'| |)(.*)(\"|'| |>)"

para testá-lo

<?php
$pat_attributes = "(\S+)=(\"|'| |)(.*)(\"|'| |>)"

$code = '    <IMG title=09.jpg alt=09.jpg src="http://example.com.jpg?v=185579" border=0 mce_src="example.com.jpg?v=185579"
    ';

preg_match_all( "@$pat_attributes@isU", $code, $ms);
var_dump( $ms );

$code = '
<a href=test.html class=xyz>
<a href="test.html" class="xyz">
<a href=\'test.html\' class="xyz">
<img src="http://"/>      ';

preg_match_all( "@$pat_attributes@isU", $code, $ms);

var_dump( $ms );

$ ms, então, contêm chaves e valores no 2º e 3º elemento.

$keys = $ms[1];
$values = $ms[2];

Este é o meu melhor RegEx para propriedades extrato em Tag HTML:

# Apare o interior jogo das citações (simples ou dupla)

(\S+)\s*=\s*([']|["])\s*([\W\w]*?)\s*\2

# Sem guarnição

(\S+)\s*=\s*([']|["])([\W\w]*?)\2

Pros:

  • Você é capaz de cortar o interior conteúdo de citações.
  • combinar todos os caracteres especiais de ASCII dentro das aspas.
  • Se você tiver title = "Você é minha" faz o RegEx não quebrado

Contras:

  • Ele retorna 3 grupos; . Pela primeira vez a propriedade, em seguida, a citação ( "|") e no final da propriedade dentro das citações i.e .: <div title="You're"> o resultado é o Grupo 1: título, Grupo 2:", Grupo 3: Você é

Este é o exemplo RegEx on-line: https://regex101.com/r/aVz4uG/13



Eu normalmente uso este RegEx para extrair o HTML Tags:

Eu recomendo este se você não usar um tipo de tag como <div, <span, etc.

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

Por exemplo:

<div title="a>b=c<d" data-type='a>b=c<d'>Hello</div>
<span style="color: >=<red">Nothing</span>
# Returns 
# <div title="a>b=c<d" data-type='a>b=c<d'>
# <span style="color: >=<red">

Este é o exemplo RegEx on-line: https://regex101.com/r/aVz4uG/15

O bug nesta RegEx é:

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

Neste tag:

<article title="a>b=c<d" data-type='a>b=c<div '>Hello</article>

Retorna <div '> mas não deve retornar qualquer jogo:

Match:  <div '>

Para "resolver" este remove o padrão [^/]+?:

<div(?:\".*?\"|'.*?'|.*?)*?>


A resposta # 317081 é bom, mas não corresponder corretamente com estes casos:

<div id="a"> # It returns "a instead of a
<div style=""> # It doesn't match instead of return only an empty property
<div title = "c"> # It not recognize the space between the equal (=)

Esta é a melhoria:

(\S+)\s*=\s*["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))?[^"']*)["']?

vs

(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?

Evite os espaços entre sinal de igual: (\ S +) \ s * = \ s * ((?: ...

Alterar a última + e. para: |? [> "']?)) [^" '] * ) [ "']

Este é o exemplo RegEx on-line: https://regex101.com/r/aVz4uG/8

algo como isso poderia ser útil

'(\S+)\s*?=\s*([\'"])(.*?|)\2

Eu sugiro que você use HTML Tidy para converter o HTML para XHTML, e então usar um XPath adequado expressão para extrair os atributos.

Se você quer ser geral, você tem que olhar para a especificação precisa da uma tag, como aqui . Mas mesmo com isso, se você fizer sua regexp perfeito, e se você tem malformado html?

Gostaria de sugerir para ir para uma biblioteca para analisar HTML, dependendo do idioma que você trabalha com: por exemplo, como bonito Sopa de python.

Se você está em .NET eu recomendo o pacote de agilidade HTML, muito robusta, mesmo com HTML malformado.

Então você pode usar XPath.

Eu reconsiderar a estratégia a utilizar apenas uma única expressão regular. Claro que é um bom jogo para chegar a uma expressão regular único que faz tudo. Mas em termos de maintainabilty você está prestes a atirar-se em ambos os pés.

tags e atributos em HTML têm a forma

<tag 
   attrnovalue 
   attrnoquote=bli 
   attrdoublequote="blah 'blah'"
   attrsinglequote='bloob "bloob"' >

Para coincidir com atributos, você precisa de um attr regex que encontra uma das quatro formas. Então você precisa ter certeza de que somente os jogos são relatados dentro de tags HTML. Supondo que você tenha a regex correto, o regex total seria:

attr(?=(attr)*\s*/?\s*>)

Os garante lookahead que apenas outros atributos e a marca de fechamento seguem o atributo. Eu uso a seguinte expressão regular para attr:

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

grupos sem importância são feitos não captura. O primeiro $1 grupo correspondente dá-lhe o nome do atributo, o valor é um dos $2 $3or ou $4. Eu uso $2$3$4 para extrair o valor. A regex final é

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

Nota: eu removi todos os grupos desnecessários no lookahead e fez todos os restantes grupos não captura

.

Eu também precisava disso e escreveu uma função para analisar atributos, você pode obtê-lo aqui:

https://gist.github.com/4153580

(Nota: Ele não usa regex)

Eu criei um função href="https://gist.github.com/tovic/b3b683f28d899e19f830" rel="nofollow"> PHP que poderiam extrair atributos de qualquer tags HTML. Ele também pode lidar com atributos como disabled que não tem valor, e também pode determinar se a tag é uma tag stand-alone (não tem tag de fechamento) ou não (tem uma marca de fechamento), verificando o resultado content:

/*! Based on <https://github.com/mecha-cms/cms/blob/master/system/kernel/converter.php> */
function extract_html_attributes($input) {
    if( ! preg_match('#^(<)([a-z0-9\-._:]+)((\s)+(.*?))?((>)([\s\S]*?)((<)\/\2(>))|(\s)*\/?(>))$#im', $input, $matches)) return false;
    $matches[5] = preg_replace('#(^|(\s)+)([a-z0-9\-]+)(=)(")(")#i', '$1$2$3$4$5<attr:value>$6', $matches[5]);
    $results = array(
        'element' => $matches[2],
        'attributes' => null,
        'content' => isset($matches[8]) && $matches[9] == '</' . $matches[2] . '>' ? $matches[8] : null
    );
    if(preg_match_all('#([a-z0-9\-]+)((=)(")(.*?)("))?(?:(\s)|$)#i', $matches[5], $attrs)) {
        $results['attributes'] = array();
        foreach($attrs[1] as $i => $attr) {
            $results['attributes'][$attr] = isset($attrs[5][$i]) && ! empty($attrs[5][$i]) ? ($attrs[5][$i] != '<attr:value>' ? $attrs[5][$i] : "") : $attr;
        }
    }
    return $results;
}

Código de Teste

$test = array(
    '<div class="foo" id="bar" data-test="1000">',
    '<div>',
    '<div class="foo" id="bar" data-test="1000">test content</div>',
    '<div>test content</div>',
    '<div>test content</span>',
    '<div>test content',
    '<div></div>',
    '<div class="foo" id="bar" data-test="1000"/>',
    '<div class="foo" id="bar" data-test="1000" />',
    '< div  class="foo"     id="bar"   data-test="1000"       />',
    '<div class id data-test>',
    '<id="foo" data-test="1000">',
    '<id data-test>',
    '<select name="foo" id="bar" empty-value-test="" selected disabled><option value="1">Option 1</option></select>'
);

foreach($test as $t) {
    var_dump($t, extract_html_attributes($t));
    echo '<hr>';
}

Isso funciona para mim. Também levar em consideração alguns casos finais que tenho encontrado.

Eu estou usando este Regex para parser XML

(?<=\s)[^><:\s]*=*(?=[>,\s])

Extraia o elemento:

var buttonMatcherRegExp=/<a[\s\S]*?>[\s\S]*?<\/a>/;
htmlStr=string.match( buttonMatcherRegExp )[0]

Em seguida, use jQuery para analisar e extrair o pouco que você deseja:

$(htmlStr).attr('style') 

ter um olhar para este Regex & PHP - isolado src atributo do tag img

talvez você pode percorrer o DOM e obter os atributos desejados. Ele funciona muito bem para mim, ficando atributos do corpo-tag

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top