RegEx stimmt mit offenen Tags überein, mit Ausnahme von in sich geschlossenen XHTML-Tags

1473

Ich muss alle diese Eröffnungs-Tags abgleichen:

<p>
<a href="foo">

Aber nicht diese:

<br />
<hr class="foo" />

Ich habe mir das ausgedacht und wollte sicherstellen, dass ich es richtig verstanden habe. Ich fange nur das ein a-z.

<([a-z]+) *[^/]*?>

Ich glaube es heißt:

  • Finden Sie dann einen weniger als
  • Suchen (und erfassen) Sie az dann ein- oder mehrmals
  • Suchen Sie dann null oder mehr Leerzeichen
  • Finden Sie ein Zeichen null oder mehrmals, gierig, außer /dann
  • Finde ein Größer als

Habe ich das richtig Und was denkst du noch wichtiger?

Jeff
quelle

Antworten:

4417

Sie können [X] HTML nicht mit Regex analysieren. Weil HTML nicht durch Regex analysiert werden kann. Regex ist kein Tool, mit dem HTML korrekt analysiert werden kann. Wie ich hier schon so oft in HTML- und Regex-Fragen beantwortet habe, können Sie mit der Verwendung von Regex kein HTML verwenden. Reguläre Ausdrücke sind ein Werkzeug, das nicht ausgereift genug ist, um die von HTML verwendeten Konstrukte zu verstehen. HTML ist keine reguläre Sprache und kann daher nicht durch reguläre Ausdrücke analysiert werden. Regex-Abfragen sind nicht in der Lage, HTML in seine sinnvollen Teile zu zerlegen. so oft, aber es geht mir nicht auf die Nerven. Selbst verbesserte unregelmäßige reguläre Ausdrücke, wie sie von Perl verwendet werden, sind nicht in der Lage, HTML zu analysieren. Du wirst mich niemals zum Knacken bringen. HTML ist eine Sprache von ausreichender Komplexität, die nicht durch reguläre Ausdrücke analysiert werden kann. Selbst Jon Skeet kann HTML nicht mit regulären Ausdrücken analysieren. Jedes Mal, wenn Sie versuchen, HTML mit regulären Ausdrücken zu analysieren, weint das unheilige Kind das Blut von Jungfrauen, und russische Hacker pwn Ihre Webapp. Das Parsen von HTML mit Regex-Beschwörungen befleckte Seelen in das Reich der Lebenden. HTML und Regex gehören zusammen wie Liebe, Ehe und ritueller Kindsmord. Das <Center> kann es nicht halten, es ist zu spät. Die Kraft von Regex und HTML zusammen im selben konzeptuellen Raum wird Ihren Geist zerstören wie so viel wässriger Kitt. Wenn Sie HTML mit Regex analysieren, geben Sie ihnen und ihren blasphemischen Methoden nach, die uns alle zur unmenschlichen Arbeit für denjenigen verurteilen, dessen Name in der mehrsprachigen Grundebene nicht ausgedrückt werden kann. HTML-plus-Regexp wird die Nerven des Lebewesens verflüssigen, während Sie beobachten, wie Ihre Psyche im Ansturm des Grauens verdorrt.Es ist zu spät. Es ist zu spät. Wir können nicht gerettet werden. Die Trangession eines Kindes stellt sicher, dass Regex das gesamte lebende Gewebe verbraucht (mit Ausnahme von HTML, das, wie zuvor prophezeit, nicht). Sehr geehrter Herr, helfen Sie uns, wie jemand diese Geißel mit Regex zum Parsen überleben kann HTML hat die Menschheit zu einer Ewigkeit der Angst Folter und Sicherheitslücken verurteilt mit Rege x als Instrument zum Prozess HTML stellt eine Brea ch zwischen dieser Welt und der Furcht Reich der korrupten Entitäten (wie SGML Entitäten, sondern mehr korrupt) einem bloßen glimp se die Welt der reg ex - Parser für HTML wird ins tantly Transport ap Bewusstsein rogrammer i nto aw orl d unaufhörlichen schreien, er kommt, Die pestilent sl Ithy regex-Infektion wil l Verschlingen Ihre HT ML - Parser, Anwendung und Existenz für alle Zeiten wie Visual Basic nur noch schlimmer er kommt er com es nicht fi ght h e kommt, HALLO s unheilige Radiance de stro҉ying , alle AUFKLäRUNG HTML - Tags Undichte fr̶ǫm Ih re Augen wie liq uid p ain, das Lied von regelmäßig exp Re ssion Parsing wird extI nguish die Stimmen von mor tal Mann aus dem sp hier kann ich sehen , es können Sie sehen , es ist schön t er f inal snufFing o f die Lüge s of Man ist alles verloren A LL I SLOST th e PONY er kommt s er CoM es ihn zusammen mir s t er mich oder Permeats es al l MY FAC E MEIN GESICHT ᵒh Gott n o NO noo O ON Θ Anschlag t er ein * ̶͑̾̾ gl ES ͎a̧͈͖r̽̾̈́͒͑en ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ̯͍̭P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ


Haben Sie versucht, stattdessen einen XML-Parser zu verwenden?


Anmerkung des Moderators

Dieser Beitrag ist gesperrt, um unangemessene Änderungen an seinem Inhalt zu verhindern. Der Beitrag sieht genau so aus, wie er aussehen soll - es gibt keine Probleme mit seinem Inhalt. Bitte kennzeichnen Sie es nicht für unsere Aufmerksamkeit.

Bobince
quelle
179
Kobi: Ich denke, es ist Zeit für mich, den Posten von Assistant Don't Parse HTML With Regex Officer zu kündigen. Egal wie oft wir es sagen, sie werden nicht aufhören, jeden Tag zu kommen ... sogar jede Stunde. Es ist eine verlorene Sache, für die jemand anderes ein bisschen kämpfen kann. Analysieren Sie also HTML mit Regex, wenn Sie müssen. Es ist nur gebrochener Code, nicht Leben und Tod.
Bobince
27
Ist es möglich, diese Antwort mit RegEx zu analysieren?
Chris Porter
2
Wenn Sie diesen Beitrag nicht sehen können, ist hier eine Bildschirmaufnahme
Andrew Keeton
3249

Während beliebiges HTML mit nur einem regulären Ausdruck unmöglich ist, ist es manchmal angebracht, sie zum Parsen eines begrenzten, bekannten Satzes von HTML zu verwenden.

Wenn Sie eine kleine Gruppe von HTML-Seiten haben, von denen Sie Daten kratzen und dann in eine Datenbank einfügen möchten, funktionieren reguläre Ausdrücke möglicherweise einwandfrei. Zum Beispiel wollte ich kürzlich die Namen, Parteien und Bezirke der australischen Bundesvertreter erfahren, die ich von der Website des Parlaments erhalten habe. Dies war ein begrenzter, einmaliger Job.

Regexes funktionierte gut für mich und war sehr schnell einzurichten.

