Utiliser des expressions régulières pour analyser HTML: pourquoi pas?


208

Il semble que chaque question sur stackoverflow où le demandeur utilise regex pour récupérer des informations HTML aura inévitablement une "réponse" qui dit de ne pas utiliser regex pour analyser HTML.

Pourquoi pas? Je suis conscient qu'il existe de "vrais" analyseurs HTML entre guillemets comme Beautiful Soup , et je suis sûr qu'ils sont puissants et utiles, mais si vous faites simplement quelque chose de simple, rapide ou sale, alors pourquoi vous dérangez d'utiliser quelque chose de si compliqué quand quelques instructions regex fonctionneront très bien?

De plus, y a-t-il juste quelque chose de fondamental que je ne comprends pas à propos des regex qui en fait un mauvais choix pour l'analyse en général?


3
je pense que c'est une dupe de stackoverflow.com/questions/133601
jcrossley3

23
Parce que seul Chuck Norris peut analyser HTML avec regex (comme expliqué dans cette fameuse chose Zalgo: stackoverflow.com/questions/1732348/… ).
takehin

1
Cette question m'a incité à en poser une autre qui est en quelque sorte liée. Si vous êtes intéressé: pourquoi il n'est pas possible d'utiliser des expressions rationnelles pour analyser HTML / XML: une explication formelle en termes simples
mac


Cette question a été ajoutée à la FAQ sur l'expression régulière de débordement de pile , sous "Tâches de validation courantes".
aliteralmind

Réponses:


212

L'analyse syntaxique HTML entière n'est pas possible avec les expressions régulières, car elle dépend de la correspondance de la balise d'ouverture et de fermeture, ce qui n'est pas possible avec les expressions rationnelles.

Les expressions régulières ne peuvent correspondre qu'à des langues régulières, mais HTML est un langage sans contexte et non pas un langage régulier (Comme l'a souligné @StefanPochmann, les langues régulières sont également sans contexte, donc sans contexte ne signifie pas nécessairement non régulier). La seule chose que vous pouvez faire avec les expressions rationnelles en HTML est l'heuristique mais cela ne fonctionnera pas dans toutes les conditions. Il devrait être possible de présenter un fichier HTML qui ne correspondra pas correctement à n'importe quelle expression régulière.


26
Jusqu'à présent, la meilleure réponse. S'il ne peut correspondre qu'à des grammaires normales, nous aurions besoin d'une expression rationnelle infiniment grande pour analyser une grammaire sans contexte comme HTML. J'aime quand ces choses ont des réponses théoriques claires.
ntownsend le

2
J'ai supposé que nous discutions de regex de type Perl où elles ne sont pas en fait des expressions régulières.
Hank Gay

5
En fait, les expressions régulières .Net peuvent faire correspondre l'ouverture et les balises de fermeture, dans une certaine mesure, en utilisant des groupes d'équilibrage et une expression soigneusement conçue. Contenir tout cela dans une expression rationnelle est toujours fou bien sûr, il ressemblerait au grand code Chtulhu et invoquerait probablement le vrai aussi. Et à la fin, cela ne fonctionnera toujours pas dans tous les cas. Ils disent que si vous écrivez une expression régulière qui peut analyser correctement tout HTML, l'univers s'effondrera sur lui-même.
Alex Paven

5
Certaines bibliothèques d'expressions régulières peuvent faire des expressions régulières récursives (ce qui en fait des expressions non régulières :)
Ondra Žižka

43
-1 Cette réponse tire la bonne conclusion ("C'est une mauvaise idée d'analyser HTML avec Regex") à partir d'arguments incorrects ("Parce que HTML n'est pas un langage normal"). La chose que la plupart des gens veulent dire de nos jours quand ils disent "regex" (PCRE) est bien capable non seulement d'analyser des grammaires sans contexte (c'est trivial en fait), mais aussi des grammaires contextuelles (voir stackoverflow.com/questions/7434272/ … ).
NikiC

35

Pour regexp quick´n´dirty fera l'affaire. Mais la chose fondamentale à savoir est qu'il est impossible de construire une expression rationnelle qui analysera correctement le HTML.

