RegEx correspond aux balises ouvertes à l'exception des balises autonomes XHTML
Je dois faire correspondre toutes ces balises d'ouverture:
<p>
<a href="foo">
Mais pas ceux-ci:
<br />
<hr class="foo" />
Je suis venu avec ceci et je voulais m'assurer de bien faire les choses. Je ne capture que le a-z
.
<([a-z]+) *[^/]*?>
Je crois qu'il dit:
- Trouvez un moins que, alors
- Trouvez (et capturez) az une ou plusieurs fois, puis
- Trouvez zéro ou plusieurs espaces, puis
- Trouver n'importe quel caractère zéro fois ou plus, gourmand, sauf
/
, alors - Trouver un plus grand que
Ai-je ce droit? Et surtout, qu'en pensez-vous?
Réponses
Vous ne pouvez pas analyser [X] HTML avec regex. Parce que HTML ne peut pas être analysé par regex. Regex n'est pas un outil qui peut être utilisé pour analyser correctement le HTML. Comme j'ai répondu à des questions sur HTML et regex ici tant de fois auparavant, l'utilisation de regex ne vous permettra pas de consommer du HTML. Les expressions régulières sont un outil qui n'est pas suffisamment sophistiqué pour comprendre les constructions employées par HTML. Le HTML n'est pas un langage régulier et ne peut donc pas être analysé par des expressions régulières. Les requêtes Regex ne sont pas équipées pour décomposer le HTML en ses parties significatives. tant de fois mais cela ne m'arrive pas. Même les expressions régulières irrégulières améliorées utilisées par Perl ne sont pas à la hauteur de l'analyse du HTML. Tu ne me feras jamais craquer. Le HTML est un langage d'une complexité suffisante pour qu'il ne puisse pas être analysé par des expressions régulières. Même Jon Skeet ne peut pas analyser le HTML à l'aide d'expressions régulières. Chaque fois que vous essayez d'analyser le HTML avec des expressions régulières, l'enfant impie pleure le sang des vierges et les pirates russes installent votre application Web. L'analyse HTML avec regex invoque les âmes souillées dans le royaume des vivants. HTML et regex vont de pair comme l'amour, le mariage et l'infanticide rituel. Le <center> ne peut pas tenir, il est trop tard. La force de l'expression régulière et du HTML réunis dans le même espace conceptuel détruira votre esprit comme autant de mastic aqueux. Si vous analysez HTML avec regex, vous cédez à Eux et à leurs manières blasphématoires qui nous condamnent tous à un travail inhumain pour Celui dont le nom ne peut pas être exprimé dans le plan multilingue de base, il vient. HTML-plus-regexp liquéfiera les nerfs du sensible pendant que vous observez, votre psyché dépérissant sous l'assaut de l'horreur. Les analyseurs HTML basés sur Rege̿̔̉x sont le cancer qui tue StackOverflowil est trop tard il est trop tard nous ne pouvons pas être sauvés la transgression d'un enfant garantit que l'expression régulière consommera tous les tissus vivants (à l'exception du HTML qu'elle ne peut pas, comme cela a été prophétisé précédemment) cher seigneur, aidez-nous comment quelqu'un peut-il survivre à ce fléau en utilisant une expression régulière pour analyser HTML a condamné l'humanité à une éternité de torture effrayante et de failles de sécurité en utilisant rege x comme outil de traitement du HTML établit une rupture entre ce monde et le royaume effrayant des entités en ruine (comme les entités SGML, mais plus corrompues) un simple aperçu de le monde de reg ex parseurs pour HTML ins tantly transports ap conscience de rogrammer i nto aw orl d de crier sans arrêt, il vient
, le pestilentielle sl
ITHY regex-infection wil l dévorent votre HT analyseur ML, l' application et l' existence de tous les temps comme Visual Basic ne fait qu'empirer il vient , il com es ne pas fi ght h e vIENT, salut s Unholy Radiance de stro҉ying toute lumière, les balises HTML qui se échappent areil Liq EYES comme uid p ain, la chanson de RÉGULIER exp re
ssion analyse syntaxique
s'ExtJ nguish les voix de mor homme tal de la sp ici , je peux le voir peut vous le voyez , il est beau t - il f inal snuf
Fing o f le mensonge de l' homme ALL IS A PERDU LL I SL OST e e PONY il venu s il cơm
es il co
me
st qu'il ICH ou Permeat es al l MON FAC e MON VISAGE dieu n o NO Noo O ON Θ arrêt t - il un * ̶͑̾̾ Gl ÉS n ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ
͎a̧͈͖r̽̾̈́͒͑e
Avez-vous essayé d'utiliser un analyseur XML à la place?
Note du modérateur
Ce message est verrouillé pour empêcher toute modification inappropriée de son contenu. Le message ressemble exactement à ce qu'il est censé ressembler - il n'y a aucun problème avec son contenu. Veuillez ne pas le signaler à notre attention.
Bien que le HTML arbitraire avec seulement une expression régulière soit impossible, il est parfois approprié de les utiliser pour analyser un ensemble limité et connu de HTML.
Si vous avez un petit ensemble de pages HTML dont vous souhaitez extraire des données, puis les insérer dans une base de données, les expressions régulières peuvent fonctionner correctement. Par exemple, j'ai récemment voulu obtenir les noms, les partis et les districts des représentants fédéraux australiens, ce que j'ai obtenu du site Web du Parlement. C'était un travail limité et ponctuel.
Les expressions régulières ont très bien fonctionné pour moi et ont été très rapides à mettre en place.
Je pense que le défaut ici est que HTML est une grammaire Chomsky Type 2 (grammaire sans contexte) et une expression régulière est une grammaire Chomsky Type 3 (grammaire régulière) . Puisqu'une grammaire de type 2 est fondamentalement plus complexe qu'une grammaire de type 3 (voir la hiérarchie de Chomsky ), il est mathématiquement impossible d'analyser du XML avec une expression régulière.
Mais beaucoup essaieront, et certains revendiqueront même le succès - mais jusqu'à ce que d'autres trouvent la faute et vous gâchent totalement.
N'écoutez pas ces gars-là. Vous pouvez totalement analyser des grammaires sans contexte avec regex si vous divisez la tâche en plus petits morceaux. Vous pouvez générer le modèle correct avec un script qui effectue chacune de ces opérations dans l'ordre:
- Résolvez le problème de l'arrêt.
- Carrez un cercle.
- Résolvez le problème du voyageur de commerce en O (log n) ou moins. Si c'est plus que cela, vous manquerez de RAM et le moteur se bloquera.
- Le modèle sera assez gros, alors assurez-vous d'avoir un algorithme qui compresse sans perte les données aléatoires.
- Presque là - il suffit de diviser le tout par zéro. Peasy facile.
Je n'ai pas tout à fait fini la dernière partie moi-même, mais je sais que je m'en rapproche. Il continue de lancer des CthulhuRlyehWgahnaglFhtagnException
s pour une raison quelconque, donc je vais le porter sur VB 6 et l'utiliser On Error Resume Next
. Je mettrai à jour le code une fois que j'enquêterai sur cette étrange porte qui vient de s'ouvrir dans le mur. Hmm.
PS Pierre de Fermat a également compris comment le faire, mais la marge dans laquelle il écrivait n'était pas assez grande pour le code.
Clause de non - responsabilité : utilisez un analyseur si vous en avez l'option. Cela dit...
Voici l'expression régulière que j'utilise (!) Pour faire correspondre les balises HTML:
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>
Ce n'est peut-être pas parfait, mais j'ai exécuté ce code à travers beaucoup de HTML. Notez qu'il attrape même des choses étranges comme <a name="badgenerator"">
, qui apparaissent sur le Web.
Je suppose que pour que cela ne corresponde pas aux balises autonomes, vous voudrez soit utiliser le look-behind négatif de Kobi :
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>
ou tout simplement combiner si et si non.
Aux votants négatifs: il s'agit d'un code de travail à partir d'un produit réel. Je doute que quiconque lisant cette page ait l'impression qu'il est socialement acceptable d'utiliser des expressions régulières sur HTML.
Mise en garde : je dois noter que cette expression régulière tombe toujours en panne en présence de blocs CDATA, de commentaires et d'éléments de script et de style. La bonne nouvelle est que vous pouvez vous débarrasser de ceux qui utilisent une expression régulière ...
Il y a des gens qui vous diront que la Terre est ronde (ou peut-être que la Terre est un sphéroïde aplati s'ils veulent utiliser des mots étranges). Ils mentent.
Il y a des gens qui vous diront que les expressions régulières ne devraient pas être récursives. Ils vous limitent. Ils ont besoin de vous subjuguer, et ils le font en vous gardant dans l'ignorance.
Vous pouvez vivre dans leur réalité ou prendre la pilule rouge.
Comme Lord Marshal (est-il un parent de la classe Marshal .NET?), J'ai vu
Underverse
Stack Based Regex-Verse et je suis
revenu avec des
pouvoirs que
vous ne pouvez pas imaginer. Oui, je pense qu'il y avait un Old One ou deux qui les protégeaient, mais ils regardaient le football à la télé, donc ce n'était pas difficile.
Je pense que le cas XML est assez simple. Le RegEx (dans la syntaxe .NET), dégonflé et codé en base64 pour le rendre plus facile à comprendre par votre faible esprit, devrait être quelque chose comme ceci:
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
+r/vD34mUADO1P4/AQAA//8=
Les options à définir sont RegexOptions.ExplicitCapture
. Le groupe de capture que vous recherchez est ELEMENTNAME
. Si le groupe de capture ERROR
n'est pas vide, une erreur d'analyse s'est produite et le Regex s'est arrêté.
Si vous rencontrez des problèmes pour le reconvertir en une expression régulière lisible par l'homme, cela devrait vous aider:
static string FromBase64(string str)
{
byte[] byteArray = Convert.FromBase64String(str);
using (var msIn = new MemoryStream(byteArray))
using (var msOut = new MemoryStream()) {
using (var ds = new DeflateStream(msIn, CompressionMode.Decompress)) {
ds.CopyTo(msOut);
}
return Encoding.UTF8.GetString(msOut.ToArray());
}
}
Si vous n'êtes pas sûr, non, je ne plaisante PAS (mais peut-être que je mens). Ça va marcher. J'ai construit des tonnes de tests unitaires pour le tester, et j'ai même utilisé (une partie) des tests de conformité . C'est un tokenizer, pas un analyseur à part entière, il ne divisera donc le XML qu'en ses jetons de composant. Il n'analysera / n'intégrera pas les DTD.
Oh ... si vous voulez le code source de l'expression régulière, avec quelques méthodes auxiliaires:
regex pour tokeniser un xml ou le regex complet
Dans le shell, vous pouvez analyser le HTML en utilisant sed :
- Turing.sed
- Écrire un analyseur HTML (devoirs)
- ???
- Profit!
Connexes (pourquoi vous ne devriez pas utiliser la correspondance regex):
- Si vous aimez tellement les expressions régulières, pourquoi ne les épousez-vous pas?
- Expressions régulières: vous avez maintenant deux problèmes
- Piratage du désinfectant HTML de stackoverflow.com
Je suis d'accord que le bon outil pour analyser XML et surtout HTML est un analyseur et non un moteur d'expression régulière. Cependant, comme d'autres l'ont souligné, l'utilisation d'une expression régulière est parfois plus rapide, plus facile et fait le travail si vous connaissez le format des données.
Microsoft a en fait une section des meilleures pratiques pour les expressions régulières dans le .NET Framework et parle spécifiquement de la prise en compte de la source d'entrée .
Les expressions régulières ont des limites, mais avez-vous envisagé les éléments suivants?
Le framework .NET est unique en ce qui concerne les expressions régulières en ce sens qu'il prend en charge les définitions de groupe d'équilibrage .
- Voir Mise en correspondance de constructions équilibrées avec des expressions régulières .NET
- Voir Expressions régulières .NET: Regex et correspondance équilibrée
- Voir la documentation de Microsoft sur les définitions de groupe d'équilibrage
Pour cette raison, je crois que vous POUVEZ analyser XML à l'aide d'expressions régulières. Notez cependant qu'il doit être du XML valide (les navigateurs sont très indulgents avec le HTML et autorisent une mauvaise syntaxe XML à l'intérieur du HTML ). Ceci est possible car la "Définition du groupe d'équilibrage" permettra au moteur d'expressions régulières d'agir comme un PDA.
Citation de l'article 1 cité ci-dessus:
Moteur d'expressions régulières .NET
Comme décrit ci-dessus, les constructions correctement équilibrées ne peuvent pas être décrites par une expression régulière. Cependant, le moteur d'expressions régulières .NET fournit quelques constructions qui permettent de reconnaître les constructions équilibrées.
(?<group>)
- pousse le résultat capturé sur la pile de capture avec le groupe de noms.(?<-group>)
- fait apparaître la capture la plus élevée avec le groupe de noms hors de la pile de capture.(?(group)yes|no)
- correspond à la partie oui s'il existe un groupe avec le nom de groupe sinon ne correspond à aucune partie.Ces constructions permettent à une expression régulière .NET d'émuler un PDA restreint en autorisant essentiellement des versions simples des opérations de pile: push, pop et empty. Les opérations simples sont à peu près équivalentes à incrémenter, décrémenter et comparer à zéro respectivement. Cela permet au moteur d'expressions régulières .NET de reconnaître un sous-ensemble des langages sans contexte, en particulier ceux qui ne nécessitent qu'un simple compteur. Cela permet à son tour aux expressions régulières .NET non traditionnelles de reconnaître des constructions individuelles correctement équilibrées.
Considérez l'expression régulière suivante:
(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
<!-- .*? --> |
<[^>]*/> |
(?<opentag><(?!/)[^>]*[^/]>) |
(?<-opentag></[^>]*[^/]>) |
[^<>]*
)*
(?(opentag)(?!))
Utilisez les drapeaux:
- Une seule ligne
- IgnorePatternWhitespace (pas nécessaire si vous réduisez l'expression régulière et supprimez tous les espaces)
- IgnoreCase (pas nécessaire)
Expression régulière expliquée (en ligne)
(?=<ul\s+id="matchMe"\s+type="square"\s*>) # match start with <ul id="matchMe"...
(?> # atomic group / don't backtrack (faster)
<!-- .*? --> | # match xml / html comment
<[^>]*/> | # self closing tag
(?<opentag><(?!/)[^>]*[^/]>) | # push opening xml tag
(?<-opentag></[^>]*[^/]>) | # pop closing xml tag
[^<>]* # something between tags
)* # match as many xml tags as possible
(?(opentag)(?!)) # ensure no 'opentag' groups are on stack
Vous pouvez essayer ceci à A Better .NET Regular Expression Tester .
J'ai utilisé la source d'échantillon de:
<html>
<body>
<div>
<br />
<ul id="matchMe" type="square">
<li>stuff...</li>
<li>more stuff</li>
<li>
<div>
<span>still more</span>
<ul>
<li>Another >ul<, oh my!</li>
<li>...</li>
</ul>
</div>
</li>
</ul>
</div>
</body>
</html>
Cela a trouvé le match:
<ul id="matchMe" type="square">
<li>stuff...</li>
<li>more stuff</li>
<li>
<div>
<span>still more</span>
<ul>
<li>Another >ul<, oh my!</li>
<li>...</li>
</ul>
</div>
</li>
</ul>
bien qu'il soit en fait sorti comme ceci:
<ul id="matchMe" type="square"> <li>stuff...</li> <li>more stuff</li> <li> <div> <span>still more</span> <ul> <li>Another >ul<, oh my!</li> <li>...</li> </ul> </div> </li> </ul>
Enfin, j'ai vraiment apprécié l'article de Jeff Atwood: Parsing Html The Cthulhu Way . Assez drôle, il cite la réponse à cette question qui compte actuellement plus de 4k votes.
Je suggère d'utiliser QueryPath pour analyser XML et HTML en PHP. C'est essentiellement la même syntaxe que jQuery, mais c'est du côté serveur.
Bien que les réponses selon lesquelles vous ne pouvez pas analyser le HTML avec des expressions régulières sont correctes, elles ne s'appliquent pas ici. L'OP veut juste analyser une balise HTML avec des expressions régulières, et c'est quelque chose qui peut être fait avec une expression régulière.
L'expression régulière suggérée est erronée, cependant:
<([a-z]+) *[^/]*?>
Si vous ajoutez quelque chose à l'expression régulière, en faisant marche arrière, il peut être forcé de faire correspondre des choses idiotes comme <a >>
, [^/]
c'est trop permissif. Notez également que cela <space>*[^/]*
est redondant, car le [^/]*
peut également correspondre à des espaces.
Ma suggestion serait
<([a-z]+)[^>]*(?<!/)>
Où (?<! ... )
est (dans les expressions rationnelles Perl) le look-behind négatif. Il lit "un <, puis un mot, puis tout ce qui n'est pas un>, dont le dernier peut ne pas être un /, suivi de>".
Notez que cela permet des choses comme <a/ >
(tout comme l'expression régulière d'origine), donc si vous voulez quelque chose de plus restrictif, vous devez créer une expression régulière pour faire correspondre les paires d'attributs séparées par des espaces.
Essayer:
<([^\s]+)(\s[^>]*?)?(?<!/)>
Il est similaire au vôtre, mais le dernier >
ne doit pas être après une barre oblique, et accepte également h1
.
Sun Tzu, un ancien stratège, général et philosophe chinois, a déclaré:
On dit que si vous connaissez vos ennemis et que vous vous connaissez vous-même, vous pouvez gagner cent batailles sans une seule perte. Si vous ne vous connaissez que vous-même, mais pas votre adversaire, vous pouvez gagner ou perdre. Si vous ne connaissez ni vous-même ni votre ennemi, vous vous mettrez toujours en danger.
Dans ce cas, votre ennemi est HTML et vous êtes vous-même ou regex. Vous pourriez même être Perl avec une regex irrégulière. Connaissez le HTML. Se connaitre.
J'ai composé un haïku décrivant la nature du HTML.
HTML has
complexity exceeding
regular language.
J'ai également composé un haïku décrivant la nature des regex en Perl.
The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
<?php
$selfClosing = explode(',', 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed'); $html = '
<p><a href="#">foo</a></p>
<hr/>
<br/>
<div>name</div>';
$dom = new DOMDocument(); $dom->loadHTML($html); $els = $dom->getElementsByTagName('*'); foreach ( $els as $el ) { $nodeName = strtolower($el->nodeName); if ( !in_array( $nodeName, $selfClosing ) ) { var_dump( $nodeName );
}
}
Production:
string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"
En gros, définissez simplement les noms de nœuds d'élément qui se ferment automatiquement, chargez toute la chaîne html dans une bibliothèque DOM, récupérez tous les éléments, bouclez et filtrez ceux qui ne se ferment pas automatiquement et opérez sur eux.
Je suis sûr que vous savez déjà que vous ne devriez pas utiliser regex à cette fin.
Je ne connais pas votre besoin exact, mais si vous utilisez également .NET, ne pourriez-vous pas utiliser Html Agility Pack ?
Extrait:
Il s'agit d'une bibliothèque de code .NET qui vous permet d'analyser les fichiers HTML «hors du Web». L'analyseur est très tolérant avec du HTML malformé "dans le monde réel".
Vous voulez que le premier >
ne soit pas précédé d'un /
. Regardez ici pour plus de détails sur la façon de procéder. C'est ce qu'on appelle un regard négatif en arrière.
Cependant, une implémentation naïve de cela finira par correspondre <bar/></foo>
dans cet exemple de document
<foo><bar/></foo>
Pouvez-vous fournir un peu plus d'informations sur le problème que vous essayez de résoudre? Êtes-vous en train de parcourir les balises de manière programmée?
Le W3C explique l'analyse sous forme de pseudo regexp:
Lien W3C
Suivez les liens var pour QName
, S
et Attribute
pour obtenir une image plus claire.
Sur cette base, vous pouvez créer une assez bonne expression rationnelle pour gérer des choses comme le décapage des balises.
Si vous en avez besoin pour PHP:
Les fonctions PHP DOM ne fonctionneront correctement que si elles sont correctement formatées en XML. Peu importe à quel point leur utilisation est meilleure pour le reste de l'humanité.
simplehtmldom est bon, mais je l'ai trouvé un peu bogué, et il est assez lourd en mémoire [plantera sur de grandes pages.]
Je n'ai jamais utilisé querypath , je ne peux donc pas commenter son utilité.
Un autre à essayer est mon DOMParser qui est très léger en ressources et que j'utilise avec bonheur depuis un moment. Simple à apprendre et puissant.
Pour Python et Java, des liens similaires ont été publiés.
Pour les downvoters - je n'ai écrit ma classe que lorsque les analyseurs XML se sont avérés incapables de résister à une utilisation réelle. Le vote négatif empêche simplement l'affichage de réponses utiles - gardez les choses en perspective par rapport à la question, s'il vous plaît.
Voici la solution:
<?php
// here's the pattern:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*(\/>|>)/'; // a string to parse: $string = 'Hello, try clicking <a href="#paragraph">here</a>
<br/>and check out.<hr />
<h2>title</h2>
<a name ="paragraph" rel= "I\'m an anchor"></a>
Fine, <span title=\'highlight the "punch"\'>thanks<span>.
<div class = "clear"></div>
<br>';
// let's get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER); // print the result: print_r($matches[0]);
?>
Pour le tester en profondeur, j'ai entré dans la chaîne des balises à fermeture automatique comme:
- <h />
- <br/>
- <br>
J'ai également entré des balises avec:
- un attribut
- plus d'un attribut
- attributs dont la valeur est liée soit à des guillemets simples, soit à des guillemets doubles
- attributs contenant des guillemets simples lorsque le délimiteur est un guillemet double et vice versa
- Attributs "unsetty" avec un espace avant le symbole "=", après lui et à la fois avant et après lui.
Si vous trouvez quelque chose qui ne fonctionne pas dans la preuve de concept ci-dessus, je suis disponible dans l'analyse du code pour améliorer mes compétences.
<EDIT> J'ai oublié que la question de l'utilisateur était d'éviter l'analyse des balises à fermeture automatique. Dans ce cas, le modèle est plus simple, se transformant en ceci:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';
L'utilisateur @ridgerunner a remarqué que le modèle n'autorise pas les attributs sans guillemets ni les attributs sans valeur . Dans ce cas, un réglage fin nous apporte le modèle suivant:
$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';
</EDIT>
Comprendre le modèle
Si quelqu'un souhaite en savoir plus sur le modèle, je donne quelques lignes:
- la première sous-expression (\ w +) correspond au nom de la balise
- la deuxième sous-expression contient le modèle d'un attribut. Il est composé de:
- un ou plusieurs espaces \ s +
- le nom de l'attribut (\ w +)
- zéro ou plusieurs espaces \ s * (c'est possible ou non, en laissant des espaces ici)
- le symbole "="
- encore une fois, zéro ou plus d'espaces blancs
- le délimiteur de la valeur d'attribut, un guillemet simple ou double ('| "). Dans le modèle, le guillemet simple est échappé car il coïncide avec le délimiteur de chaîne PHP. Cette sous-expression est capturée avec les parenthèses afin de pouvoir être référencée encore une fois pour analyser la fermeture de l'attribut, c'est pourquoi c'est très important.
- la valeur de l'attribut, correspondant à presque tout: (. *?); dans cette syntaxe spécifique, en utilisant la correspondance gourmande (le point d'interrogation après l'astérisque), le moteur RegExp active un opérateur de type "look-ahead", qui correspond à tout sauf à ce qui suit cette sous-expression
- voici le plaisir: la partie \ 4 est un opérateur de référence arrière , qui fait référence à une sous-expression définie auparavant dans le modèle, dans ce cas, je fais référence à la quatrième sous-expression, qui est le premier délimiteur d'attribut trouvé
- zéro ou plusieurs espaces \ s *
- la sous-expression d'attribut se termine ici, avec la spécification de zéro ou plusieurs occurrences possibles, données par l'astérisque.
- Puis, comme une balise peut se terminer par un espace avant le symbole ">", zéro ou plusieurs espaces sont mis en correspondance avec le sous-modèle \ s *.
- La balise à mettre en correspondance peut se terminer par un simple symbole ">", ou une éventuelle fermeture XHTML, qui utilise la barre oblique avant: (/> |>). La barre oblique est bien sûr échappée car elle coïncide avec le délimiteur d'expression régulière.
Petite astuce: pour mieux analyser ce code il faut regarder le code source généré puisque je n'ai pas fourni de caractères spéciaux HTML pour échapper.
Chaque fois que j'ai besoin d'extraire rapidement quelque chose d'un document HTML, j'utilise Tidy pour le convertir en XML, puis j'utilise XPath ou XSLT pour obtenir ce dont j'ai besoin. Dans votre cas, quelque chose comme ceci:
//p/a[@href='foo']
J'ai déjà utilisé un outil open source appelé HTMLParser . Il est conçu pour analyser le HTML de différentes manières et sert très bien cet objectif. Il peut analyser le HTML en tant que treenode différent et vous pouvez facilement utiliser son API pour extraire des attributs du nœud. Vérifiez-le et voyez si cela peut vous aider.
J'aime analyser le HTML avec des expressions régulières. Je n'essaye pas d'analyser le HTML idiot qui est délibérément cassé. Ce code est mon analyseur principal (édition Perl):
$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g; s/^ ?\n//s; s/ $//s; print
Cela s'appelle htmlsplit , divise le HTML en lignes, avec une balise ou un morceau de texte sur chaque ligne. Les lignes peuvent ensuite être traitées avec d'autres outils de texte et scripts, tels que grep , sed , Perl, etc. Je ne plaisante même pas :) Profitez-en.
Il est assez simple de réorganiser mon script Perl slurp-tout-en-premier en une belle chose en streaming, si vous souhaitez traiter d'énormes pages Web. Mais ce n'est pas vraiment nécessaire.
Division HTML
Quelques meilleures expressions régulières:
/(<.*?>|[^<]+)\s*/g # Get tags and text
/(\w+)="(.*?)"/g # Get attibutes
Ils sont bons pour XML / XHTML.
Avec des variations mineures, il peut faire face à du HTML désordonné ... ou convertir le HTML -> XHTML en premier.
La meilleure façon d'écrire des expressions régulières est dans le style Lex / Yacc , pas comme des monstruosités opaques ou des monstruosités multilignes commentées. Je n'ai pas encore fait ça ici; ceux-ci en ont à peine besoin.
Voici un analyseur basé sur PHP qui analyse le HTML en utilisant une expression régulière impie. En tant qu'auteur de ce projet, je peux vous dire qu'il est possible d'analyser le HTML avec regex, mais pas efficace. Si vous avez besoin d'une solution côté serveur (comme je l'ai fait pour mon plugin WordPress wp-Typography ), cela fonctionne.
Il y a quelques bonnes expressions régulières pour remplacer HTML par BBCode ici . Pour tous les opposants, notez qu'il n'essaye pas d'analyser complètement le HTML, juste pour le nettoyer. Il peut probablement se permettre de supprimer les balises que son simple "analyseur" ne peut pas comprendre.
Par exemple:
$store =~ s/http:/http:\/\//gi; $store =~ s/https:/https:\/\//gi;
$baseurl = $store;
if (!$query->param("ascii")) { $html =~ s/\s\s+/\n/gi;
$html =~ s/<pre(.*?)>(.*?)<\/pre>/\[code]$2\[\/code]/sgmi;
}
$html =~ s/\n//gi; $html =~ s/\r\r//gi;
$html =~ s/$baseurl//gi;
$html =~ s/<h[1-7](.*?)>(.*?)<\/h[1-7]>/\n\[b]$2\[\/b]\n/sgmi;
$html =~ s/<p>/\n\n/gi; $html =~ s/<br(.*?)>/\n/gi;
$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]$2\[\/code]/sgmi;
$html =~ s/<b>(.*?)<\/b>/\[b]$1\[\/b]/gi;
$html =~ s/<i>(.*?)<\/i>/\[i]$1\[\/i]/gi;
$html =~ s/<u>(.*?)<\/u>/\[u]$1\[\/u]/gi;
$html =~ s/<em>(.*?)<\/em>/\[i]$1\[\/i]/gi;
$html =~ s/<strong>(.*?)<\/strong>/\[b]$1\[\/b]/gi;
$html =~ s/<cite>(.*?)<\/cite>/\[i]$1\[\/i]/gi;
$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi; $html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<link(.*?)>//gi; $html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]$2/gi; $html =~ s/<ul(.*?)>/\[list]/gi;
$html =~ s/<\/ul>/\[\/list]/gi; $html =~ s/<div>/\n/gi;
$html =~ s/<\/div>/\n/gi; $html =~ s/<td(.*?)>/ /gi;
$html =~ s/<tr(.*?)>/\n/gi; $html =~ s/<img(.*?)src="(.*?)"(.*?)>/\[img]$baseurl\/$2\[\/img]/gi;
$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/$2]$4\[\/url]/gi;
$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/$1]$2\[\/url]/gi;
$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/$1\[\/img]/gi; $html =~ s/<head>(.*?)<\/head>//sgmi;
$html =~ s/<object>(.*?)<\/object>//sgmi; $html =~ s/<script(.*?)>(.*?)<\/script>//sgmi;
$html =~ s/<style(.*?)>(.*?)<\/style>//sgmi; $html =~ s/<title>(.*?)<\/title>//sgmi;
$html =~ s/<!--(.*?)-->/\n/sgmi; $html =~ s/\/\//\//gi;
$html =~ s/http:\//http:\/\//gi; $html =~ s/https:\//https:\/\//gi;
$html =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gsi; $html =~ s/\r\r//gi;
$html =~ s/\[img]\//\[img]/gi; $html =~ s/\[url=\//\[url=/gi;
A propos de la question des méthodes d'expressions régulières pour analyser (x) HTML, la réponse à tous ceux qui ont parlé de certaines limites est: vous n'avez pas été suffisamment entraîné pour gouverner la force de cette arme puissante, puisque personne ici n'a parlé de récursivité .
Un collègue indépendant des expressions régulières m'a informé de cette discussion, qui n'est certainement pas la première sur le Web sur ce sujet ancien et brûlant.
Après avoir lu certains articles, la première chose que j'ai faite a été de rechercher la chaîne "? R" dans ce fil. La seconde était de rechercher la "récursivité".
Non, sainte vache, aucune correspondance trouvée. Puisque personne n'a mentionné le mécanisme principal sur lequel un analyseur est construit, je me suis vite rendu compte que personne n'avait compris.
Si un analyseur HTML (x) a besoin d'une récursivité, un analyseur d'expressions régulières sans récursivité n'est pas suffisant à cet effet. C'est une construction simple.
L' art noir des expressions régulières est difficile à maîtriser , alors peut-être qu'il y a d'autres possibilités que nous avons laissées de côté en essayant et en testant notre solution personnelle pour capturer l'ensemble du Web d'une seule main ... Eh bien, j'en suis sûr :)
Voici le modèle magique:
$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";
Essayez-le. Il est écrit sous forme de chaîne PHP, donc le modificateur "s" permet aux classes d'inclure des nouvelles lignes.
Voici un exemple de note sur le manuel PHP que j'ai écrit en janvier: Référence
(Attention. Dans cette note, j'ai mal utilisé le modificateur "m"; il devrait être effacé, même s'il est rejeté par le moteur d'expressions régulières, car aucun ^
ou l' $
ancrage n'a été utilisé).
Maintenant, nous pourrions parler des limites de cette méthode d'un point de vue plus éclairé:
- selon l'implémentation spécifique du moteur d'expressions régulières, la récursivité peut avoir une limite dans le nombre de motifs imbriqués analysés , mais cela dépend du langage utilisé
- bien que corrompu, (x) HTML n'entraîne pas d'erreurs graves. Il n'est pas désinfecté .
Quoi qu'il en soit, ce n'est qu'un modèle d'expression régulière, mais il révèle la possibilité de développer de nombreuses implémentations puissantes.
J'ai écrit ce modèle pour alimenter l' analyseur de descente récursive d'un moteur de modèle que j'ai construit dans mon framework, et les performances sont vraiment excellentes, à la fois en temps d'exécution ou en utilisation de la mémoire (rien à voir avec d'autres moteurs de modèle qui utilisent la même syntaxe).
Comme beaucoup de gens l'ont déjà souligné, le HTML n'est pas un langage ordinaire, ce qui peut rendre son analyse très difficile. Ma solution à cela est de le transformer en un langage régulier à l'aide d'un programme bien rangé, puis d'utiliser un analyseur XML pour consommer les résultats. Il y a beaucoup de bonnes options pour cela. Mon programme est écrit en utilisant Java avec la bibliothèque jtidy pour transformer le HTML en XML, puis Jaxen en xpath dans le résultat.
<\s*(\w+)[^/>]*>
Les parties expliquées:
<
: Caractère de départ
\s*
: Il peut y avoir des espaces avant le nom de la balise (moche, mais possible).
(\w+)
: les balises peuvent contenir des lettres et des chiffres (h1). Eh bien, \w
correspond également à «_», mais cela ne fait pas de mal, je suppose. Si vous êtes curieux, utilisez plutôt ([a-zA-Z0-9] +).
[^/>]*
: Tout sauf >
et /
jusqu'à la fermeture>
>
: Fermeture >
SANS RELATION
Et aux boursiers, qui sous-estiment les expressions régulières, affirmant qu'elles ne sont aussi puissantes que les langages réguliers:
un n ba n ba n qui n'est ni régulier ni même sans contexte peut être associé à^(a+)b\1b\1$
Backreferencing FTW !
Si vous essayez simplement de trouver ces balises (sans ambitions d'analyse), essayez cette expression régulière:
/<[^/]*?>/g
Je l'ai écrit en 30 secondes, et testé ici: http://gskinner.com/RegExr/
Il correspond aux types de balises que vous avez mentionnés, tout en ignorant les types que vous avez dit que vous vouliez ignorer.
Il me semble que vous essayez de faire correspondre les balises sans "/" à la fin. Essaye ça:
<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
Il est vrai que lors de la programmation, il est généralement préférable d'utiliser des analyseurs et des API dédiés au lieu d'expressions régulières lorsque vous traitez avec HTML, surtout si la précision est primordiale (par exemple, si votre traitement peut avoir des implications sur la sécurité). Cependant, je ne souscris pas à une vue dogmatique selon laquelle le balisage de style XML ne devrait jamais être traité avec des expressions régulières. Il y a des cas où les expressions régulières sont un excellent outil pour le travail, comme lors de modifications ponctuelles dans un éditeur de texte, de correction de fichiers XML cassés ou de gestion de formats de fichiers qui ressemblent à du XML mais ne sont pas tout à fait XML. Il y a certains problèmes à prendre en compte, mais ils ne sont pas insurmontables ni même nécessairement pertinents.
Une simple expression régulière comme <([^>"']|"[^"]*"|'[^']*')*>
est généralement assez bonne, dans des cas tels que ceux que je viens de mentionner. C'est une solution naïve, tout bien considéré, mais elle autorise correctement les >
symboles non codés dans les valeurs d'attribut. Si vous recherchez, par exemple, une table
balise, vous pouvez l'adapter en tant que </?table\b([^>"']|"[^"]*"|'[^']*')*>
.
Juste pour donner une idée de ce à quoi ressemblerait une expression régulière HTML plus "avancée", ce qui suit fait un travail assez respectable d'émulation du comportement réel du navigateur et de l'algorithme d'analyse HTML5:
</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)
Ce qui suit correspond à une définition assez stricte des balises XML (même si elle ne tient pas compte de l'ensemble complet des caractères Unicode autorisés dans les noms XML):
<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>
Certes, cela ne tient pas compte du contexte environnant et de quelques cas extrêmes, mais même de telles choses pourraient être traitées si vous le vouliez vraiment (par exemple, en recherchant entre les correspondances d'une autre expression régulière).
À la fin de la journée, utilisez l'outil le plus approprié pour le travail, même dans les cas où cet outil se trouve être une expression régulière.
Bien qu'il ne soit pas approprié et efficace d'utiliser des expressions régulières à cette fin, les expressions régulières fournissent parfois des solutions rapides pour des problèmes de correspondance simples et à mon avis, ce n'est pas si horrible d'utiliser des expressions régulières pour des travaux triviaux.
Il existe un article de blog définitif sur la correspondance des éléments HTML les plus internes, écrit par Steven Levithan.