Kaitlin Duck Sherwood
quelle
131
Außerdem wird das Scrapen von regelmäßig regelmäßig formatierten Daten aus großen Dokumenten mit vernünftiger Verwendung von Scan & Regex viel schneller sein als mit jedem generischen Parser. Und wenn Sie mit dem Codieren von Regexen vertraut sind, ist das Codieren viel schneller als das Codieren von xpaths. Und mit ziemlicher Sicherheit weniger anfällig für Änderungen an dem, was Sie kratzen. Also bleh.
Michael Johnston
255
@ MichaelJohnston "Weniger zerbrechlich"? Mit ziemlicher Sicherheit nicht. Regexes kümmern sich um Textformatierungsdetails, die ein XML-Parser stillschweigend ignorieren kann. Zwischen &foo;Codierungen und CDATAAbschnitten wechseln? Verwenden Sie einen HTML-Minifier, um alle Leerzeichen in Ihrem Dokument zu entfernen, die der Browser nicht rendert? Ein XML-Parser kümmert sich nicht darum, ebenso wenig wie eine gut geschriebene XPath-Anweisung. Ein Regex-basierter "Parser" andererseits ...
Charles Duffy
41
@ CharlesDuffy für einen einmaligen Job ist es in Ordnung, und für Leerzeichen verwenden wir \ s +
Quantum
68
@xiaomao in der Tat, wenn ich alle Fallstricke und Problemumgehungen kennen muss, um eine 80% ige Lösung zu erhalten, die den Rest der Zeit "funktioniert für Sie" nicht funktioniert, kann ich Sie nicht aufhalten. In der Zwischenzeit bin ich mit Parsern auf meiner Seite des Zauns, die mit 100% syntaktisch gültigem XML arbeiten.
Charles Duffy
374
Ich musste einmal einige Daten von ~ 10.000 Seiten abrufen, alle mit derselben HTML-Vorlage. Sie waren mit HTML-Fehlern übersät, die dazu führten, dass Parser erstickten, und ihr gesamtes Styling war inline oder mit <font>usw.: Keine Klassen oder IDs zur Navigation im DOM. Nachdem ich den ganzen Tag mit dem "richtigen" Ansatz gekämpft hatte, wechselte ich schließlich zu einer Regex-Lösung und ließ sie in einer Stunde funktionieren.
Paul A Jungwirth
2037

Ich denke, der Fehler hier ist, dass HTML eine Chomsky-Typ-2-Grammatik (kontextfreie Grammatik) und RegEx eine Chomsky-Typ-3-Grammatik (reguläre Grammatik) ist . Da eine Grammatik vom Typ 2 grundsätzlich komplexer ist als eine Grammatik vom Typ 3 (siehe Chomsky-Hierarchie ), ist es mathematisch unmöglich , XML mit RegEx zu analysieren.

Aber viele werden es versuchen, einige werden sogar Erfolg beanspruchen - aber bis andere den Fehler finden und dich total durcheinander bringen.

Vlad Gudim
quelle
225
Das OP bittet darum, eine sehr begrenzte Teilmenge von XHTML: Start-Tags zu analysieren. Was (X) HTML zu einer CFG macht, ist das Potenzial, Elemente zwischen den Start- und End-Tags anderer Elemente zu haben (wie in einer Grammatikregel A -> s A e). (X) HTML ist nicht diese Eigenschaft hat in einem Start - Tag: ein Start - Tag enthalten kann Tags keine anderen Anfang. Die Teilmenge, die das OP zu analysieren versucht, ist keine CFG.
LarsH
101
In CS Theorie, reguläre Sprachen sind eine strenge Teilmenge der kontextfreien Sprachen, aber reguläre Ausdrücke Implementierungen in den Mainstream - Programmiersprachen sind leistungsfähiger. Wie noulakaz.net/weblog/2007/03/18/… beschreibt, können sogenannte "reguläre Ausdrücke" auf unäre Primzahlen prüfen, was sicherlich ein regulärer Ausdruck aus der CS-Theorie nicht erreichen kann.
Adam Mihalcin
11
@eyelidlessness: das gleiche "nur wenn" gilt für alle CFGs, nicht wahr? Das heißt, wenn die (X) HTML-Eingabe nicht gut geformt ist, funktioniert nicht einmal ein ausgewachsener XML-Parser zuverlässig. Wenn Sie Beispiele für die "(X) HTML-Syntaxfehler, die in realen Benutzeragenten implementiert sind" angeben, auf die Sie sich beziehen, verstehe ich möglicherweise, was Sie besser erreichen.
LarsH
82
@ AdamMihalcin ist genau richtig. Die meisten vorhandenen Regex-Engines sind leistungsfähiger als Chomsky-Typ-3-Grammatiken (z. B. nicht gieriges Matching, Backrefs). Einige Regex-Engines (wie Perls) sind vollständig. Es ist wahr, dass selbst dies schlechte Tools zum Parsen von HTML sind, aber dieses oft zitierte Argument ist nicht der Grund dafür.
Dubiousjim
27
Dies ist die "vollständigste und kurzeste" Antwort hier. Es führt die Leute dazu, Grundlagen formaler Grammatiken und Sprachen und hoffentlich einige Mathematik zu lernen, damit sie keine Zeit mit hoffnungslosen Dingen wie dem Lösen von NP-Aufgaben in Polynomzeit verschwenden
Mischmashru
1332

Hör nicht auf diese Jungs. Sie können kontextfreie Grammatiken mit Regex vollständig analysieren, wenn Sie die Aufgabe in kleinere Teile aufteilen. Sie können das richtige Muster mit einem Skript generieren, das diese in der folgenden Reihenfolge ausführt:

  1. Lösen Sie das Halteproblem.
  2. Quadriere einen Kreis.
  3. Erarbeiten Sie das Problem des Handlungsreisenden in O (log n) oder weniger. Wenn es mehr als das ist, wird Ihnen der RAM ausgehen und die Engine wird hängen bleiben.
  4. Das Muster ist ziemlich groß. Stellen Sie daher sicher, dass Sie einen Algorithmus haben, der zufällige Daten verlustfrei komprimiert.
  5. Fast da - teilen Sie das Ganze einfach durch Null. Kinderleicht.

Ich habe den letzten Teil selbst noch nicht ganz beendet, aber ich weiß, dass ich näher komme. Es wirft CthulhuRlyehWgahnaglFhtagnExceptionaus irgendeinem Grund immer wieder s, also werde ich es auf VB 6 portieren und verwenden On Error Resume Next. Ich werde mit dem Code aktualisieren, sobald ich diese seltsame Tür untersucht habe, die sich gerade in der Wand geöffnet hat. Hmm.

PS Pierre de Fermat fand auch heraus, wie es geht, aber der Rand, in den er schrieb, war nicht groß genug für den Code.

Justin Morgan
quelle
80
Die Division durch Null ist ein viel einfacheres Problem als die anderen, die Sie erwähnen. Wenn Sie Intervalle anstelle einer einfachen Gleitkomma-Arithmetik verwenden (die jeder sein sollte, aber niemand), können Sie etwas glücklich durch [ein Intervall mit] Null teilen. Das Ergebnis ist einfach ein Intervall mit Plus und Minus unendlich.
rjmunro
148
Das Problem der kleinen Ränder von Fermat wurde durch weiche Ränder in moderner Textbearbeitungssoftware gelöst.
kd4ttc
50
Fermats kleines Randproblem wurde von Randall Munroe gelöst, indem die Schriftgröße auf Null gesetzt wurde: xkcd.com/1381
heltonbiker
29
Zu Ihrer Information: Fermats Problem wurde tatsächlich 1995 gelöst , und es dauerte nur 358 Jahre, bis Mathematiker dies taten.
jmiserez
10
Ich konnte diesen klebrigen Schritt der Division durch Null umgehen, indem ich stattdessen Brownsche Ratschen verwendete, die aus der Kaltfusion stammen ... obwohl es nur funktioniert, wenn ich die kosmologische Konstante entferne.
Tim Lehner
1072

Haftungsausschluss : Verwenden Sie einen Parser, wenn Sie die Option haben. Das gesagt...

Dies ist der reguläre Ausdruck, den ich (!) Zum Abgleichen von HTML-Tags verwende:

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

Es mag nicht perfekt sein, aber ich habe diesen Code durch viel HTML ausgeführt. Beachten Sie, dass es sogar seltsame Dinge auffängt <a name="badgenerator"">, die im Web auftauchen.

Ich denke, damit es nicht zu eigenständigen Tags passt, möchten Sie entweder Kobis negativen Look-Behind verwenden:

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