La raison en est que les expressions rationnelles ne peuvent pas gérer les expressions imbriquées arbitrairement. Voir Les expressions régulières peuvent-elles être utilisées pour faire correspondre des modèles imbriqués?


1
Certaines bibliothèques d'expression régulière peuvent faire des expressions régulières récursives (ce qui en fait des expressions non régulières :)
Ondra Žižka

23

(Sur http://htmlparsing.com/regexes )

Supposons que vous ayez un fichier HTML dans lequel vous essayez d'extraire des URL à partir de balises <img>.

<img src="http://example.com/whatever.jpg">

Vous écrivez donc une expression régulière comme celle-ci en Perl:

if ( $html =~ /<img src="(.+)"/ ) {
    $url = $1;
}

Dans ce cas, $urlcontiendra en effet http://example.com/whatever.jpg. Mais que se passe-t-il lorsque vous commencez à obtenir du HTML comme ceci:

<img src='http://example.com/whatever.jpg'>

ou

<img src=http://example.com/whatever.jpg>

ou

<img border=0 src="http://example.com/whatever.jpg">

ou

<img
    src="http://example.com/whatever.jpg">

ou vous commencez à obtenir des faux positifs

<!-- // commented out
<img src="http://example.com/outdated.png">
-->

Cela semble si simple, et cela pourrait être simple pour un seul fichier immuable, mais pour tout ce que vous allez faire sur des données HTML arbitraires, les expressions rationnelles ne sont qu'une recette pour de futurs chagrins.


4
Cela semble être la vraie réponse - alors qu'il est probablement possible d'analyser du code HTML arbitraire avec des expressions régulières, car les expressions régulières d'aujourd'hui sont plus qu'un simple automate fini, afin d'analyser du code HTML arbitraire et pas seulement une page concrète, vous devez réimplémenter un analyseur HTML dans regexp et les regex deviennent sûrement 1000 fois illisibles.
Smit Johnth

1
Hey Andy, j'ai pris le temps de trouver une expression qui soutienne vos cas mentionnés. stackoverflow.com/a/40095824/1204332 Faites-moi savoir ce que vous en pensez! :)
Ivan Chaer

2
Le raisonnement de cette réponse est bien dépassé et s'applique encore moins aujourd'hui qu'il ne le faisait à l'origine (ce qui, je pense, ne l'a pas été). (Citant OP: "si vous faites juste quelque chose de simple, rapide ou sale ...".)
Sz.

16

Deux raisons rapides:

  • il est difficile d'écrire une expression régulière capable de résister aux entrées malveillantes; beaucoup plus difficile que d'utiliser un outil préconstruit
  • écrire une expression régulière qui peut fonctionner avec le balisage ridicule avec lequel vous serez inévitablement coincé est difficile; beaucoup plus difficile que d'utiliser un outil préconstruit

Concernant l'adéquation des expressions rationnelles pour l'analyse en général: elles ne conviennent pas. Avez-vous déjà vu le genre de regex dont vous auriez besoin pour analyser la plupart des langues?


2
Sensationnel? Un downvote après 2+ ans? Au cas où quelqu'un se poserait la question, je n'ai pas dit "Parce que c'est théoriquement impossible" parce que la question posait clairement la question "rapide et sale", pas "correcte". Le PO a clairement déjà lu les réponses qui couvraient le territoire théoriquement impossible et n'était toujours pas satisfait.
Hank Gay

1
Avoir un vote positif après 5+ ans. :) Quant à savoir pourquoi vous pourriez avoir reçu le downvote, je ne suis pas qualifié pour le dire, mais personnellement, j'aurais aimé voir quelques exemples ou explications plutôt que la question rhétorique de clôture.
Adam Jensen

3
Essentiellement, toutes les analyses html rapides et sales qui sont effectuées dans l'expédition de produits ou d'outils internes finissent par être une faille de sécurité béante ou un bug en attente de se produire. Il faut le décourager avec enthousiasme. Si l'on peut utiliser un regex, on peut utiliser un analyseur html approprié.
Rétablir Monica le

16

En ce qui concerne l'analyse, les expressions régulières peuvent être utiles dans l'étape "analyse lexicale" (lexer), où l'entrée est décomposée en jetons. Il est moins utile à l'étape réelle de la "construction d'un arbre d'analyse".

Pour un analyseur HTML, je m'attendrais à ce qu'il n'accepte que du HTML bien formé et qui nécessite des capacités en dehors de ce qu'une expression régulière peut faire (ils ne peuvent pas "compter" et s'assurer qu'un nombre donné d'éléments d'ouverture sont équilibrés par le même nombre des éléments de fermeture).


8

Parce qu'il existe de nombreuses façons de "bousiller" le HTML que les navigateurs traiteront de manière plutôt libérale, mais il faudrait un certain effort pour reproduire le comportement libéral du navigateur pour couvrir tous les cas avec des expressions régulières, donc votre expression régulière échouera inévitablement sur certains cas, et cela pourrait introduire de graves lacunes de sécurité dans votre système.


1
Très vrai, la majorité du HTML semble être horrible. Je ne comprends pas comment une expression régulière défaillante peut introduire de graves failles de sécurité. Pouvez-vous donner un exemple?
ntownsend le

4
ntownsend: Par exemple, vous pensez que vous avez supprimé toutes les balises de script du HTML mais votre regex échoue couvre un cas spécial (qui, disons, ne fonctionne que sur IE6): boom, vous avez une vulerability XSS!
Tamas Czinege

1
C'était un exemple strictement hypothétique car la plupart des exemples du monde réel sont trop compliqués pour tenir dans ces commentaires, mais vous pouvez en trouver quelques-uns en recherchant rapidement sur le sujet.
Tamas Czinege

3
+1 pour avoir mentionné l'angle de sécurité. Lorsque vous vous connectez à Internet dans son intégralité, vous ne pouvez pas vous permettre d'écrire du code hacky «fonctionne la plupart du temps».
j_random_hacker

7

Le problème est que la plupart des utilisateurs qui posent une question concernant HTML et regex le font parce qu'ils ne peuvent pas trouver une propre regex qui fonctionne. Ensuite, il faut se demander si tout serait plus facile lors de l'utilisation d'un analyseur DOM ou SAX ou quelque chose de similaire. Ils sont optimisés et construits dans le but de travailler avec des structures de documents de type XML.

Bien sûr, il existe des problèmes qui peuvent être résolus facilement avec des expressions régulières. Mais l'accent est mis sur facilement .

Si vous voulez simplement trouver toutes les URL qui ressemblent à http://.../des regexps. Mais si vous voulez trouver toutes les URL qui sont dans un élément a qui a la classe «mylink», vous feriez probablement mieux d'utiliser un analyseur approprié.


6

Les expressions régulières n'ont pas été conçues pour gérer une structure de balise imbriquée, et il est au mieux compliqué (au pire, impossible) de gérer tous les cas de bord possibles que vous obtenez avec du vrai HTML.


6

Je crois que la réponse réside dans la théorie du calcul. Pour qu'une langue soit analysée à l'aide d'expressions rationnelles, elle doit être par définition "régulière" ( lien ). HTML n'est pas un langage standard car il ne répond pas à un certain nombre de critères pour un langage régulier (beaucoup à voir avec les nombreux niveaux d'imbrication inhérents au code html). Si vous êtes intéressé par la théorie du calcul, je recommanderais ce livre.


1
J'ai lu ce livre. Il ne m'est tout simplement pas venu à l'esprit que le HTML est un langage sans contexte.
ntownsend le

4

Cette expression récupère les attributs des éléments HTML. Elle supporte:

  • attributs non cotés / entre guillemets,
  • guillemets simples / doubles,
  • échappé des guillemets à l'intérieur des attributs,
  • des espaces autour de signes égaux,
  • un nombre quelconque d'attributs,
  • vérifier uniquement les attributs à l'intérieur des balises,
  • échapper aux commentaires, et
  • gérer différentes citations dans une valeur d'attribut.

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

Vérifiez-le . Cela fonctionne mieux avec les drapeaux "gisx", comme dans la démo.


1
C'est très intéressant. Pas lisible, probablement difficile à déboguer mais quand même: Travail impressionnant!
Eric Duminil

Cela suppose encore vaguement que le HTML est bien formé. Sans correspondance de contexte, cela correspondra aux URL apparentes dans des contextes où vous ne voulez généralement pas les faire correspondre, comme dans un morceau de code JavaScript à l'intérieur d'une <script>balise.
tripleee

4

HTML / XML est divisé en balisage et contenu. Regex n'est utile que pour effectuer une analyse de balise lexicale. Je suppose que vous pouvez en déduire le contenu. Ce serait un bon choix pour un analyseur SAX. Les balises et le contenu peuvent être fournis à une fonction définie par l'utilisateur où l'imbrication / fermeture des éléments peut être conservée.

En ce qui concerne simplement l'analyse des balises, cela peut être fait avec regex et utilisé pour retirer les balises d'un document.

Au fil des années de tests, j'ai trouvé le secret de la façon dont les navigateurs analysent les balises, bien ou mal formées.

Les éléments normaux sont analysés avec cette forme:

Le noyau de ces balises utilise cette expression régulière

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

Vous remarquerez cela [^>]?comme l'une des alternances. Cela correspondra à des citations déséquilibrées de balises mal formées.

C'est aussi la racine la plus simple de tous les maux aux expressions régulières. La façon dont il est utilisé déclenchera un bump-along pour satisfaire son conteneur quantifié gourmand et incontournable.

S'il est utilisé de manière passive, il n'y a jamais de problème Mais, si vous forcez quelque chose à correspondre en l'entrecoupant avec une paire attribut / valeur souhaitée, et ne fournissez pas une protection adéquate contre le retour en arrière, c'est un cauchemar hors de contrôle.

Ceci est la forme générale des anciennes balises. Remarquez le [\w:]représentant du nom de la balise? En réalité, les caractères légaux représentant le nom de la balise sont une incroyable liste de caractères Unicode.

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

En passant, nous constatons également que vous ne pouvez tout simplement pas rechercher une balise spécifique sans analyser TOUTES les balises. Je veux dire que vous pourriez, mais il faudrait utiliser une combinaison de verbes comme (* SKIP) (* FAIL) mais toujours toutes les balises doivent être analysées.

La raison en est que la syntaxe des balises peut être cachée dans d'autres balises, etc.

Donc, pour analyser passivement toutes les balises, une expression régulière est nécessaire comme celle ci-dessous. Celui-ci correspond également au contenu invisible .

Au fur et à mesure que de nouveaux codes HTML ou xml ou tout autre développent de nouvelles constructions, ajoutez-les simplement comme l'une des alternances.


Remarque sur la page Web - Je n'ai jamais vu de page Web (ou xhtml / xml) avec laquelle cela
a rencontré des problèmes. Si vous en trouvez un, faites-le moi savoir.

Note sur les performances - C'est rapide. C'est l'analyseur de balises le plus rapide que j'ai vu
(il peut y en avoir un plus rapide, qui sait).
J'ai plusieurs versions spécifiques. Il est également excellent comme grattoir
(si vous êtes du type pratique).


Regex brut complet

<(?:(?:(?:(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]*?))))>

Aspect formaté

 <
 (?:
      (?:
           (?:
                # Invisible content; end tag req'd
                (                             # (1 start)
                     script
                  |  style
                  |  object
                  |  embed
                  |  applet
                  |  noframes
                  |  noscript
                  |  noembed 
                )                             # (1 end)
                (?:
                     \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]*? )
           )
      )
 )
 >

