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?
regex
html-parsing
ntownsend
la source
la source
Réponses:
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.
la source
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?
la source
(Sur http://htmlparsing.com/regexes )
Supposons que vous ayez un fichier HTML dans lequel vous essayez d'extraire des URL à partir de balises <img>.
Vous écrivez donc une expression régulière comme celle-ci en Perl:
Dans ce cas,
$url
contiendra en effethttp://example.com/whatever.jpg
. Mais que se passe-t-il lorsque vous commencez à obtenir du HTML comme ceci:ou
ou
ou
ou vous commencez à obtenir des faux positifs
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.
la source
Deux raisons rapides:
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?
la source
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).
la source
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.
la source
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é.la source
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.
la source
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.
la source
Cette expression récupère les attributs des éléments HTML. Elle supporte:
(?:\<\!\-\-(?:(?!\-\-\>)\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.
la source
<script>
balise.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
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.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é
la source
"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.
la source
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.
la source
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à.
la source
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
strrpos
pour 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.
la source
<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?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:
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:
<div ...>
</div>
<!-- ... -->
<![CDATA[ ... ]]>
<div .../>
<input checked>
<div style='...'>
<div style="...">
<a title='John\'s Story'>
(ce n'est pas vraiment du HTML valide, mais je suis un gars sympa)
<a href = '...'>
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._q
et_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
attributes
groupe pour obtenir chacunattribute
,attribute_name
etattribute_value
hors de celui - ci.Démo ici: https://regex101.com/r/mH8jSu/11
la source
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.
la source
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.
la source