oder einfach kombinieren, wenn und wenn nicht.

An Downvoter: Dies ist Arbeitscode von einem tatsächlichen Produkt. Ich bezweifle, dass jemand, der diese Seite liest, den Eindruck bekommt, dass es gesellschaftlich akzeptabel ist, Regexes in HTML zu verwenden.

Vorsichtsmaßnahme : Ich sollte beachten, dass dieser reguläre Ausdruck bei Vorhandensein von CDATA-Blöcken, Kommentaren sowie Skript- und Stilelementen immer noch zusammenbricht. Eine gute Nachricht ist, dass Sie diese mit einem regulären Ausdruck loswerden können ...

itsadok
quelle
94
Ich würde mit etwas gehen, das an vernünftigen Dingen arbeitet, als darüber zu weinen, dass es nicht universell perfekt ist :-)
Prajeesh Kumar
55
Verwendet jemand CDATA in HTML?
Danubian Sailor
16
Sie lösen das Parsing-Problem also nicht nur mit regulärem Ausdruck, sondern als Teil des Parsers funktioniert dies möglicherweise. PS: Arbeitsprodukt bedeutet nicht guten Code. Nichts für ungut, aber so funktioniert industrielle Programmierung und bekommt ihr Geld
mishmashru
32
Ihre Regex-Starts schlagen mit dem kürzestmöglichen, gültigen HTML fehl : <!doctype html><title><</title>. Einfache '<!doctype html><title><</title>'.match(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g)Rückgabe ["<!doctype html>", "<title>", "<</title>"]während sollte ["<title>", "</title>"].
2
Wenn wir nur versuchen, mit den angegebenen Beispielen übereinzustimmen und nicht übereinzustimmen, funktioniert /< "foo"> <br /> <hr class = "foo" />'.match(/<.([^r>[^>‹*)?>/g)
imma
506

Es gibt Leute, die Ihnen sagen, dass die Erde rund ist (oder dass die Erde ein abgeflachter Sphäroid ist, wenn sie seltsame Wörter verwenden möchten). Sie lügen.

Es gibt Leute, die Ihnen sagen, dass reguläre Ausdrücke nicht rekursiv sein sollten. Sie begrenzen dich. Sie müssen dich unterwerfen, und sie tun es, indem sie dich in Unwissenheit halten.

Sie können in ihrer Realität leben oder die rote Pille nehmen.

Wie Lord Marshal (er ist ein Verwandter der Klasse Marshal .NET?), Ich habe das gesehen Underverse Stapel Based Regex-Verse und mit zurück Kräfte Wissen Sie können sich nicht vorstellen. Ja, ich glaube, es gab ein oder zwei Alte, die sie beschützten, aber sie sahen Fußball im Fernsehen, also war es nicht schwierig.

Ich denke, der XML-Fall ist recht einfach. Die RegEx (in der .NET-Syntax), die in base64 deflationiert und codiert ist, um das Verständnis für Ihren schwachen Verstand zu erleichtern, sollte ungefähr so ​​aussehen:

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
+r/vD34mUADO1P4/AQAA//8=

Die einzustellenden Optionen sind RegexOptions.ExplicitCapture. Die von Ihnen gesuchte Erfassungsgruppe ist ELEMENTNAME. Wenn die Erfassungsgruppe ERRORnicht leer ist, ist ein Analysefehler aufgetreten und der Regex wurde gestoppt.

Wenn Sie Probleme haben, es in einen für Menschen lesbaren regulären Ausdruck umzuwandeln, sollte dies helfen:

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());
    }
}

Wenn Sie sich nicht sicher sind, nein, ich mache keine Witze (aber vielleicht lüge ich). Es wird klappen. Ich habe Tonnen von Komponententests gebaut, um es zu testen, und ich habe sogar (einen Teil) der Konformitätstests verwendet . Es ist ein Tokenizer, kein ausgewachsener Parser, daher wird das XML nur in seine Komponententoken aufgeteilt. DTDs werden nicht analysiert / integriert.

Oh ... wenn Sie den Quellcode des regulären Ausdrucks mit einigen Hilfsmethoden möchten:

Regex zum Tokenisieren einer XML-Datei oder des vollständigen einfachen Regex

Xanatos
quelle
68
Mein Gott, es ist massiv. Meine größte Frage ist warum? Sie erkennen, dass alle modernen Sprachen XML-Parser haben, oder? Sie können das alles in 3 Zeilen erledigen und sicher sein, dass es funktioniert. Ist Ihnen außerdem klar, dass reiner Regex bestimmte Dinge nachweislich nicht kann? Es sei denn, Sie haben einen hybriden Regex- / Imperativ-Code-Parser erstellt, aber es sieht nicht so aus, als hätten Sie ihn. Können Sie auch zufällige Daten komprimieren?
Justin Morgan
113
@ Justin Ich brauche keinen Grund. Es konnte getan werden (und es war nicht illegal / unmoralisch), also habe ich es getan. Es gibt keine Einschränkungen für den Verstand außer denen, die wir anerkennen (Napoleon Hill) ... Moderne Sprachen können XML analysieren? "Ja wirklich?" Und ich dachte, dass das illegal ist! :-)
Xanatos
76
Sir, ich bin überzeugt. Ich werde diesen Code als Teil des Kernels für meine Perpetual-Motion-Maschine verwenden. Können Sie glauben, dass diese Dummköpfe beim Patentamt meine Anmeldung immer wieder ablehnen? Nun, ich werde es ihnen zeigen. Ich werde sie alle zeigen!
Justin Morgan
31
@Justin Ein XML-Parser ist also per Definition fehlerfrei, ein Regex nicht? Denn wenn ein Xml-Parser per Definition nicht fehlerfrei ist, kann es zu einem XML-Parser kommen, der zum Absturz führt, und wir kehren zu Schritt 0 zurück. Sagen wir Folgendes: Sowohl der Xml-Parser als auch dieser Regex versuchen, alle "legalen" Daten zu analysieren "XML. Sie KÖNNEN "illegales" XML analysieren. Bugs könnten beide zum Absturz bringen. C # XmlReader ist sicherlich mehr getestet als dieser Regex.
Xanatos
31
Nein, nichts ist fehlerfrei: 1) Alle Programme enthalten mindestens einen Fehler. 2) Alle Programme enthalten mindestens eine Zeile unnötigen Quellcodes. 3) Mit # 1 und # 2 und unter Verwendung der logischen Induktion ist es einfach zu beweisen, dass jedes Programm mit einem Fehler auf eine einzige Codezeile reduziert werden kann. (von Learning Perl)
Scott Weaver
299

In der Shell können Sie HTML mit sed analysieren :

  1. Turing.sed
  2. HTML-Parser schreiben (Hausaufgaben)
  3. ???
  4. Profitieren!

Verwandte (warum Sie Regex Match nicht verwenden sollten):

Kenorb
quelle
3
Ich fürchte, Sie haben den Witz nicht verstanden, @kenorb. Bitte lesen Sie die Frage und die akzeptierte Antwort noch einmal. Hier geht es weder um HTML-Parsing-Tools im Allgemeinen noch um HTML-Parsing-Shell-Tools, sondern um das Parsen von HTML über reguläre Ausdrücke.
Palec
1
Nein, @Abdul. Es ist völlig nachweisbar (im mathematischen Sinne) unmöglich.
Palec
3
Ja, diese Antwort fasst es gut zusammen, @Abdul. Beachten Sie jedoch, dass Regex-Implementierungen keine wirklich regulären Ausdrücke im mathematischen Sinne sind - sie haben Konstrukte, die sie stärker machen, oft Turing-vollständig (entspricht Grammatiken vom Typ 0). Das Argument bricht mit dieser Tatsache, ist aber immer noch in dem Sinne gültig, dass Regexe niemals dazu in der Lage sein sollten, einen solchen Job zu erledigen.
Palec
2
Übrigens war der Witz, auf den ich mich bezog, der Inhalt dieser Antwort vor Kenorbs (radikalen) Änderungen, insbesondere Revision 4, @Abdul.
Palec
3
Das Lustige ist, dass OP nie darum gebeten hat, HTML mit Regex zu analysieren. Er bat darum, Text (der zufällig HTML ist) mit Regex abzugleichen. Welches ist völlig vernünftig.
Paralife
274