3

"Cela dépend" cependant. Il est vrai que les expressions rationnelles n'analysent pas et ne peuvent pas analyser le HTML avec une vraie précision, pour toutes les raisons données ici. Si, cependant, les conséquences d'une erreur (comme la non-gestion des balises imbriquées) sont mineures et si les expressions rationnelles sont très pratiques dans votre environnement (comme lorsque vous piratez Perl), allez-y.

Supposons que vous analysiez, peut-être, des pages Web qui renvoient vers votre site - peut-être que vous les avez trouvées avec une recherche de liens Google - et que vous vouliez un moyen rapide d'avoir une idée générale du contexte entourant votre lien. Vous essayez de générer un petit rapport qui pourrait vous alerter de lier le spam, quelque chose comme ça.

Dans ce cas, une mauvaise analyse de certains documents ne sera pas un gros problème. Personne d'autre que vous ne verra les erreurs, et si vous êtes très chanceux, il y en aura assez peu pour que vous puissiez faire un suivi individuel.

Je suppose que je dis que c'est un compromis. Parfois, la mise en œuvre ou l'utilisation d'un analyseur correct - aussi simple que cela puisse être - ne vaut pas la peine si la précision n'est pas critique.

Soyez prudent avec vos hypothèses. Je peux penser à quelques façons dont le raccourci d'expression rationnelle peut se retourner si vous essayez d'analyser quelque chose qui sera affiché en public, par exemple.


