Question

J'essaie d'extraire les attributs d'une balise d'ancrage (<a>). Jusqu'à présent, j'ai cette expression:

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

qui fonctionne pour des chaînes comme

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

et (guillemets simples)

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

mais pas pour une chaîne sans guillemets:

<a href=test.html class=xyz>

Comment modifier mon expression rationnelle en la faisant fonctionner avec des attributs sans guillemets? Ou y a-t-il une meilleure façon de le faire?

Merci!

Mise à jour: Merci pour tous les bons commentaires et conseils reçus jusqu'à présent. Il y a une chose que je n'ai pas mentionnée: je dois malheureusement corriger / modifier le code non écrit par moi-même. Et il n’ya pas de temps / d’argent pour réécrire ces choses de bas en haut.

Était-ce utile?

La solution

Si vous avez un élément tel que

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

cette expression rationnelle pourrait être utilisée pour trouver successivement chaque nom et valeur d'attribut

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

Appliqué sur:

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

cela donnerait:

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

Remarque: Cela ne fonctionne pas avec les valeurs d'attributs numériques, par exemple. <div id="1"> ne fonctionnera pas.

Autres conseils

Bien que le conseil de ne pas analyser le code HTML via regexp soit valide, voici une expression qui correspond à peu près à ce que vous avez demandé:

/
   \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;

& "Attendez, &" tu pourrais dire. " Qu'en est-il des * commentaires?!?! " Bien, vous pouvez alors remplacer . dans la section non-backtracking par: (Il gère également les sections CDATA.)