Ich bin damit einverstanden, dass das richtige Tool zum Parsen von XML und insbesondere HTML ein Parser und keine Engine für reguläre Ausdrücke ist. Wie andere bereits betont haben, ist die Verwendung eines regulären Ausdrucks manchmal schneller, einfacher und erledigt die Aufgabe, wenn Sie das Datenformat kennen.

Microsoft hat tatsächlich einen Abschnitt mit Best Practices für reguläre Ausdrücke in .NET Framework und spricht speziell über die Berücksichtigung der Eingabequelle .

Reguläre Ausdrücke haben zwar Einschränkungen, aber haben Sie Folgendes berücksichtigt?

Das .NET Framework ist einzigartig, wenn es um reguläre Ausdrücke geht, da es Ausgleichsgruppendefinitionen unterstützt .

Aus diesem Grund glaube ich, dass Sie XML mit regulären Ausdrücken analysieren können. Beachten Sie jedoch, dass es sich um gültiges XML handeln muss ( Browser verzeihen HTML sehr und erlauben eine schlechte XML-Syntax in HTML ). Dies ist möglich, da die "Balancing Group Definition" es der Engine für reguläre Ausdrücke ermöglicht, als PDA zu fungieren.

Zitat aus Artikel 1 oben zitiert:

.NET-Engine für reguläre Ausdrücke

Wie oben beschrieben, können richtig ausgeglichene Konstrukte nicht durch einen regulären Ausdruck beschrieben werden. Die .NET-Engine für reguläre Ausdrücke bietet jedoch einige Konstrukte, mit denen ausgewogene Konstrukte erkannt werden können.

  • (?<group>) - schiebt das erfasste Ergebnis mit der Namensgruppe auf den Erfassungsstapel.
  • (?<-group>) - Das oberste Capture wird mit der Namensgruppe vom Capture-Stapel entfernt.
  • (?(group)yes|no) - stimmt mit dem Ja-Teil überein, wenn eine Gruppe mit der Namensgruppe vorhanden ist, andernfalls stimmt kein Teil überein.

Diese Konstrukte ermöglichen es einem regulären .NET-Ausdruck, einen eingeschränkten PDA zu emulieren, indem im Wesentlichen einfache Versionen der Stapeloperationen zugelassen werden: Push, Pop und Leer. Die einfachen Operationen sind so ziemlich gleichbedeutend mit Inkrementieren, Dekrementieren und Vergleichen mit Null. Auf diese Weise kann die .NET-Engine für reguläre Ausdrücke eine Teilmenge der kontextfreien Sprachen erkennen, insbesondere diejenigen, für die nur ein einfacher Zähler erforderlich ist. Dies ermöglicht wiederum, dass die nicht traditionellen regulären .NET-Ausdrücke einzelne richtig ausgewogene Konstrukte erkennen.

Betrachten Sie den folgenden regulären Ausdruck:

(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
   <!-- .*? -->                  |
   <[^>]*/>                      |
   (?<opentag><(?!/)[^>]*[^/]>)  |
   (?<-opentag></[^>]*[^/]>)     |
   [^<>]*
)*
(?(opentag)(?!))

Verwenden Sie die Flags:

  • Einzelne Zeile
  • IgnorePatternWhitespace (nicht erforderlich, wenn Sie Regex reduzieren und alle Leerzeichen entfernen)
  • IgnoreCase (nicht erforderlich)