3

Il y a certainement des cas où l'utilisation d'une expression régulière pour analyser des informations HTML est la bonne façon de procéder - cela dépend beaucoup de la situation spécifique.

Le consensus ci-dessus est qu'en général c'est une mauvaise idée. Cependant, si la structure HTML est connue (et peu susceptible de changer), alors c'est toujours une approche valide.


3

Gardez à l'esprit que même si HTML lui-même n'est pas régulier, certaines parties d'une page que vous consultez peuvent être régulières.

Par exemple, c'est une erreur pour les <form>balises à imbriquer; si la page Web fonctionne correctement, l'utilisation d'une expression régulière pour saisir un <form>serait tout à fait raisonnable.

J'ai récemment fait du grattage Web en utilisant uniquement du sélénium et des expressions régulières. Je suis parti avec elle parce que les données que je voulais était placée dans une <form>, et mis dans un format de table simple (donc je ne pouvais même compter sur <table>, <tr>et <td>d'être non imbriquées - qui est en fait très rare). Dans une certaine mesure, les expressions régulières étaient même presque nécessaires, car une partie de la structure dont j'avais besoin était délimitée par des commentaires. (Beautiful Soup peut vous donner des commentaires, mais il aurait été difficile de saisir <!-- BEGIN -->et<!-- END --> bloquer en utilisant Beautiful Soup.)

