RegEx match ouvert à l'exception des balises XHTML autonome tags
j'ai besoin de correspondre à tous de ces balises d'ouverture:
<p>
<a href="foo">
mais pas ceux-ci:
<br />
<hr class="foo" />
j'ai trouvé ça et je voulais m'assurer que j'avais bien compris. Je ne capture que le a-z
.
<([a-z]+) *[^/]*?>
je crois que c'est écrit:
- trouver un moins - que, alors
- trouver (et capturer) a-z une ou plusieurs fois, puis
- Trouver zéro ou plus d'espaces, puis
- trouver n'importe quel caractère zéro ou plus de fois, cupide, sauf
/
, puis - trouver un plus grand-que
ai-je le droit? Et, plus important encore, qu'en pensez-vous?
30 réponses
vous ne pouvez pas Parser [X]HTML avec regex. Parce que le HTML ne peut pas être analysé par regex. Regex n'est pas un outil qui peut être utilisé pour analyser correctement HTML. Comme je l'ai répondu dans les questions HTML-et-regex ici tant de fois avant, l'utilisation de regex ne vous permettra pas de consommer HTML. Les expressions régulières sont un outil qui n'est pas suffisamment sophistiqué pour comprendre les constructions employées par HTML. HTML n'est pas un langage régulier et ne peut donc pas être interprété par des expressions régulières. Les requêtes Regex ne sont pas équipé pour décomposer HTML en ses parties significatives. de nombreuses fois, mais il n'est pas arriver à moi. Même les expressions régulières irrégulières améliorées utilisées par Perl ne sont pas à la hauteur de la tâche D'analyse HTML. Tu ne me feras jamais craquer. HTML est un langage d'une complexité telle qu'il ne peut être interprété par des expressions régulières. Même Jon Skeet ne peut pas analyser HTML en utilisant des expressions régulières. Chaque fois que vous tentez D'analyser HTML avec des expressions régulières, l'enfant Impie pleure le sang des vierges, et Les pirates russes repn votre webapp. Analyse HTML avec la regex citation entachée âmes dans le royaume des vivants. HTML et regex vont ensemble comme l'amour, le mariage et l'infanticide rituel. Le
inal snuf
fing o f le mensonge s de L'homme tout est LOŚT A LL EST L OST th e poney à ce qu'il vienne s il com ͎a̧͈͖r̽̾̈́͒͑e
n ot 1519140920 1519140920 1519140920 1519140920 1519140920 1519140920"
avez-vous essayé d'utiliser un analyseur XML à la place?
Note du modérateur
ce message est verrouillé pour empêcher les modifications inappropriées à son contenu. Le post regarde exactement comme il est censé regarder - il n'y a aucun problème avec son contenu. Ne le signalez pas à notre attention.
S'il est vrai que demander à des regexes de parser arbitraire HTML est comme demander à un débutant d'écrire un système d'exploitation, il est parfois approprié de parser un limité, connu ensemble de HTML.
si vous avez un petit ensemble de pages HTML que vous voulez gratter des données à partir et ensuite stuff dans une base de données, regexes pourrait fonctionner très bien. Par exemple, j'ai récemment voulu obtenir les noms, les partis et les districts de L'Australie représentants fédéraux, que j'ai trouvé sur le site web du Parlement. C'était un tirage limité, un temps de travail.
Regexes ont fonctionné très bien pour moi, et ont été très rapide à mettre en place.
je pense que le défaut ici est que HTML est un Chomsky type 2 grammar (context free grammar) et RegEx est un Chomsky Type 3 grammar (regular grammar) . Puisqu'une grammaire de Type 2 est fondamentalement plus complexe qu'une grammaire de Type 3 (voir la hiérarchie de Chomsky ), vous ne pouvez pas faire ce travail. Mais beaucoup vont essayer, certains disent de succès, et d'autres vont trouver la faille et totalement désordre vous.
n'écoutez pas ces gars. Vous totalement can analyse grammaires sans contexte avec regex si vous cassez la tâche en plus petits morceaux. Vous pouvez générer le modèle correct avec un script qui fait chacun de ceux-ci dans l'ordre:
- résolvez le problème D'arrêt.
- carré un cercle.
- résout le problème du vendeur itinérant en o(log N) ou moins. Si C'est plus que ça, tu n'auras plus de mémoire vive. et le moteur se bloque.
- le modèle sera assez grand, donc assurez-vous que vous avez un algorithme qui comprime sans perte des données aléatoires.
- presque là - il suffit de diviser le tout par zéro. Facile comme bonjour.
Je n'ai pas tout à fait terminé la dernière partie moi-même, mais je sais que je me rapproche. Il continue à lancer CthulhuRlyehWgahnaglFhtagnException
s pour une raison quelconque, donc je vais le porter à VB 6 et utiliser On Error Resume Next
. Je vais mettre à jour avec le code une fois que j'aurai enquêté sur cette porte étrange qui vient de s'ouvrir dans le mur. Hum.
P. Pierre de Fermat a aussi compris comment faire, mais la marge qu'il écrivait n'était pas assez grande pour le code.
clause de non-responsabilité : utilisez un analyseur si vous avez l'option. Qui a dit...
C'est le regex que j'utilise (!) pour faire correspondre les balises HTML:
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>
ce n'est peut-être pas parfait, mais j'ai passé ce code à travers un lot de HTML. Notez qu'il capte même des choses étranges comme <a name="badgenerator"">
, qui apparaissent sur le web.
je suppose que pour qu'il ne corresponde pas à des tags auto-contenus, vous voulez soit utiliser Kobi 's négatif look-behind:
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>
ou simplement combiner si et si non.
To downvoters: Ceci est un code de travail à partir d'un produit réel. Je doute que quiconque lisant cette page aura l'impression qu'il est socialement acceptable d'utiliser regexes sur HTML.
Caveat : je dois noter que ce regex se décompose toujours en présence de CDATA des blocs, des commentaires, et le scénario et les éléments de style. Bonne nouvelle, vous pouvez vous débarrasser de ces aide d'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 oblat s'ils veulent utiliser des mots étranges). Ils sont couchés.
il y a des gens qui vous diront que les Expressions régulières ne devraient pas être récursives. Ils sont la limitation de vous. Ils ont besoin de vous soumettre, et ils le font en vous gardant dans l'ignorance.
Vous pouvez vivre dans leur réalité ou prendre la pilule rouge.
Comme Le Seigneur Marshal (est-il un parent du cours de Marshal. net?), J'ai vu la Underverse Pile en Fonction Regex Verset et revient avec pouvoirs les connaissances que vous ne pouvez pas imaginer. Oui, je pense qu'il y en avait un ou deux vieux 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 rendre plus facile à comprenez par votre esprit faible, 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 alors il y a eu une erreur d'analyse et le Regex s'est arrêté.
si vous avez des problèmes pour le reconvertir en une regex lisible par l'utilisateur, 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 suis allongé). Il VA travailler. J'ai construit des tonnes de tests unitaires pour le tester, et j'ai même utilisé (en partie) les tests de conformité . C'est un tokenizer, pas un analyseur complet, donc il ne divisera le XML que dans ses composants. Il ne va pas analyser / intégrer DTDs.
Oh... si vous voulez le code source du regex, avec quelques méthodes auxiliaires:
dans shell, vous pouvez Parser HTML en utilisant:
-
sed bien que:
- de Turing.sed
- écrire HTML parser (devoirs)
- ???
- Profit!
-
hxselect
à partir dehtml-xml-utils
colis -
vim
/ex
(qui peut facilement sauter entre les balises html ), par exemple:-
suppression de l'étiquette de style avec le code intérieur:
$ curl -s http://example.com/ | ex -s +'/<style.*/norm nvatd' +%p -cq! /dev/stdin
-
-
grep
, par exemple:-
html de H1:
$ curl -s http://example.com/ | grep -o '<h1>.*</h1>' <h1>Example Domain</h1>
-
extraction du corps:
$ curl -s http://example.com/ | tr '\n' ' ' | grep -o '<body>.*</body>' <body> <div> <h1>Example Domain</h1> ...
-
-
html2text
à la plaine de l'analyse de texte:-
comme analyse des tables :
$ html2text foo.txt | column -ts'|'
-
-
utilisant
xpath
(XML::XPath
module perl), voir exemple ici -
perl ou Python (voir @Gilles exemple )
-
pour l'analyse de plusieurs fichiers à la fois, voir: comment analyser des centaines de fichiers html source dans shell?
(pourquoi vous ne devriez pas utiliser des regex match):
je conviens que le bon outil pour analyser XML et en particulier HTML est un analyseur et non un moteur d'expression régulier. Cependant, comme d'autres l'ont souligné, l'utilisation d'un regex est parfois plus rapide, plus facile et permet de faire le travail si vous connaissez le format des données.
Microsoft a en fait une section de meilleures pratiques pour les Expressions régulières dans le cadre .NET et parle spécifiquement de envisager[ing] la source D'entrée .
les Expressions régulières ont des limites, mais avez-vous considéré ce qui suit?
le .net framework est unique en ce qui concerne les expressions régulières en ce qu'il supporte Balancing Group Definitions .
- voir corresponding Balanced Constructs with .NET Regular Expressions
- See .NET Expressions régulières: l'expression rationnelle et Équilibrée de Correspondance
- voir Docs de Microsoft sur Balancing Group Definitions
pour cette raison, je crois que vous pouvez analyser XML en utilisant des expressions régulières. Notez cependant, qu'il doit être valide XML ( les navigateurs sont très indulgents de HTML et permettent une mauvaise syntaxe XML à L'intérieur de HTML ). C'est possible puisque l'équilibre Définition du groupe" permettra au moteur d'expression régulier d'agir comme un PDA.
citation de l'article 1 cité ci-dessus:
.NET Moteur d'Expression Régulière
comme décrit ci-dessus, des constructions équilibrées ne peuvent être décrites par une expression régulière. Cependant, le moteur d'expression régulier. net fournit quelques constructions qui permettent équilibrée des constructions à reconnaître.
(?<group>)
- pousse le résultat capturé sur la pile de capture avec le nom du groupe.(?<-group>)
- saute le plus haut de la capture avec le nom du groupe de l' la capture de la pile.(?(group)yes|no)
- correspond à la partie Oui s'il existe un groupe avec le groupe de noms ne correspond autrement aucune partie.ces constructions permettent à une expression régulière .NET d'émuler un PDA restreint en permettant essentiellement des versions simples de la pile opérations: push, pop et vide. Les opérations simples sont assez équivalent à incrémenter, décrémenter et de les comparer à zéro respectivement. Cela permet au moteur d'expression régulière .NET de reconnaître un sous-ensemble de langues sans contexte, en particulier celles qui nécessite un compteur simple. Cela permet à son tour pour les non-traditionnels .net expressions régulières pour reconnaître l'individu correctement équilibré construire.
considérer l'expression régulière suivante:
(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
<!-- .*? --> |
<[^>]*/> |
(?<opentag><(?!/)[^>]*[^/]>) |
(?<-opentag></[^>]*[^/]>) |
[^<>]*
)*
(?(opentag)(?!))
utilisez les drapeaux:
- Singleline
- IgnorePatternWhitespace (pas nécessaire si vous vous effondrez regex et supprimer tous les blancs espace)
- IgnoreCase (pas nécessaire)
Expression Régulière Expliqué (inline)
(?=<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 à un meilleur .net Regular Expression Tester .
j'ai utilisé la source de l'é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>
C'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 réellement 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 voix.
je suggère d'utiliser QueryPath pour analyser XML et HTML en PHP. C'est fondamentalement la même syntaxe que jQuery, seulement c'est du côté du serveur.
bien que les réponses que vous ne pouvez pas analyser HTML avec regexes soient correctes, elles ne s'appliquent pas ici. L'OP veut simplement analyser une balise HTML avec des regexes, et c'est quelque chose qui peut être fait avec une expression régulière.
L'a suggéré regex est mauvais, si:
<([a-z]+) *[^/]*?>
si vous ajoutez quelque chose au regex, par rétractation il peut être forcé de correspondre à des choses stupides comme <a >>
, [^/]
est trop permissif. Notez également que <space>*[^/]*
est redondant, car le [^/]*
peut aussi correspondre à des espaces.
ma suggestion serait
<([a-z]+)[^>]*(?<!/)>
où (?<! ... )
est (dans les regexes Perl) le look négatif. Il lit "<, puis un mot, puis tout ce qui n'est pas un >, dont le dernier peut ne pas être une / suivie par >".
notez que cela permet des choses comme <a/ >
(tout comme le regex original), donc si vous voulez quelque chose de plus restrictif, vous besoin de construire un regex pour apparier des paires d'attributs séparées par des espaces.
, Essayez:
<([^\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 Chinois, général, et philosophe, a dit:
il est dit que si vous connaissez vos ennemis et vous connaissez vous-même, vous pouvez gagner une centaine de batailles sans une seule perte. Si vous ne connaissez que vous-même, mais pas votre adversaire, vous pouvez gagner ou perdre. Si vous ne connaissez ni vous ni votre ennemi, vous vous mettrez toujours en danger.
dans ce cas votre ennemi est HTML et vous êtes soit vous-même ou à l'expression rationnelle. Vous pourriez même être Perl avec regex irrégulier. Connaître le langage HTML. Savez vous-même.
j'ai composé un haïku décrivant la nature de HTML.
HTML has
complexity exceeding
regular language.
j'ai aussi composé un haïku décrivant la nature du 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 );
}
}
sortie:
string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"
définit simplement les noms des noeuds d'éléments qui se ferment d'eux-mêmes, charge toute la chaîne html dans une bibliothèque DOM, saisit tous les éléments, boucle et filtre ceux qui ne se ferment pas d'eux-mêmes et opère sur eux.
je suis sûr que vous savez déjà que vous ne devriez pas utiliser regex dans ce but.
Je ne sais pas si vous en avez vraiment besoin, mais si vous utilisez aussi .NET, ne pouvez-vous pas utiliser Html Agility Pack ?
extrait:
c'est une bibliothèque de code .NET qui permet vous pour analyser" hors du web " HTML fichier. L'analyseur est très tolérant avec" real world " malformé HTML.
vous voulez le premier >
Non précédé d'un /
. Regardez ici pour plus de détails sur la façon de faire cela. C'est ce que l'on appelle le lookbehind négatif.
cependant, une mise en œuvre naïve de ce qui 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? Est-ce que vous itérez par le biais de tags de manière programmatique?
le W3C explique l'analyse sous une forme pseudo regexp:
lien W3C
suivez les liens var pour QName
, S
, et Attribute
pour obtenir une image plus claire.
Basé sur cela, vous pouvez créer un assez bon regexp pour gérer des choses comme les étiquettes de stripping.
si vous avez besoin de cela pour PHP:
le PHP DOM fonctions ne fonctionnera pas correctement à moins qu'il ne soit correctement formaté XML. Peu importe combien leur utilisation est meilleure pour le reste de l'humanité.
simplehtmldom est bon, mais je l'ai trouvé un peu buggy, et il est tout à fait mémoire lourd [se crash sur de grandes pages.]
Je n'ai jamais utilisé querypath , donc ne peut pas commenter sur son utilité.
un autre à essayer est mon DOMParser qui est très léger sur les ressources et je l'ai utilisé heureusement pendant un certain temps. Simple à apprendre et puissant.
pour Python et Java, des liens similaires ont été affiché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. La dévalorisation religieuse empêche juste des réponses utiles d'être affiché-gardez les choses dans la perspective de la question, s'il vous plaît.
j'ai utilisé un outil open source appelé HTMLParser avant. Il est conçu pour analyser HTML de différentes façons et sert assez bien le but. Il peut analyser HTML comme treenode différent et vous pouvez facilement utiliser son API pour obtenir des attributs hors du noeud. Vérifier et voir si cela peut vous aider.
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']
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 étiquettes à fermeture automatique comme:
-
-
- < br>
j'ai aussi entré des étiquettes avec:
- un attribut
- plus d'un attribut
- attributs dont la valeur est liée soit en apostrophes ou en "1519230920 des" guillemets
- attributs contenant des guillemets simples lorsque le délimiteur est une Guillemette double et vice versa
- "unpretty" attributs avec un espace avant le symbole"=", après qu'elle et à la fois avant et après.
si vous trouvez quelque chose qui ne fonctionne pas dans la validation de principe ci-dessus, je suis disponible en analysant le code pour améliorer mon compétence.
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\4\s*)*\s*>/';
l'utilisateur @ridgerunner a remarqué que le modèle ne permet pas attributs non référencés ou attributs sans valeur . Dans ce cas, un réglage fin nous apporte le motif suivant:
$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\5\s*)?)*\s*>/';
< / EDIT>
Comprendre le motif
si quelqu'un est intéressé à en savoir plus sur le modèle, je fournis une certaine ligne:
- la première sous-expression (\w+) correspond au nom de la balise
- la deuxième sous-expression contient le motif d'un attribut. Il est composé par:
- un ou plusieurs espaces blancs \s +
- le nom de l'attribut (\w+)
- zéro ou plusieurs espaces \s* (il est possible ou non, en laissant des blancs ici)
- le symbole " = "
- encore une fois, zéro espace ou plus
- délimiteur de la valeur de l'attribut, simple ou double guillemet ('|"). Dans le motif, la citation unique est échappée car elle coïncide avec le délimiteur de chaîne PHP. Cette sous-expression est capturée avec les parenthèses pour qu'elle puisse être référencé à nouveau pour analyser la fermeture de l'attribut, c'est pourquoi il est très important.
- la valeur de l'attribut, égalée par presque n'importe quoi: (.*?); dans cette syntaxe spécifique, en utilisant le Greedy match (le point d'interrogation après l'astérisque) le moteur RegExp permet 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 backreference , qui se réfère à une sous-expression définie auparavant dans le modèle, dans ce cas, je me réfère à la quatrième sous-expression, qui est le premier délimiteur d'attribut trouvé
- zéro ou plusieurs espaces \s*
- la sous-expression de l'attribut se termine ici, avec la spécification de zéro ou plus d'occurrences possibles, données par l'astérisque.
- puis, depuis un l'étiquette peut se terminer par un espace blanc avant le symbole">", zéro ou plusieurs espaces blancs sont assortis avec le \s* subpattern.
- la balise à apparier peut se terminer par un simple symbole">", ou une fermeture possible XHTML, qui utilise la barre oblique devant elle: (/>|>). La barre oblique est, bien sûr, échappée puisqu'elle coïncide avec le délimiteur de l'expression régulière.
petit conseil: pour mieux analyser ce code, il est nécessaire de regarder le code source généré depuis Je n'ai pas fourni de caractères spéciaux HTML échappant.
j'aime analyser HTML avec des expressions régulières. Je ne cherche pas à analyser HTML idiot qui est délibérément cassé. Ce code est mon analyseur principal (Perl edition):
$_ = 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 étiquette 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 :) Enjoy.
il est assez simple de réajuster mon script Slurp-everything-first Perl en une chose de streaming agréable, si vous souhaitez traiter d'énormes pages web. Mais ce n'est pas vraiment nécessaire.
je parie que je serai rétrogradé pour ça.
contre mon attente cela a suscité quelques critiques, donc je vais suggérer 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 au HTML désordonné... ou convertissez D'abord le HTML -> XHTML.
la meilleure façon d'écrire des expressions régulières est dans le style Lex / Yacc , pas en tant que monocouches opaques ou monstruosités à plusieurs lignes commentées. Je ne l'ai pas encore fait ici, ceux-là en ont à peine besoin.
voici un analyseur basé sur PHP qui analyse HTML en utilisant un certain regex ungodly. En tant qu'auteur de ce projet, je peux vous dire qu'il est possible de parser 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 regexes pour remplacer HTML avec BBCode ici . Pour tous les nonistes, notez qu'il n'essaie pas de pleinement analyser HTML, juste pour l'assainir. Il peut probablement se permettre de tuer des étiquettes que son simple "parser" 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]\[\/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]\[\/b]\n/sgmi;
$html =~ s/<p>/\n\n/gi;
$html =~ s/<br(.*?)>/\n/gi;
$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]\[\/code]/sgmi;
$html =~ s/<b>(.*?)<\/b>/\[b]\[\/b]/gi;
$html =~ s/<i>(.*?)<\/i>/\[i]\[\/i]/gi;
$html =~ s/<u>(.*?)<\/u>/\[u]\[\/u]/gi;
$html =~ s/<em>(.*?)<\/em>/\[i]\[\/i]/gi;
$html =~ s/<strong>(.*?)<\/strong>/\[b]\[\/b]/gi;
$html =~ s/<cite>(.*?)<\/cite>/\[i]\[\/i]/gi;
$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=]\[\/color]/sgmi;
$html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=]\[\/color]/sgmi;
$html =~ s/<link(.*?)>//gi;
$html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]/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\/\[\/img]/gi;
$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/]\[\/url]/gi;
$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/]\[\/url]/gi;
$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/\[\/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/<(?:[^>'"]*|(['"]).*?)*>//gsi;
$html =~ s/\r\r//gi;
$html =~ s/\[img]\//\[img]/gi;
$html =~ s/\[url=\//\[url=/gi;
à propos de la question des méthodes RegExp pour analyser (x)HTML, la réponse à tous ceux qui ont parlé de certaines limites est: vous n'avez pas été suffisamment formé pour contrôler la force de cette arme puissante, depuis personne ici a parlé de récursion .
un collègue de RegExp-agnostic m'a notifié cette discussion, qui n'est certainement pas la première sur le web à propos de ce vieux sujet brûlant.
après en lisant certains billets, la première chose que j'ai faite a été de chercher le "?R" chaîne dans ce fil. La seconde était de chercher à propos de "récursion".
Non, Sainte vache, aucune correspondance trouvée.
Comme personne n'a mentionné le mécanisme principal sur lequel un analyseur est construit, j'ai vite compris que personne n'avait compris.
si un analyseur (x)HTML a besoin de récursion, un analyseur RegExp sans récursion n'est pas suffisant. C'est une construction simple.
le art noir de RegExp est difficile à maîtriser , donc 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 le web entier dans une main... Eh bien, j'en suis sûr:)
voici le motif magique:
$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\1[\s]*>))/s";
essayez.
Il est écrit comme une chaîne de caractères PHP, donc le modificateur "s" permet aux classes d'inclure les nouvelles lignes.
Voici un exemple de note sur le manuel PHP j'ai écrit en janvier: référence
(attention, dans cette note j'ai mal utilisé le modificateur" m"; il doit être effacé, bien qu'il soit écarté par le moteur RegExp, puisqu'aucun ancrage n'a été utilisé).
Maintenant, on parle des limites de cette méthode d'un point de vue informé:
- selon la mise en œuvre spécifique du moteur RegExp, recursion peut avoir une limite dans le nombre de motifs imbriqués parsed , mais cela dépend du langage utilisé
- bien que corrompu (x)HTML ne conduit pas dans des erreurs graves, il n'est pas assaini .
de toute façon, ce n'est qu'un motif RegExp, mais il révèle la possibilité de développer de nombreuses implémentations puissantes.
J'ai écrit Ce modèle pour alimenter le Parser de descente récursive d'un moteur de modèle que j'ai construit dans mon cadre, et les performances sont vraiment grandes, à la fois dans les délais d'exécution ou dans l'utilisation de 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é, HTML n'est pas un langage régulier qui peut le rendre très difficile à analyser. Ma solution c'est de le transformer en un langage régulier à l'aide d'un bon programme, puis utiliser un analyseur XML pour consommer les résultats. Il ya 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 à xpath dans le résultat.
<\s*(\w+)[^/>]*>
les parties expliquées:
<
: caractère de départ
\s*
: il peut avoir des espaces avant le nom de l'étiquette (laid mais possible).
(\w+)
: les étiquettes peuvent contenir des lettres et des chiffres (h1). Eh bien, \w
correspond aussi à"_", mais ça ne fait pas mal je suppose. Si curieux utiliser ([A-zA-Z0-9]+) à la place.
[^/>]*
: tout sauf >
et /
jusqu'à la fermeture >
>
: fermeture >
sans lien
et à ceux qui sous-estiment les expressions régulières en disant qu'elles sont seulement aussi puissantes que les langues régulières:
un n ba n ba n qui n'est pas régulière et pas même sans contexte, peut être mis en correspondance avec ^(a+)bb$
Backreferencing FTW !
j'ai récemment écrit un désinfectant HTML en Java. Il est basé sur une approche mixte d'expressions régulières et de code Java. Personnellement je déteste les expressions régulières et sa folie (lisibilité, maintenabilité, etc.), mais si vous réduisez le champ de ses applications, il peut s'adapter à vos besoins. Quoi qu'il en soit, mon désinfectant utilise une liste blanche pour les balises HTML et une liste noire pour certains attributs de style.
pour votre commodité, j'ai mis en place un terrain de jeu pour que vous puissiez tester si le code correspond vos exigences: playground et Code Java . Vos commentaires seront appréciés.
il y a un petit article décrivant ce travail sur mon blog: http://roberto.open-lab.com
Il me semble que vous essayez de faire correspondre les balises sans un "/" à la fin. Essayez ceci:
<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
si vous essayez simplement de trouver ces étiquettes (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 tags que vous avez mentionnés, tout en ignorant les types que vous avez dit que vous vouliez ignorer.
bien qu'il ne soit pas approprié et efficace d'utiliser des expressions régulières dans ce but, il arrive que des expressions régulières fournissent des solutions rapides pour des problèmes d'allumettes simples et, à mon avis, ce n'est pas une horreur d'utiliser des expressions régulières pour des travaux insignifiants.
Il y a un définitive blog sur la correspondance intime des éléments HTML écrit par Steven Levithan.