Regulärer Ausdruck erklärt (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

Sie können dies bei A Better .NET Regular Expression Tester versuchen .

Ich habe die Beispielquelle verwendet von:

<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 &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>
</div>
</body>
</html>

Dies fand die Übereinstimmung:

   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>

obwohl es tatsächlich so herauskam:

<ul id="matchMe" type="square">           <li>stuff...</li>           <li>more stuff</li>           <li>               <div>                    <span>still more</span>                    <ul>                         <li>Another &gt;ul&lt;, oh my!</li>                         <li>...</li>                    </ul>               </div>           </li>        </ul>

Zuletzt hat mir Jeff Atwoods Artikel: Parsing Html The Cthulhu Way sehr gut gefallen . Komischerweise wird die Antwort auf diese Frage zitiert, die derzeit über 4.000 Stimmen hat.

Sam
quelle
18
System.Textist nicht Teil von C #. Es ist Teil von .NET.
John Saunders
8
In der ersten Zeile Ihres regulären Ausdrucks ( (?=<ul\s*id="matchMe"\s*type="square"\s*>) # match start with <ul id="matchMe"...) sollte zwischen "<ul" und "id" \s+nicht stehen \s*, es sei denn, Sie möchten, dass es mit <ulid = ... übereinstimmt;)
C0deH4cker
@ C0deH4cker Sie richtig sind, sollte der Ausdruck haben \s+statt \s*.
Sam
4
Nicht, dass ich es wirklich verstehe, aber ich denke, Ihre Regex versagt am<img src="images/pic.jpg" />
Scheintod
3
@Scheintod Danke für den Kommentar. Ich habe den Code aktualisiert. Der vorherige Ausdruck schlug für selbstschließende Tags fehl, in denen sich /irgendwo etwas befand, das für Ihr <img src="images/pic.jpg" />HTML fehlschlug .
Sam
258

Ich schlage vor, QueryPath zum Parsen von XML und HTML in PHP zu verwenden. Es ist im Grunde die gleiche Syntax wie jQuery, nur auf der Serverseite.

John Fiala
quelle
8
@ Kyle - jQuery analysiert kein XML, sondern verwendet den integrierten Parser des Clients (falls vorhanden). Daher benötigen Sie dazu nicht jQuery, sondern nur zwei Zeilen einfaches altes JavaScript . Wenn kein integrierter Parser vorhanden ist, hilft jQuery nicht.
RobG
1
@RobG Tatsächlich verwendet jQuery das DOM, nicht den integrierten Parser.
Qix - MONICA wurde
11
@ Qix - Sie sollten den Autoren der Dokumentation dann besser sagen: " jQuery.parseXML verwendet die native Analysefunktion des Browsers ... ". Quelle: jQuery.parseXML ()
RobG
6
Nachdem ich von der Meme-Frage ( meta.stackexchange.com/questions/19478/the-many-memes-of-meta/… ) hierher gekommen bin , finde ich es toll , dass eine der Antworten "Use jQuery"
Jorn
221

Die Antworten, dass Sie HTML nicht mit regulären Ausdrücken analysieren können, sind zwar korrekt, gelten hier jedoch nicht. Das OP möchte nur ein HTML-Tag mit regulären Ausdrücken analysieren, und das kann mit einem regulären Ausdruck erfolgen.

Der vorgeschlagene reguläre Ausdruck ist jedoch falsch:

<([a-z]+) *[^/]*?>

Wenn Sie etwas zu dem regulären Ausdruck hinzufügen, indem Rückzieher kann es gezwungen sein , dumme Sachen zu passen wie <a >>, [^/]zu tolerant ist. Beachten Sie auch, dass dies <space>*[^/]*redundant ist, da die [^/]*auch Leerzeichen entsprechen können.

Mein Vorschlag wäre

<([a-z]+)[^>]*(?<!/)>

Wo (?<! ... )ist (in Perl-Regexen) der negative Rückblick. Es lautet "a <, dann ein Wort, dann alles, was nicht a> ist, von denen das letzte möglicherweise kein / ist, gefolgt von>".

Beachten Sie, dass dies Dinge wie <a/ >(genau wie der ursprüngliche reguläre Ausdruck) zulässt. Wenn Sie also etwas restriktiveres wünschen, müssen Sie einen regulären Ausdruck erstellen, der mit durch Leerzeichen getrennten Attributpaaren übereinstimmt.

moritz
quelle
29
+1 für die Feststellung, dass es bei der Frage nicht um das Parsen von vollständigem (X) HTML geht, sondern um das Abgleichen von (X) offenen HTML-Tags.
LarsH
10
Etwas anderes, was die meisten Antworten zu ignorieren scheinen, ist, dass ein HTML-Parser in seiner Implementierung sehr gut reguläre Ausdrücke für Teile von HTML verwenden kann, und ich wäre überrascht, wenn die meisten Parser dies nicht tun würden.
Thayne
@ Thayne Genau. Beim Parsen einzelner Tags ist ein regulärer Ausdruck das richtige Werkzeug für den Job. Es ist ziemlich lächerlich, dass man die Hälfte der Seite nach unten scrollen muss, um eine vernünftige Antwort zu finden. Die akzeptierte Antwort ist falsch, weil sie Lexing und Parsing verwechselt.
Kasperd
2
Die hier gegebene Antwort schlägt fehl, wenn ein Attributwert ein '>' oder '/' Zeichen enthält.
Martin L
Dies funktioniert bei HTML mit Kommentaren oder CData-Abschnitten nicht ordnungsgemäß. Es funktioniert auch nicht richtig, wenn ein Anführungszeichen ein >Zeichen enthält . Ich stimme zu, was OP mit einem regulären Ausdruck vorschlagen kann , aber der hier vorgestellte ist viel zu simpel.
JacquesB
183

Versuchen:

<([^\s]+)(\s[^>]*?)?(?<!/)>

Es ist ähnlich wie deins, aber das letzte >darf nicht nach einem Schrägstrich sein und akzeptiert auch h1.

Kobi
quelle
107
<a href="foo" title="5> 3 "> Ups </a>
Gareth
21
Das ist sehr wahr, und ich habe darüber nachgedacht, aber ich habe angenommen, dass das >Symbol ordnungsgemäß in & gt; maskiert ist.
Kobi
65
>ist in einem Attributwert gültig. In der Serialisierung "Canonical XML" dürfen Sie diese nicht verwenden &gt;. (Was nicht ganz relevant ist, außer um zu betonen, dass >in einem Attribut Wert überhaupt keine ungewöhnliche Sache ist.)
Bobince
5
@Kobi: Was bedeutet das Ausrufezeichen (das Sie am Ende platziert haben) in einem regulären Ausdruck?
Marco Demaio
6
@ Bobince: Bist du sicher? Ich verstehe nicht mehr, so ist auch dieser gültige HTML- <div title="this tag is a <div></div>">hello</div>
Code
179

Sun Tzu, ein alter chinesischer Stratege, General und Philosoph, sagte:

Es heißt, wenn Sie Ihre Feinde kennen und sich selbst kennen, können Sie hundert Schlachten ohne einen einzigen Verlust gewinnen. Wenn Sie nur sich selbst kennen, aber nicht Ihren Gegner, können Sie gewinnen oder verlieren. Wenn Sie weder sich selbst noch Ihren Feind kennen, gefährden Sie sich immer selbst.

In diesem Fall ist Ihr Feind HTML und Sie sind entweder Sie selbst oder Regex. Sie könnten sogar Perl mit unregelmäßigem Regex sein. HTML kennen. Sich selbst kennen.

Ich habe ein Haiku verfasst, das die Natur von HTML beschreibt.

HTML has
complexity exceeding
regular language.

Ich habe auch ein Haiku verfasst, das die Natur von Regex in Perl beschreibt.

The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
Cytinus
quelle
153
<?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 );
    }
}

Ausgabe:

string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"

Definieren Sie einfach die Namen der Elementknoten, die sich selbst schließen, laden Sie die gesamte HTML-Zeichenfolge in eine DOM-Bibliothek, greifen Sie auf alle Elemente zu, durchlaufen Sie sie und filtern Sie diejenigen heraus, die sich nicht selbst schließen, und bearbeiten Sie sie.

Ich bin mir sicher, dass Sie bereits wissen, dass Sie Regex für diesen Zweck nicht verwenden sollten.

meder
quelle
1
Wenn Sie mit echtem XHTML arbeiten, fügen Sie getElementsByTagName an NSund geben Sie den Namespace an.
Meder Omuraliev
148

Ich weiß nicht, wie genau Sie dies benötigen, aber wenn Sie auch .NET verwenden, können Sie dann nicht das HTML Agility Pack verwenden ?

Auszug:

Es ist eine .NET-Codebibliothek, mit der Sie HTML-Dateien "aus dem Web" analysieren können. Der Parser ist sehr tolerant gegenüber "realem" fehlerhaftem HTML.

GONeale
quelle
137

Sie möchten, dass dem ersten >kein a vorangestellt wird /. Einzelheiten dazu finden Sie hier . Es wird als negatives Aussehen bezeichnet.

Eine naive Implementierung davon wird jedoch <bar/></foo>in diesem Beispieldokument übereinstimmen

<foo><bar/></foo>

Können Sie etwas mehr Informationen zu dem Problem geben, das Sie lösen möchten? Durchlaufen Sie Tags programmgesteuert?

Jherico
quelle
1
Ja, das bin ich sicher. Ermitteln Sie alle derzeit geöffneten Tags und vergleichen Sie diese mit den geschlossenen Tags in einem separaten Array. RegEx tut meinem Gehirn weh.
Jeff
122

Das W3C erklärt das Parsen in einer Pseudo-Regexp-Form:
W3C Link

Folgen Sie den var-Links für QName, Sund Attribute, um ein klareres Bild zu erhalten.
Basierend darauf können Sie einen ziemlich guten regulären Ausdruck erstellen, um Dinge wie das Entfernen von Tags zu handhaben.

John-David Dalton
quelle
5
Das ist kein Pseudo-Regexp-Formular, das ist ein EBNF-Formular, wie hier angegeben: XML-Spezifikation, Anhang 6
Rob G
106

Wenn Sie dies für PHP benötigen:

Die PHP-DOM- Funktionen funktionieren nur dann ordnungsgemäß, wenn XML ordnungsgemäß formatiert ist. Egal wie viel besser sie für den Rest der Menschheit sind.

simplehtmldom ist gut, aber ich fand es ein bisschen fehlerhaft und es ist ziemlich speicherlastig [Wird auf großen Seiten abstürzen.]

Ich habe Querypath noch nie verwendet , kann daher seine Nützlichkeit nicht kommentieren.

Ein weiterer Versuch ist mein DOMParser, der sehr ressourcenschonend ist und den ich seit einiger Zeit gerne benutze. Einfach zu lernen und leistungsstark.

Für Python und Java wurden ähnliche Links veröffentlicht.

Für die Downvoter - Ich habe meine Klasse erst geschrieben, als sich herausstellte, dass die XML-Parser der tatsächlichen Verwendung nicht standhalten konnten. Religiöses Downvoting verhindert nur, dass nützliche Antworten veröffentlicht werden - halten Sie die Dinge bitte im Blickfeld der Frage.

SamGoody
quelle
95

Hier ist die Lösung:

<?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]);
?>

Um es gründlich zu testen, habe ich in die Zeichenfolge automatisch schließende Tags eingegeben:

  1. <h />
  2. <br/>
  3. <br>