Si je devais m'inquiéter des tables imbriquées, mon approche n'aurait tout simplement pas fonctionné! J'aurais dû me rabattre sur Beautiful Soup. Même dans ce cas, cependant, vous pouvez parfois utiliser une expression régulière pour saisir le morceau dont vous avez besoin, puis effectuer une analyse descendante à partir de là.


2

En fait, l'analyse HTML avec regex est parfaitement possible en PHP. Il vous suffit d'analyser la chaîne entière à l'envers en utilisant strrpospour trouver <et répéter l'expression régulière à partir de là en utilisant à chaque fois des spécificateurs non reconnus pour passer les balises imbriquées. Pas luxueux et terriblement lent sur de grandes choses, mais je l'ai utilisé pour mon propre éditeur de modèle personnel pour mon site Web. Je n'analysais pas réellement HTML, mais quelques balises personnalisées que j'ai faites pour interroger les entrées de la base de données pour afficher des tableaux de données (ma <#if()>balise pourrait mettre en évidence des entrées spéciales de cette façon). Je n'étais pas prêt à opter pour un analyseur XML sur seulement quelques balises auto-créées (avec des données très non XML à l'intérieur) ici et là.

Donc, même si cette question est considérablement morte, elle apparaît toujours dans une recherche Google. Je l'ai lu et j'ai pensé "défi accepté" et j'ai fini de corriger mon code simple sans avoir à tout remplacer. Décidé d'offrir une opinion différente à toute personne recherchant une raison similaire. De plus, la dernière réponse a été publiée il y a 4 heures, donc c'est toujours un sujet brûlant.


2
-1 pour avoir suggéré une TERRIBLE idée. Avez-vous envisagé un espace entre le tag et le support d'angle de fermeture? (Par exemple, <tag >) Avez-vous envisagé des balises de fermeture commentées? (Par exemple, <tag> <!-- </tag> -->) Avez-vous envisagé CDATA? Avez-vous envisagé des balises à cas incohérent? (Par exemple, <Tag> </tAG>) Avez-vous pensé à cela également?
rmunn

1
Dans le cas particulier de vos quelques balises personnalisées, oui, les expressions régulières fonctionnent bien. Ce n'est donc pas que votre utilisation en soit une erreur dans votre cas particulier . Ce n'est pas du HTML, cependant, et dire "L'analyse HTML avec regex est parfaitement possible en PHP" est tout simplement faux et c'est une TERRIBLE idée. Les incohérences du vrai HTML (et il y en a bien plus que les quelques que j'ai énumérées) sont la raison pour laquelle vous ne devriez jamais analyser le vrai HTML avec des expressions régulières. Voir, bien, toutes les autres réponses à cette question, ainsi que celle à laquelle j'ai lié dans mon autre commentaire ci-dessus.
rmunn

