Es scheint, dass jede Frage zum Stackoverflow, bei der der Fragesteller Regex verwendet, um Informationen aus HTML abzurufen, unweigerlich eine "Antwort" hat, die besagt, dass Regex nicht zum Parsen von HTML verwendet werden soll.
Warum nicht? Ich bin mir bewusst, dass es "echte" HTML-Parser wie Beautiful Soup gibt , und ich bin sicher, dass sie leistungsstark und nützlich sind, aber wenn Sie nur etwas Einfaches, Schnelles oder Schmutziges tun, warum dann? sich die Mühe machen, etwas so Kompliziertes zu verwenden, wenn ein paar Regex-Anweisungen gut funktionieren?
Gibt es darüber hinaus nur etwas Grundlegendes, das ich über Regex nicht verstehe, was sie zu einer schlechten Wahl für das Parsen im Allgemeinen macht?
regex
html-parsing
ntownsend
quelle
quelle
Antworten:
Eine vollständige HTML-Analyse ist mit regulären Ausdrücken nicht möglich, da dies davon abhängt, ob das öffnende und das schließende Tag übereinstimmen, was mit regulären Ausdrücken nicht möglich ist.
Reguläre Ausdrücke können nur mit regulären Sprachen übereinstimmen , aber HTML ist eine kontextfreie Sprache und keine reguläre Sprache (Wie @StefanPochmann betonte, sind reguläre Sprachen auch kontextfrei, sodass kontextfrei nicht unbedingt nicht regulär bedeutet). Das einzige, was Sie mit regulären Ausdrücken in HTML tun können, sind Heuristiken, aber das funktioniert nicht unter allen Bedingungen. Es sollte möglich sein, eine HTML-Datei zu präsentieren, die von einem regulären Ausdruck falsch abgeglichen wird.
quelle
Für schnelles und schmutziges Regexp ist das in Ordnung. Das Grundlegende zu wissen ist jedoch, dass es unmöglich ist , einen regulären Ausdruck zu erstellen, der HTML korrekt analysiert.
Der Grund dafür ist, dass reguläre Ausdrücke keine arbitarisch verschachtelten Ausdrücke verarbeiten können. Siehe Können reguläre Ausdrücke verwendet werden, um verschachtelte Muster abzugleichen?
quelle
(Von http://htmlparsing.com/regexes )
Angenommen, Sie haben eine HTML-Datei, in der Sie versuchen, URLs aus <img> -Tags zu extrahieren.
Sie schreiben also einen regulären Ausdruck in Perl:
In diesem Fall
$url
wird in der Tat enthaltenhttp://example.com/whatever.jpg
. Aber was passiert, wenn Sie HTML wie folgt erhalten:oder
oder
oder
oder Sie bekommen falsch positive Ergebnisse von
Es sieht so einfach aus, und es mag für eine einzelne, unveränderliche Datei einfach sein, aber für alles, was Sie mit beliebigen HTML-Daten tun werden, sind reguläre Ausdrücke nur ein Rezept für zukünftigen Herzschmerz.
quelle
Zwei schnelle Gründe:
In Bezug auf die Eignung von Regexen für das Parsen im Allgemeinen: Sie sind nicht geeignet. Haben Sie jemals die Art von Regexes gesehen, die Sie benötigen würden, um die meisten Sprachen zu analysieren?
quelle
In Bezug auf das Parsen können reguläre Ausdrücke in der Phase der "lexikalischen Analyse" (Lexer) nützlich sein, in der die Eingabe in Token unterteilt wird. In der eigentlichen Phase "Erstellen eines Analysebaums" ist dies weniger nützlich.
Für einen HTML-Parser würde ich erwarten, dass er nur wohlgeformtes HTML akzeptiert, und dies erfordert Funktionen, die außerhalb der Möglichkeiten eines regulären Ausdrucks liegen (sie können nicht "zählen" und sicherstellen, dass eine bestimmte Anzahl von Eröffnungselementen durch dieselbe Anzahl ausgeglichen wird von schließenden Elementen).
quelle
Da es viele Möglichkeiten gibt, HTML zu "vermasseln", die Browser auf ziemlich liberale Weise behandeln, wäre es jedoch ziemlich aufwändig, das liberale Verhalten des Browsers zu reproduzieren, um alle Fälle mit regulären Ausdrücken abzudecken, sodass Ihre Regex bei bestimmten Sondervorgängen unvermeidlich fehlschlägt Fälle, und das würde möglicherweise ernsthafte Sicherheitslücken in Ihrem System führen.
quelle
Das Problem ist, dass die meisten Benutzer, die eine Frage stellen, die mit HTML und Regex zu tun hat, dies tun, weil sie keinen eigenen Regex finden, der funktioniert. Dann muss man sich überlegen, ob bei Verwendung eines DOM- oder SAX-Parsers oder ähnlichem alles einfacher wäre. Sie sind für die Arbeit mit XML-ähnlichen Dokumentstrukturen optimiert und konstruiert.
Sicher, es gibt Probleme, die mit regulären Ausdrücken leicht gelöst werden können. Aber der Schwerpunkt liegt auf leicht .
Wenn Sie nur alle URLs finden möchten, die so aussehen, als
http://.../
wären Sie mit regulären Ausdrücken einverstanden. Wenn Sie jedoch alle URLs finden möchten, die sich in einem a-Element mit der Klasse 'mylink' befinden, verwenden Sie wahrscheinlich besser einen geeigneten Parser.quelle
Reguläre Ausdrücke wurden nicht für die Verarbeitung einer verschachtelten Tag-Struktur entwickelt, und es ist bestenfalls kompliziert (im schlimmsten Fall unmöglich), alle möglichen Randfälle zu behandeln, die Sie mit echtem HTML erhalten.
quelle
Ich glaube, dass die Antwort in der Berechnungstheorie liegt. Damit eine Sprache mit Regex analysiert werden kann, muss sie per Definition "normal" sein ( Link ). HTML ist keine reguläre Sprache, da es eine Reihe von Kriterien für eine reguläre Sprache nicht erfüllt (viel zu tun mit den vielen Verschachtelungsebenen, die HTML-Code innewohnt). Wenn Sie sich für die Berechnungstheorie interessieren, würde ich dieses Buch empfehlen .
quelle
Dieser Ausdruck ruft Attribute aus HTML-Elementen ab. Es unterstützt:
(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)
Schau es dir an . Es funktioniert besser mit den "gisx" -Flaggen als in der Demo.
quelle
<script>
Tag.HTML / XML ist in Markup und Inhalt unterteilt. Regex ist nur nützlich, wenn Sie eine lexikalische Tag-Analyse durchführen. Ich denke, Sie könnten den Inhalt ableiten. Es wäre eine gute Wahl für einen SAX-Parser. Tags und Inhalte können an eine benutzerdefinierte Funktion gesendet werden, mit der das Verschachteln / Schließen von Elementen verfolgt werden kann.
Das Parsen der Tags kann mit Regex erfolgen und zum Entfernen von Tags aus einem Dokument verwendet werden.
In jahrelangen Tests habe ich das Geheimnis gefunden, wie Browser gut und schlecht geformte Tags analysieren.
Die normalen Elemente werden mit dieser Form analysiert:
Der Kern dieser Tags verwendet diesen regulären Ausdruck
Sie werden dies
[^>]?
als eine der Alternativen bemerken . Dies entspricht unausgeglichenen Anführungszeichen von schlecht geformten Tags.Es ist auch die Wurzel aller Übel für reguläre Ausdrücke. Die Art und Weise, wie es verwendet wird, löst einen Bump-Along aus, um den gierigen, mit Must-Match quantifizierten Container zu befriedigen.
Bei passiver Verwendung gibt es kein Problem. Wenn Sie jedoch eine Übereinstimmung erzwingen , indem Sie sie mit einem gewünschten Attribut / Wert-Paar durchsetzen und keinen ausreichenden Schutz vor Rückverfolgung bieten, ist dies ein außer Kontrolle geratener Albtraum.
Dies ist die allgemeine Form für einfache alte Tags. Beachten Sie die
[\w:]
Darstellung des Tag-Namens? In Wirklichkeit sind die legalen Zeichen, die den Tag-Namen darstellen, eine unglaubliche Liste von Unicode-Zeichen.Im weiteren Verlauf sehen wir auch, dass Sie einfach nicht nach einem bestimmten Tag suchen können, ohne ALLE Tags zu analysieren . Ich meine, Sie könnten, aber es müsste eine Kombination von Verben wie (* SKIP) (* FAIL) verwendet werden, aber dennoch müssen alle Tags analysiert werden.
Der Grund dafür ist, dass die Tag-Syntax möglicherweise in anderen Tags usw. verborgen ist.
Um alle Tags passiv zu analysieren, wird ein regulärer Ausdruck wie der folgende benötigt. Dieser besondere passt auch zu unsichtbaren Inhalten .
Wenn neues HTML oder XML oder andere neue Konstrukte entwickeln, fügen Sie es einfach als eine der Alternativen hinzu.
Hinweis zur Webseite - Ich habe noch nie eine Webseite (oder xhtml / xml) gesehen, mit der dies
Probleme hatte. Wenn Sie einen finden, lassen Sie es mich wissen.
Leistungshinweis - Es geht schnell. Dies ist der schnellste Tag-Parser, den ich gesehen habe
(es kann schneller sein, wer weiß).
Ich habe mehrere spezifische Versionen. Es eignet sich auch hervorragend als Schaber
(wenn Sie ein praktischer Typ sind).
Komplette rohe Regex
<(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\s+(?>"[\S\s]*?"|'[\S\s]*?'|(?:(?!/>)[^>])?)+)?\s*>)[\S\s]*?</\1\s*(?=>))|(?:/?[\w:]+\s*/?)|(?:[\w:]+\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)+\s*/?)|\?[\S\s]*?\?|(?:!(?:(?:DOCTYPE[\S\s]*?)|(?:\[CDATA\[[\S\s]*?\]\])|(?:--[\S\s]*?--)|(?:ATTLIST[\S\s]*?)|(?:ENTITY[\S\s]*?)|(?:ELEMENT[\S\s]*?))))>
Formatierter Look
quelle
"Es kommt aber darauf an". Es ist wahr, dass reguläre Ausdrücke HTML aus allen hier angegebenen Gründen nicht mit wahrer Genauigkeit analysieren können und können. Wenn die Konsequenzen eines Fehlers (z. B. das Nichthandhaben verschachtelter Tags) jedoch gering sind und Regexes in Ihrer Umgebung sehr praktisch sind (z. B. wenn Sie Perl hacken), fahren Sie fort.
Angenommen, Sie analysieren Webseiten, die auf Ihre Website verweisen - vielleicht haben Sie sie mit einer Google-Linksuche gefunden - und möchten einen schnellen Überblick über den Kontext Ihres Links erhalten. Sie versuchen, einen kleinen Bericht zu erstellen, der Sie möglicherweise auf das Verknüpfen von Spam aufmerksam macht.
In diesem Fall ist es keine große Sache, einige der Dokumente falsch zu analysieren. Niemand außer Ihnen wird die Fehler sehen, und wenn Sie sehr viel Glück haben, gibt es nur wenige, die Sie individuell nachverfolgen können.
Ich denke, ich sage, es ist ein Kompromiss. Manchmal ist die Implementierung oder Verwendung eines korrekten Parsers - so einfach das auch sein mag - die Mühe nicht wert, wenn die Genauigkeit nicht kritisch ist.
Sei einfach vorsichtig mit deinen Annahmen. Ich kann mir einige Möglichkeiten vorstellen, wie die Regexp-Verknüpfung nach hinten losgehen kann, wenn Sie versuchen, etwas zu analysieren, das beispielsweise öffentlich gezeigt wird.
quelle
Es gibt definitiv Fälle, in denen die Verwendung eines regulären Ausdrucks zum Parsen einiger Informationen aus HTML der richtige Weg ist - dies hängt stark von der jeweiligen Situation ab.
Der obige Konsens ist, dass es im Allgemeinen eine schlechte Idee ist. Wenn die HTML-Struktur jedoch bekannt ist (und sich wahrscheinlich nicht ändert), ist dies immer noch ein gültiger Ansatz.
quelle
Beachten Sie, dass HTML selbst zwar nicht regelmäßig ist, Teile einer Seite, die Sie sich ansehen, jedoch möglicherweise regelmäßig sind.
Beispielsweise ist es ein Fehler
<form>
, wenn Tags verschachtelt werden. Wenn die Webseite korrekt funktioniert, ist<form>
es völlig sinnvoll , einen regulären Ausdruck zu verwenden, um eine zu erfassen.Ich habe kürzlich Web-Scraping nur mit Selen und regulären Ausdrücken durchgeführt. Ich habe mit ihm weg , weil die Daten , die ich in einem setzen wollte
<form>
, und legten in einem einfachen Tabellenformat (so ich zählen konnte sogar<table>
,<tr>
und<td>
nicht verschachtelt zu sein - das ist eigentlich sehr ungewöhnlich ist). In gewissem Maße waren reguläre Ausdrücke sogar fast notwendig, da ein Teil der Struktur, auf die ich zugreifen musste, durch Kommentare begrenzt war. (Schöne Suppe kann Ihnen Kommentare geben, aber es wäre schwierig gewesen, mit Schöne Suppe zu greifen<!-- BEGIN -->
und zu<!-- END -->
blockieren.)Wenn ich mich jedoch um verschachtelte Tabellen kümmern müsste, hätte mein Ansatz einfach nicht funktioniert! Ich hätte auf Beautiful Soup zurückgreifen müssen. Selbst dann können Sie jedoch manchmal einen regulären Ausdruck verwenden, um den benötigten Block zu erfassen und von dort aus einen Drilldown durchzuführen.
quelle
Tatsächlich ist HTML-Parsing mit Regex in PHP durchaus möglich. Sie müssen nur die gesamte Zeichenfolge rückwärts analysieren
strrpos
, um<
den regulären Ausdruck von dort aus zu finden und zu wiederholen. Verwenden Sie dabei jedes Mal ungreedy-Bezeichner, um über verschachtelte Tags hinwegzukommen. Nicht schick und furchtbar langsam bei großen Dingen, aber ich habe es für meinen persönlichen Vorlageneditor für meine Website verwendet. Ich habe nicht wirklich HTML analysiert, sondern ein paar benutzerdefinierte Tags, die ich zum Abfragen von Datenbankeinträgen erstellt habe, um Datentabellen anzuzeigen (mein<#if()>
Tag könnte auf diese Weise spezielle Einträge hervorheben). Ich war nicht bereit, hier und da einen XML-Parser für nur ein paar selbst erstellte Tags (mit sehr Nicht-XML-Daten) zu verwenden.Obwohl diese Frage erheblich tot ist, wird sie dennoch in einer Google-Suche angezeigt. Ich las es und dachte, "Herausforderung angenommen" und beendete die Korrektur meines einfachen Codes, ohne alles ersetzen zu müssen. Beschlossen, jedem, der nach einem ähnlichen Grund sucht, eine andere Meinung zu geben. Auch die letzte Antwort wurde vor 4 Stunden gepostet, daher ist dies immer noch ein heißes Thema.
quelle
<tag >
) Haben Sie über auskommentierte schließende Tags nachgedacht? (ZB<tag> <!-- </tag> -->
) Haben Sie über CDATA nachgedacht? Haben Sie inkonsistente Fall-Tags in Betracht gezogen? (ZB<Tag> </tAG>
) finden Sie diese auch?Auch dafür habe ich mich an einer Regex versucht. Dies ist vor allem nützlich, um Inhaltsblöcke zu finden, die mit dem nächsten HTML-Tag gepaart sind, und es wird nicht nach passenden Close-Tags gesucht, aber es werden Close-Tags gefunden. Rollen Sie einen Stapel in Ihrer eigenen Sprache, um diese zu überprüfen.
Mit 'sx'-Optionen verwenden. 'g' auch, wenn Sie Glück haben:
Dieser ist für Python konzipiert (er funktioniert möglicherweise für andere Sprachen, hat ihn noch nicht ausprobiert, verwendet positive Lookaheads, negative Lookbehinds und benannte Rückreferenzen). Unterstützt:
<div ...>
</div>
<!-- ... -->
<![CDATA[ ... ]]>
<div .../>
<input checked>
<div style='...'>
<div style="...">
<a title='John\'s Story'>
(das ist kein wirklich gültiger HTML-Code, aber ich bin ein netter Kerl)
<a href = '...'>
Es ist auch ziemlich gut, keine fehlerhaften Tags auszulösen, beispielsweise wenn Sie ein
<
oder vergessen>
.Wenn Ihre Regex-Variante wiederholte benannte Captures unterstützt, sind Sie golden, Python
re
jedoch nicht (ich weiß, dass Regex dies tut, aber ich muss Vanille-Python verwenden). Folgendes erhalten Sie:content
- Der gesamte Inhalt bis zum nächsten Tag. Sie könnten das weglassen.markup
- Das gesamte Tag mit allem darin.comment
- Wenn es sich um einen Kommentar handelt, wird der Kommentarinhalt angezeigt.cdata
- Wenn es ein ist<![CDATA[...]]>
, der CDATA-Inhalt.close_tag
- Wenn es sich um ein Close-Tag (</div>
) handelt, den Tag-Namen.tag
- Wenn es sich um ein offenes Tag (<div>
) handelt, der Tag-Name.attributes
- Alle Attribute innerhalb des Tags. Verwenden Sie diese Option, um alle Attribute abzurufen, wenn Sie keine wiederholten Gruppen erhalten.attribute
- Wiederholt jedes Attribut.attribute_name
- Wiederholt jeden Attributnamen.attribute_value
- Wiederholt jeden Attributwert. Dies schließt die Anführungszeichen ein, wenn es zitiert wurde.is_self_closing
- Dies ist,/
wenn es sich um ein selbstschließendes Tag handelt, sonst nichts._q
und_v
- diese ignorieren; Sie werden intern für Rückreferenzen verwendet.Wenn Ihre Regex-Engine keine wiederholten benannten Captures unterstützt, wird ein Abschnitt aufgerufen, in dem Sie jedes Attribut abrufen können. Führen Sie einfach , dass regex auf die
attributes
Gruppe jeweils zu bekommenattribute
,attribute_name
undattribute_value
aus ihm heraus.Demo hier: https://regex101.com/r/mH8jSu/11
quelle
Reguläre Ausdrücke sind für eine solche Sprache wie HTML nicht leistungsfähig genug. Sicher, es gibt einige Beispiele, in denen Sie reguläre Ausdrücke verwenden können. Im Allgemeinen ist es jedoch nicht zum Parsen geeignet.
quelle
Weißt du ... es gibt eine Menge Mentalität von dir, die es NICHT kann und ich denke, dass jeder auf beiden Seiten des Zauns richtig und falsch ist. Sie KÖNNEN es tun, aber es erfordert etwas mehr Verarbeitung, als nur einen regulären Ausdruck dagegen auszuführen. Nehmen Sie dies (ich habe es innerhalb einer Stunde geschrieben) als Beispiel. Es wird davon ausgegangen, dass der HTML-Code vollständig gültig ist. Abhängig von der Sprache, in der Sie den oben genannten regulären Ausdruck anwenden, können Sie den HTML-Code jedoch korrigieren, um sicherzustellen, dass er erfolgreich ist. Entfernen Sie beispielsweise schließende Tags, die nicht vorhanden sein sollen:
</img>
Zum Beispiel. Fügen Sie dann den schließenden einzelnen HTML-Schrägstrich zu Elementen hinzu, denen diese fehlen usw.Ich würde dies im Zusammenhang mit dem Schreiben einer Bibliothek verwenden, mit der ich beispielsweise HTML-Elemente abrufen kann, die denen von JavaScript
[x].getElementsByTagName()
ähneln. Ich würde einfach die Funktionalität, die ich im Abschnitt DEFINE der Regex geschrieben habe, zusammenfügen und sie verwenden, um nacheinander in einen Baum von Elementen zu treten.Wird dies die endgültige 100% ige Antwort für die Validierung von HTML sein? Nein, aber es ist ein Anfang und mit etwas mehr Arbeit kann es getan werden. Der Versuch, dies innerhalb einer Regex-Ausführung zu tun, ist jedoch weder praktisch noch effizient.
quelle