Ich habe auch Tags eingegeben mit:

  1. ein Attribut
  2. mehr als ein Attribut
  3. Attribute, deren Wert entweder in einfache Anführungszeichen oder in doppelte Anführungszeichen gebunden ist
  4. Attribute, die einfache Anführungszeichen enthalten, wenn das Trennzeichen ein doppeltes Anführungszeichen ist und umgekehrt
  5. "unpretty" -Attribute mit einem Leerzeichen vor dem Symbol "=", danach und sowohl davor als auch danach.

Sollten Sie im obigen Proof of Concept etwas finden, das nicht funktioniert, kann ich den Code analysieren, um meine Fähigkeiten zu verbessern.

<EDIT> Ich habe vergessen, dass die Frage des Benutzers darin bestand, das Parsen von selbstschließenden Tags zu vermeiden. In diesem Fall ist das Muster einfacher und wird folgendermaßen:

$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';

Der Benutzer @ridgerunner hat festgestellt, dass das Muster keine nicht zitierten Attribute oder Attribute ohne Wert zulässt . In diesem Fall bringt uns eine Feinabstimmung das folgende Muster:

$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';

</ EDIT>

Das Muster verstehen

Wenn jemand mehr über das Muster erfahren möchte, gebe ich eine Zeile an:

  1. Der erste Unterausdruck (\ w +) entspricht dem Tag-Namen
  2. Der zweite Unterausdruck enthält das Muster eines Attributs. Es besteht aus:
    1. ein oder mehrere Leerzeichen \ s +
    2. der Name des Attributs (\ w +)
    3. null oder mehr Leerzeichen \ s * (es ist möglich oder nicht, hier Leerzeichen zu lassen)
    4. das Symbol "="
    5. wieder null oder mehr Leerzeichen
    6. Das Trennzeichen des Attributwerts ist ein einfaches oder doppeltes Anführungszeichen ('| "). In dem Muster wird das einfache Anführungszeichen maskiert, da es mit dem PHP-Zeichenfolgenbegrenzer übereinstimmt. Dieser Unterausdruck wird mit den Klammern erfasst, sodass auf ihn verwiesen werden kann Um das Schließen des Attributs erneut zu analysieren, ist dies sehr wichtig.
    7. der Wert des Attributs, der mit fast allem übereinstimmt : (. *?); In dieser speziellen Syntax aktiviert die RegExp-Engine unter Verwendung der gierigen Übereinstimmung (dem Fragezeichen nach dem Sternchen) einen "Look-Ahead" -ähnlichen Operator, der mit allem übereinstimmt, was nicht diesem Unterausdruck folgt
    8. Hier kommt der Spaß: Der \ 4-Teil ist ein Rückreferenzoperator , der sich auf einen zuvor im Muster definierten Unterausdruck bezieht. In diesem Fall beziehe ich mich auf den vierten Unterausdruck, der das erste gefundene Attributtrennzeichen ist
    9. null oder mehr Leerzeichen \ s *
    10. Der Attribut-Unterausdruck endet hier mit der Angabe von null oder mehr möglichen Vorkommen, die durch das Sternchen angegeben werden.
  3. Da ein Tag möglicherweise mit einem Leerzeichen vor dem Symbol ">" endet, werden null oder mehr Leerzeichen mit dem Untermuster \ s * abgeglichen.
  4. Das übereinstimmende Tag kann mit einem einfachen ">" Symbol oder einem möglichen XHTML-Abschluss enden, der den Schrägstrich davor verwendet: (/> |>). Der Schrägstrich wird natürlich maskiert, da er mit dem Trennzeichen für reguläre Ausdrücke übereinstimmt.

Kleiner Tipp: Um diesen Code besser analysieren zu können, muss der generierte Quellcode betrachtet werden, da ich keine HTML-Sonderzeichen angegeben habe.

Emanuele Del Grande
quelle
12
Stimmt nicht mit gültigen Tags überein, deren Attribute keinen Wert haben, d <option selected>. H. Stimmt auch nicht mit gültigen Tags mit nicht zitierten Attributwerten überein, d <p id=10>. H.
Ridgerunner
1
@ridgerunner: Vielen Dank für deinen Kommentar. In diesem Fall muss sich das Muster etwas ändern: $ pattern = '/ <(\ w +) (\ s + (\ w +) (\ s * \ = \ s * (\' | "|) (. *?) \\ 5 \ s *)?) * \ S *> / '; Ich habe es getestet und arbeite bei nicht zitierten Attributen oder Attributen ohne Wert.
Emanuele Del Grande
Wie wäre es mit einem Leerzeichen vor dem Tag-Namen: < a href="http://wtf.org" >Ich bin mir ziemlich sicher, dass es legal ist, aber Sie stimmen nicht damit überein.
Floris
7
NEIN Entschuldigung, Leerzeichen vor einem Tagnamen sind illegal. Abgesehen davon, dass Sie "ziemlich sicher" sind, warum liefern Sie nicht einige Beweise für Ihren Einwand? Hier sind meine, w3.org/TR/xml11/#sec-starttags, die auf XML 1.1 verweisen, und Sie können dasselbe für HTML 4, 5 und XHTML finden, da eine W3C-Validierung auch warnen würde, wenn Sie einen Test durchführen. Wie viele andere bla-bla-Dichter hier erhielt ich bis auf einige hundert Minuspunkte meiner Antworten noch keine intelligente Argumentation, um zu demonstrieren, wo mein Code gemäß den in der Frage angegebenen Vertragsregeln versagt . Ich würde sie nur begrüßen.
Emanuele Del Grande
@ridgerunner natürlich war dein Kommentar intelligent und willkommen.
Emanuele Del Grande
91

Wann immer ich schnell etwas aus einem HTML-Dokument extrahieren muss, verwende ich Tidy, um es in XML zu konvertieren, und verwende dann XPath oder XSLT, um das zu bekommen, was ich brauche. In Ihrem Fall so etwas:

//p/a[@href='foo']
Amal Murali
quelle
89

Ich habe zuvor ein Open-Source-Tool namens HTMLParser verwendet . Es wurde entwickelt, um HTML auf verschiedene Arten zu analysieren und erfüllt den Zweck recht gut. Es kann HTML als unterschiedlichen Treenode analysieren und Sie können seine API einfach verwenden, um Attribute aus dem Knoten abzurufen. Probieren Sie es aus und sehen Sie, ob dies Ihnen helfen kann.

wen
quelle
84

Ich mag es, HTML mit regulären Ausdrücken zu analysieren. Ich versuche nicht, idiotisches HTML zu analysieren, das absichtlich kaputt ist. Dieser Code ist mein Hauptparser (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

Es heißt htmlsplit und teilt den HTML-Code in Zeilen mit einem Tag oder Textblock in jeder Zeile auf. Die Zeilen können dann mit anderen Textwerkzeugen und Skripten wie grep , sed , Perl usw. weiter verarbeitet werden. Ich scherze nicht einmal :) Viel Spaß.

Es ist einfach genug, mein Perl-Skript "Slurp-Everything-First" in ein nettes Streaming-Ding umzuwandeln, wenn Sie riesige Webseiten verarbeiten möchten. Aber es ist nicht wirklich notwendig.

Ich wette, ich werde dafür herabgestimmt.

HTML Split


Entgegen meiner Erwartung erhielt dies einige positive Stimmen, daher werde ich einige bessere reguläre Ausdrücke vorschlagen:

/(<.*?>|[^<]+)\s*/g    # get tags and text
/(\w+)="(.*?)"/g       # get attibutes

Sie sind gut für XML / XHTML.

Mit geringfügigen Abweichungen kann es mit unordentlichem HTML umgehen ... oder zuerst HTML -> XHTML konvertieren.


Der beste Weg, reguläre Ausdrücke zu schreiben, ist der Lex / Yacc- Stil, nicht als undurchsichtige Einzeiler oder kommentierte mehrzeilige Monstrositäten. Das habe ich hier noch nicht gemacht; diese brauchen es kaum.

Sam Watkins
quelle
35
"Ich versuche nicht, idiotisches HTML zu analysieren, das absichtlich kaputt ist." Woher kennt Ihr Code den Unterschied?
Kevin Panko
Nun, es spielt keine Rolle, ob der HTML-Code fehlerhaft ist oder nicht. Das Ding teilt HTML immer noch in Tags und Text auf. Das einzige, was es beschmutzen könnte, ist, wenn Leute nicht entflohene <oder> Zeichen in Text oder Attribute aufnehmen. In der Praxis funktioniert mein kleiner HTML-Splitter gut. Ich brauche keinen riesigen Monstrositätsblock voller Heuristiken. Einfache Lösungen sind nicht jedermanns Sache ...!
Sam Watkins
Ich habe einige einfachere reguläre Ausdrücke zum Extrahieren von Tags, Text und Attributen für XML / XHTML hinzugefügt.
Sam Watkins
(Get Attribute Bug 1) /(\w+)="(.*?)"/setzt doppelte Anführungszeichen voraus. Es werden Werte in einfachen Anführungszeichen fehlen. In HTML-Version 4 und früheren Versionen ist ein nicht zitierter Wert zulässig, wenn es sich um ein einfaches Wort handelt.
David Andersson
(Get Attribute Bug 2) /(\w+)="(.*?)"/stimmt möglicherweise fälschlicherweise mit Text überein, der wie ein Attribut innerhalb eines Attributs aussieht, z <img title="Nope down='up' for aussies" src="..." />. Wenn es global angewendet wird, stimmt es auch mit solchen Dingen in normalem Text oder in HTML-Kommentaren überein.
David Andersson
74

Hier ist ein PHP-basierter Parser , der HTML mit einem gottlosen regulären Ausdruck analysiert. Als Autor dieses Projekts kann ich Ihnen sagen, dass es möglich ist, HTML mit Regex zu analysieren, aber nicht effizient. Wenn Sie eine serverseitige Lösung benötigen (wie ich es für mein wp-Typography WordPress-Plugin getan habe ), funktioniert dies.

Kingjeffrey
quelle
1
htmlawed ist ein weiteres PHP-Projekt, das HTML analysiert, um zu filtern, zu konvertieren usw. Hat einen netten Code, wenn Sie es herausfinden können!
user594694
Nein, Sie können HTML nicht mit Regex analysieren. Aber für einige Teilmengen, es kann funktionieren.
Mirabilos
71

Es gibt einige nette reguläre Ausdrücke zu ersetzen HTML mit BBCode hier . Beachten Sie für alle Neinsager, dass er nicht versucht, HTML vollständig zu analysieren, sondern nur zu bereinigen. Er kann es sich wahrscheinlich leisten, Tags abzutöten, die sein einfacher "Parser" nicht verstehen kann.

Zum Beispiel:

$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;
Kenorb
quelle
15
Tu das nicht. Bitte.
Maletor
68

In Bezug auf die Frage der RegExp-Methoden zum Parsen von (x) HTML lautet die Antwort auf alle, die über einige Grenzen gesprochen haben: Sie sind nicht ausreichend geschult, um die Kraft dieser mächtigen Waffe zu beherrschen , da NIEMAND hier über Rekursion sprach .

Ein RegExp-agnostischer Kollege hat mich über diese Diskussion informiert, die sicherlich nicht die erste im Internet zu diesem alten und heißen Thema ist.

Nachdem ich einige Beiträge gelesen hatte, suchte ich als erstes nach dem "? R" -String in diesem Thread. Die zweite war die Suche nach "Rekursion".
Nein, heilige Kuh, keine Übereinstimmung gefunden.
Da niemand den Hauptmechanismus erwähnte, auf dem ein Parser aufgebaut ist, wurde mir schnell bewusst, dass niemand den Punkt verstand.

Wenn ein (x) HTML-Parser eine Rekursion benötigt, reicht ein RegExp-Parser ohne Rekursion für diesen Zweck nicht aus. Es ist ein einfaches Konstrukt.

Die schwarze Kunst von RegExp ist schwer zu beherrschen . Vielleicht gibt es weitere Möglichkeiten, die wir ausgelassen haben, als wir unsere persönliche Lösung ausprobiert und getestet haben, um das gesamte Web in einer Hand zu erfassen ... Nun, da bin ich mir sicher :)