2
PHP est un langage complet, donc ce n'est pas du tout faux. Tout ce qui est possible sur le plan informatique est possible, y compris l'analyse HTML. Les espaces dans les balises n'étaient JAMAIS un problème et je l'ai depuis adapté pour répertorier les éléments de balises dans l'ordre. Mon utilisation a automatiquement corrigé les balises avec une casse incohérente, dépouillé les éléments commentés au tout premier stade et après quelques ajouts ultérieurs, toutes sortes de balises peuvent être facilement ajoutées (bien que cela soit sensible à la casse, par mon propre choix). Et je suis presque sûr que CDATA est en fait un élément XML, pas HTML.
Deji

2
Mon ancienne méthode (que j'ai décrite ici) était assez inefficace et j'ai récemment commencé une réécriture de nombreux éditeurs de contenu. Quand il s'agit de faire ces choses, la possibilité n'est pas le problème; la meilleure façon est toujours la principale préoccupation. La vraie réponse est "il n'y a pas de moyen FACILE de le faire en PHP". PERSONNE ne dit qu'il n'y a aucun moyen de le faire en PHP ou que c'est une idée terrible, mais que c'est impossible avec regex, que je n'ai honnêtement jamais essayé, mais le seul gros défaut de ma réponse est que je supposais que la question faisait référence à regex dans le cadre de PHP, ce qui n'est pas forcément le cas.
Deji

2

J'ai essayé ma main à un regex pour cela aussi. Il est principalement utile pour trouver des morceaux de contenu associés à la prochaine balise HTML, et il ne recherche pas les balises de fermeture correspondantes , mais il récupérera les balises de fermeture. Lancez une pile dans votre propre langue pour les vérifier.

À utiliser avec les options 'sx'. 'g' aussi si vous vous sentez chanceux:

(?P<content>.*?)                # Content up to next tag
(?P<markup>                     # Entire tag
  <!\[CDATA\[(?P<cdata>.+?)]]>| # <![CDATA[ ... ]]>
  <!--(?P<comment>.+?)-->|      # <!-- Comment -->
  </\s*(?P<close_tag>\w+)\s*>|  # </tag>
  <(?P<tag>\w+)                 # <tag ...
    (?P<attributes>
      (?P<attribute>\s+
# <snip>: Use this part to get the attributes out of 'attributes' group.
        (?P<attribute_name>\w+)
        (?:\s*=\s*
          (?P<attribute_value>
            [\w:/.\-]+|         # Unquoted
            (?=(?P<_v>          # Quoted
              (?P<_q>['\"]).*?(?<!\\)(?P=_q)))
            (?P=_v)
          ))?
# </snip>
      )*
    )\s*
  (?P<is_self_closing>/?)   # Self-closing indicator
  >)                        # End of tag

Celui-ci est conçu pour Python (il peut fonctionner pour d'autres langages, ne l'a pas essayé, il utilise des lookaheads positifs, des lookbehinds négatifs et des backreferences nommées). Les soutiens:

  • Tag ouvert - <div ...>
  • Fermer la balise - </div>
  • Commentaire - <!-- ... -->
  • CDATA - <![CDATA[ ... ]]>
  • Étiquette à fermeture automatique - <div .../>
  • Valeurs d'attribut facultatives - <input checked>
  • Valeurs d'attribut non cotées / cotées - <div style='...'>
  • Citations simples / doubles - <div style="...">
  • Citations échappées - <a title='John\'s Story'>
    (ce n'est pas vraiment du HTML valide, mais je suis un gars sympa)
  • Espaces autour de signes égaux - <a href = '...'>
  • Captures nommées pour les bits intéressants

Il est également très utile de ne pas déclencher sur des balises malformées, comme lorsque vous oubliez un <ou> .

Si votre saveur regex prend en charge les captures nommées répétées, vous êtes en or, mais pas Python re(je sais que regex le fait, mais j'ai besoin d'utiliser Python vanille). Voici ce que vous obtenez:

  • content- Tout le contenu jusqu'à la prochaine balise. Vous pourriez laisser ça de côté.
  • markup - La balise entière avec tout en elle.
  • comment - S'il s'agit d'un commentaire, le contenu du commentaire.
  • cdata- Si c'est le cas <![CDATA[...]]>, le contenu CDATA.
  • close_tag- S'il s'agit d'une balise close ( </div>), le nom de la balise.
  • tag- S'il s'agit d'une balise ouverte ( <div>), le nom de la balise.
  • attributes- Tous les attributs à l'intérieur de la balise. Utilisez-le pour obtenir tous les attributs si vous n'obtenez pas de groupes répétés.
  • attribute - Répété, chaque attribut.
  • attribute_name - Répété, chaque nom d'attribut.
  • attribute_value- Répété, chaque valeur d'attribut. Cela inclut les citations si elles ont été citées.
  • is_self_closing- C'est /si c'est une balise à fermeture automatique, sinon rien.
  • _qet _v- Ignorer ces derniers; ils sont utilisés en interne pour les références arrières.

Si votre moteur d'expression régulière ne prend pas en charge les captures nommées répétées, il existe une section appelée que vous pouvez utiliser pour obtenir chaque attribut. Il suffit de lancer que regex sur le attributesgroupe pour obtenir chacun attribute, attribute_nameet attribute_valuehors de celui - ci.

Démo ici: https://regex101.com/r/mH8jSu/11


1

Les expressions régulières ne sont pas assez puissantes pour un tel langage comme HTML. Bien sûr, il existe des exemples où vous pouvez utiliser des expressions régulières. Mais en général, il n'est pas approprié pour l'analyse.


0

Vous, savez ... il y a beaucoup de mentalité que vous NE POUVEZ PAS le faire et je pense que tout le monde des deux côtés de la clôture a raison et tort. Vous POUVEZ le faire, mais cela prend un peu plus de traitement que de simplement exécuter une expression rationnelle contre elle. Prenez ceci (j'ai écrit ceci en moins d'une heure) comme exemple. Il suppose que le code HTML est complètement valide, mais selon la langue que vous utilisez pour appliquer l'expression régulière susmentionnée, vous pouvez effectuer une correction du code HTML pour vous assurer qu'il réussira. Par exemple, supprimer des balises de fermeture qui ne sont pas censées être là: </img>par exemple. Ensuite, ajoutez la barre oblique HTML de fermeture unique aux éléments qui leur manquent, etc.

J'utiliserais cela dans le contexte de l'écriture d'une bibliothèque qui me permettrait d'effectuer une récupération d'élément HTML semblable à celle de JavaScript [x].getElementsByTagName(), par exemple. Je voudrais simplement épisser la fonctionnalité que j'ai écrite dans la section DEFINE de l'expression régulière et l'utiliser pour entrer à l'intérieur d'un arbre d'éléments, un à la fois.

Alors, sera-ce la réponse finale à 100% pour valider le HTML? Non. Mais c'est un début et avec un peu plus de travail, cela peut être fait. Cependant, essayer de le faire à l'intérieur d'une exécution d'expressions régulières n'est ni pratique ni efficace.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.