(?:[^<]|<[^!]|<![^-\[]|<!\[(?!CDATA)|<!\[CDATA\[.*?\]\]>|<!--(?:[^-]|-[^-])*-->)
  • De même, si vous souhaitez exécuter une substitution sous Perl 5.10 (et je pense PCRE), vous pouvez mettre \K juste avant le nom de l'attribut sans avoir à vous soucier de la capture de tout ce que vous souhaitez ignorer.

Réponse de Token Mantra: vous ne devez pas modifier / modifier / récolter / ou produire de toute autre manière HTML / XML à l'aide d'une expression régulière.

il y a aussi des conditionnelles majuscules comme \ 'et \ " qui doit être pris en compte. Vous feriez bien mieux d'utiliser un analyseur DOM, un analyseur XML ou l'un des nombreux autres outils éprouvés pour ce travail au lieu d'inventer le vôtre.

Je ne me soucie pas vraiment de celui que vous utilisez, tant qu'il est reconnu, testé et que vous en utilisez un.

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. 

Je suis d'accord avec tout le monde: n'analysez pas le code HTML avec regexp.

Il n'est pas possible de créer une expression qui sélectionne des attributs pour un code HTML correct, sans parler de toutes les variantes malformées possibles. Votre expression rationnelle est déjà à peu près illisible, même sans essayer de faire face au manque de guillemets invalide; poursuivez plus loin dans l'horreur du code HTML du monde réel et vous vous rendrez fou avec un flot impossible à maintenir d'expressions non fiables.

Il existe des bibliothèques pour lire le code HTML rompu ou le corriger en XHTML valide que vous pouvez ensuite facilement dévorer avec un analyseur XML. Utilisez-les.

Vous ne pouvez pas utiliser le même nom pour plusieurs captures. Ainsi, vous ne pouvez pas utiliser de quantificateur pour les expressions avec des captures nommées.

Donc, soit & # 8217; t n’utilisez pas de captures nommées:

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

Ou ne pas & # 8217; n'utilisez pas le quantificateur pour cette expression:

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

Ceci autorise également les valeurs d'attributs telles que bar=' baz='quux:

foo="bar=' baz='quux"

L’inconvénient sera que vous devrez effacer les guillemets en début et en fin de séquence.

PHP (PCRE) et Python

Extraction d'attributs simples ( voir que cela fonctionne ):

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

Ou avec la vérification d'ouverture / fermeture de balise, la récupération du nom de balise et l'échappement de commentaires. Cette expression prévoit les guillemets simples / doubles, les guillemets échappés à l'intérieur d'attributs, les espaces autour des signes d'égalité, un nombre différent d'attributs, la recherche des attributs dans les balises et la gestion de guillemets différents dans une valeur d'attribut. ( le voir fonctionner ):

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

(Fonctionne mieux avec les " gisx & "; flags.)

Javascript

Etant donné que Javascript les expressions régulières ne prennent pas en charge les repères, elle ne prend pas en charge la plupart des fonctionnalités des expressions précédentes que je propose. Mais si cela convient aux besoins de quelqu'un, vous pouvez essayer cette version. ( le voir fonctionner ).

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

splattne,

La solution @VonC fonctionne en partie, mais il y a un problème si la balise comportait un mélange de non cités et de citations

.

Celui-ci fonctionne avec des attributs mixtes

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

pour le tester

<?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 contiendrait alors des clés et des valeurs sur les 2ème et 3ème éléments.

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

C’est mon meilleur RegEx pour extraire les propriétés en balise HTML:

# Coupez la correspondance à l'intérieur des guillemets (simples ou doubles)

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

# Sans trim

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

Avantages:

  • Vous pouvez supprimer le contenu à l'intérieur de guillemets.
  • Faites correspondre tous les caractères ASCII spéciaux à l'intérieur des guillemets.
  • Si vous avez title = & "Tu es à moi &"; le RegEx n'est pas cassé

Inconvénients:

  • Il retourne 3 groupes. d'abord la propriété, puis la citation (" | ') et à la fin, la propriété à l'intérieur des guillemets, par exemple: <div title="You're"> le résultat est Groupe 1: titre, Groupe 2: " ;, Groupe 3: Vous êtes.

Voici l'exemple de RegEx en ligne: https://regex101.com/r/aVz4uG/13

J'utilise normalement ce RegEx pour extraire les balises HTML:

Je le recommande si vous n'utilisez pas un type de balise tel que <div, <span, etc.

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

Par exemple:

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

Voici l'exemple de RegEx en ligne: https://regex101.com/r/aVz4uG/15

Le bogue dans ce RegEx est:

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

Dans cette balise:

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

Renvoie <div '> mais il ne devrait y avoir aucune correspondance:

Match:  <div '>

Pour " résoudre " cela supprime le [^/]+? modèle:

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

La réponse # 317081 est bonne mais elle ne correspond pas correctement à ces cas:

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

C'est l'amélioration:

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

vs

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

Évitez les espaces entre les signaux égaux: (\ S +) \ s * = \ s * ((?: ...

Changer le dernier + et. pour: | [> " '))) ? [^ & ";]] ) [&";]]

Voici l'exemple de RegEx en ligne: https://regex101.com/r/aVz4uG/8

quelque chose comme ça pourrait être utile

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

Je vous suggère d'utiliser HTML Tidy pour convertir le code HTML en XHTML, puis d'utiliser un XPath approprié. expression pour extraire les attributs.

Si vous voulez être général, vous devez regarder la spécification précise de la balise a, comme ici . Mais même avec cela, si vous faites votre expression rationnelle parfaite, que se passe-t-il si vous avez du HTML malformé?

Je suggérerais de rechercher une bibliothèque pour analyser le code HTML, en fonction de la langue avec laquelle vous travaillez: par exemple. comme la belle soupe de python.

Si vous utilisez .NET, je vous recommande le pack d'agilité HTML, très robuste, même avec du code HTML mal formé.

Ensuite, vous pouvez utiliser XPath.

Je reconsidérerais la stratégie consistant à n'utiliser qu'une seule expression régulière. Bien sûr, c’est un jeu sympa de proposer une seule expression régulière qui fasse tout. Mais en termes de maintenabilité, vous êtes sur le point de vous tirer une balle dans la main.

Les balises et attributs HTML ont la forme suivante

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

Pour faire correspondre les attributs, vous avez besoin d’une expression régulière attr qui recherche l’un des quatre formulaires. Ensuite, vous devez vous assurer que seules les correspondances sont signalées dans les balises HTML. En supposant que vous ayez la regex correcte, la regex totale serait:

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

Le lookahead garantit que seuls les autres attributs et la balise de fermeture suivent l'attribut. J'utilise l'expression régulière suivante pour $1:

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

Les groupes sans importance sont rendus non capturés. Le premier groupe correspondant $2 vous donne le nom de l'attribut, la valeur est l'un des $3 ou $4 ou $2$3$4. J'utilise <=> pour extraire la valeur. La regex finale est

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

Remarque: j'ai supprimé tous les groupes inutiles dans le look et nous avons rendu tous les groupes restants non capturés.

J'avais aussi besoin de ça et j'ai écrit une fonction pour analyser les attributs, vous pouvez l'obtenir à partir d'ici:

https://gist.github.com/4153580

(Remarque: il n’utilise pas d’expression régulière)

J'ai créé une fonction PHP pouvant extraire les attributs de toutes les balises HTML. Il peut également gérer des attributs tels que disabled sans valeur et déterminer si la balise est une balise autonome (n'a pas de balise de fermeture) ou non (a une balise de fermeture) en vérifiant le content résultat:

/*! 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;
}

Code de test

$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>';
}

Cela fonctionne pour moi. Il prend également en compte certains cas finaux que j'ai rencontrés.

J'utilise cet analyseur syntaxique Regex for XML

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

Extraire l'élément:

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

Utilisez ensuite jQuery pour analyser et extraire le bit souhaité:

$(htmlStr).attr('style') 

regardez ça Regex & amp; PHP - Isoler l'attribut src de la balise img

Peut-être pouvez-vous parcourir le DOM et obtenir les attributs souhaités. Cela fonctionne très bien pour moi, obtenir les attributs du tag body

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top