Hier ist das magische Muster:

$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";

Probier es einfach.
Es ist als PHP-String geschrieben, daher bewirkt der Modifikator "s", dass Klassen Zeilenumbrüche enthalten.
Hier ist ein Beispiel für das PHP-Handbuch, das ich im Januar geschrieben habe: Referenz

(Achten Sie darauf, dass ich in diesem Hinweis den Modifikator "m" falsch verwendet habe. Er sollte gelöscht werden, obwohl er von der RegExp-Engine verworfen wird, da keine ^ oder $ Verankerung verwendet wurde.)

Nun könnten wir über die Grenzen dieser Methode aus einer informierten Sicht sprechen:

  1. Je nach der spezifischen Implementierung der RegExp-Engine kann die Anzahl der analysierten verschachtelten Muster für die Rekursion begrenzt sein , dies hängt jedoch von der verwendeten Sprache ab
  2. Obwohl beschädigtes (x) HTML nicht zu schwerwiegenden Fehlern führt, wird es nicht bereinigt .

Trotzdem ist es nur ein RegExp-Muster, aber es offenbart die Möglichkeit, viele leistungsfähige Implementierungen zu entwickeln.
Ich habe dieses Muster geschrieben, um den rekursiven Abstiegsparser einer in meinem Framework erstellten Template-Engine zu betreiben. Die Leistung ist sowohl in Bezug auf die Ausführungszeiten als auch in Bezug auf die Speichernutzung wirklich großartig (nichts mit anderen Template-Engines zu tun, die dieselbe Syntax verwenden).

Emanuele Del Grande
quelle
35
Ich werde dies in den Bin "Regex, der nicht mehr als Attribute zulässt" setzen. Überprüfen Sie es mit <input value = "is 5> 3?" />
Gareth
68
Wenn Sie so etwas in den Produktionscode einfügen, werden Sie wahrscheinlich vom Betreuer erschossen. Eine Jury würde ihn niemals verurteilen.
Aehiilrs
30
Reguläre Ausdrücke können nicht funktionieren, da sie per Definition nicht rekursiv sind. Durch Hinzufügen eines rekursiven Operators zu regulären Ausdrücken wird eine CFG grundsätzlich nur mit schlechterer Syntax erstellt. Warum nicht etwas verwenden, das in erster Linie rekursiv gestaltet ist, anstatt die Rekursion gewaltsam in etwas einzufügen, das bereits überfüllt ist mit fremder Funktionalität?
Welbog
16
Mein Einwand betrifft nicht die Funktionalität, sondern die investierte Zeit. Das Problem mit RegEx ist, dass Sie zu dem Zeitpunkt, an dem Sie die Cutsey Little One-Liner veröffentlichen, anscheinend etwas effizienter gemacht haben ("Siehe eine Codezeile!"). Und natürlich erwähnt niemand die halbe Stunde (oder 3), die sie mit ihrem Spickzettel verbracht haben und (hoffentlich) jede mögliche Permutation von Eingaben getestet haben. Und wenn Sie all das hinter sich haben, kann der Betreuer, wenn er den Code herausfindet oder validiert, ihn nicht einfach ansehen und feststellen, dass er richtig ist. Sie müssen den Ausdruck zerlegen und ihn im Wesentlichen noch einmal wiederholen ...
Oorang
15
... zu wissen, dass es gut ist. Und das wird auch bei Menschen passieren, die gut mit Regex umgehen können. Und ehrlich gesagt vermute ich, dass die überwiegende Mehrheit der Menschen es nicht gut weiß. Sie nehmen also einen der berüchtigtsten Wartungs-Albträume und kombinieren ihn mit einer Rekursion, die der andere Wartungs-Albtraum ist, und ich denke mir, was ich für mein Projekt wirklich brauche, ist jemand, der etwas weniger klug ist. Das Ziel ist es, Code zu schreiben, den schlechte Programmierer pflegen können, ohne die Codebasis zu beschädigen. Ich weiß, dass es schwierig ist, auf den kleinsten gemeinsamen Nenner zu codieren. Aber exzellentes Talent einzustellen ist schwer, und Sie oft ...
Oorang
62

Wie viele Leute bereits betont haben, ist HTML keine reguläre Sprache, was das Parsen sehr schwierig machen kann. Meine Lösung besteht darin, es mit einem aufgeräumten Programm in eine normale Sprache umzuwandeln und dann einen XML-Parser zu verwenden, um die Ergebnisse zu nutzen. Dafür gibt es viele gute Möglichkeiten. Mein Programm wird mit Java mit der jtidy- Bibliothek geschrieben, um den HTML-Code in XML umzuwandeln, und dann mit Jaxen in xpath in das Ergebnis.

Corey Sanders
quelle
61
<\s*(\w+)[^/>]*>

Die Teile erklärt:

<: Startcharakter

\s*: Es kann Leerzeichen vor dem Tag-Namen haben (hässlich, aber möglich).

(\w+): Tags können Buchstaben und Zahlen enthalten (h1). Nun, \wpasst auch zu '_', aber es tut nicht weh, denke ich. Wenn Sie neugierig sind, verwenden Sie stattdessen ([a-zA-Z0-9] +).

[^/>]*: alles außer >und /bis zum Schließen>

>: schließen >

NICHT VERWANDT

Und für Leute, die reguläre Ausdrücke unterschätzen und sagen, dass sie nur so mächtig sind wie reguläre Sprachen:

a n ba n ba n, das nicht regelmäßig und nicht einmal kontextfrei ist, kann mit abgeglichen werden^(a+)b\1b\1$

Rückreferenz FTW !

Daghan
quelle
@GlitchMr, das war sein Punkt. Moderne reguläre Ausdrücke sind weder technisch regulär noch gibt es einen Grund dafür.
Alanaktion
3
@alanaktion: Die "modernen" regulären Ausdrücke (gelesen: mit Perl-Erweiterungen) können nicht innerhalb übereinstimmen O(MN)(M ist die Länge der regulären Ausdrücke, N ist die Textlänge). Rückreferenzen sind eine der Ursachen dafür. Die Implementierung in awk hat keine Rückreferenzen und stimmt mit der O(MN)Zeit überein .
Konrad Borowski
56

Wenn Sie nur versuchen, diese Tags zu finden (ohne die Ambitionen zu analysieren), versuchen Sie diesen regulären Ausdruck:

/<[^/]*?>/g

Ich habe es in 30 Sekunden geschrieben und hier getestet: http://gskinner.com/RegExr/

Es entspricht den von Ihnen erwähnten Tags, während die von Ihnen angegebenen Typen ignoriert werden.

Lonnie Best
quelle
2
Ich denke du meinst \/>statt \\>.
Justin Morgan
Nein, genau \>das habe ich gemeint. Ich wollte nie den regulären Ausdruck meines ursprünglichen Beitrags bearbeiten.
Lonnie Best
2
Zu Ihrer Information, Sie müssen nicht in spitzen Klammern stehen. Natürlich schadet es nicht, ihnen zu entkommen, aber schauen Sie sich die Verwirrung an, die Sie hätten vermeiden können. ;)
Alan Moore
Ich entkomme manchmal unnötig, wenn ich nicht sicher bin, ob etwas ein besonderer Charakter ist oder nicht. Ich habe die Antwort bearbeitet. es funktioniert genauso, aber prägnanter.
Lonnie Best
Wenn ich das jetzt betrachte, weiß ich nicht, warum ich dachte \/, dass Sie es gemeint haben , da dies genau das Gegenteil der Anforderungen bewirken würde. Vielleicht dachte ich, Sie bieten ein negatives Filtermuster an.
Justin Morgan
54

Mir scheint, Sie versuchen, Tags ohne ein "/" am Ende abzugleichen. Versuche dies:

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
Manixrock
quelle
8
Das funktioniert nicht. Für die Eingabe '<xa = "<b>" /> <y>' sind die Übereinstimmungen x und y, obwohl x beendet ist.
Ceving
51

Es ist richtig, dass es beim Programmieren normalerweise am besten ist, dedizierte Parser und APIs anstelle von regulären Ausdrücken zu verwenden, wenn Sie mit HTML arbeiten, insbesondere wenn die Genauigkeit von größter Bedeutung ist (z. B. wenn Ihre Verarbeitung Sicherheitsauswirkungen haben könnte). Ich schreibe jedoch keiner dogmatischen Ansicht zu, dass XML-artiges Markup niemals mit regulären Ausdrücken verarbeitet werden sollte. Es gibt Fälle, in denen reguläre Ausdrücke ein großartiges Werkzeug für den Job sind, z. B. beim einmaligen Bearbeiten in einem Texteditor, beim Beheben fehlerhafter XML-Dateien oder beim Umgang mit Dateiformaten, die aussehen, aber nicht ganz XML sind. Es gibt einige Probleme, die Sie beachten müssen, aber sie sind nicht unüberwindbar oder sogar unbedingt relevant.

Ein einfacher Regex wie <([^>"']|"[^"]*"|'[^']*')*>ist normalerweise gut genug, in Fällen wie den gerade erwähnten. Alles >in allem ist es eine naive Lösung, aber es erlaubt korrekt nicht codierte Symbole in Attributwerten. Wenn Sie beispielsweise nach einem tableTag suchen , können Sie es als anpassen </?table\b([^>"']|"[^"]*"|'[^']*')*>.

Um einen Eindruck davon zu bekommen, wie ein "fortgeschrittener" HTML-Regex aussehen würde, können Sie im Folgenden das Verhalten des realen Browsers und den HTML5-Parsing-Algorithmus nachvollziehen:

</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)

Das Folgende entspricht einer ziemlich strengen Definition von XML-Tags (obwohl nicht der gesamte Satz von Unicode-Zeichen berücksichtigt wird, die in XML-Namen zulässig sind):

<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>

Zugegeben, diese berücksichtigen nicht den umgebenden Kontext und einige Randfälle, aber selbst solche Dinge könnten behandelt werden, wenn Sie es wirklich wollten (z. B. indem Sie zwischen den Übereinstimmungen eines anderen regulären Ausdrucks suchen).

Verwenden Sie am Ende des Tages das für den Job am besten geeignete Werkzeug, auch wenn es sich bei diesem Werkzeug zufällig um einen regulären Ausdruck handelt.

Slevithan
quelle
49

Obwohl es nicht geeignet und effektiv ist, reguläre Ausdrücke für diesen Zweck zu verwenden, bieten reguläre Ausdrücke manchmal schnelle Lösungen für einfache Übereinstimmungsprobleme, und meiner Ansicht nach ist es nicht so schrecklich, reguläre Ausdrücke für triviale Arbeiten zu verwenden.

Es gibt einen endgültigen Blog-Beitrag über übereinstimmende innerste HTML-Elemente, die von Steven Levithan geschrieben wurden.

Emre Yazici
quelle