Oh ja, Sie können Regexes verwenden, um HTML zu analysieren!
Für die Aufgabe, die Sie versuchen, sind Regexes vollkommen in Ordnung!
Es ist wahr, dass die meisten Leute die Schwierigkeit, HTML mit regulären Ausdrücken zu analysieren, unterschätzen und dies daher schlecht tun.
Dies ist jedoch kein grundlegender Fehler im Zusammenhang mit der Computertheorie. Diese Albernheit wird hier viel nachgeahmt , aber glauben Sie ihnen nicht.
Obwohl dies sicherlich möglich ist (dieses Posting dient als Existenzbeweis für diese unbestreitbare Tatsache), heißt das nicht, dass es so sein sollte.
Sie müssen selbst entscheiden, ob Sie in der Lage sind, aus regulären Ausdrücken einen dedizierten, speziellen HTML-Parser zu schreiben. Die meisten Menschen sind es nicht.
Aber ich bin es. ☻
Allgemeine Regex-basierte HTML-Parsing-Lösungen
Zuerst werde ich zeigen, wie einfach es ist, beliebigen HTML-Code mit regulären Ausdrücken zu analysieren . Das vollständige Programm finden Sie am Ende dieses Beitrags, aber das Herzstück des Parsers ist:
for (;;) {
given ($html) {
last when (pos || 0) >= length;
printf "\@%d=", (pos || 0);
print "doctype " when / \G (?&doctype) $RX_SUBS /xgc;
print "cdata " when / \G (?&cdata) $RX_SUBS /xgc;
print "xml " when / \G (?&xml) $RX_SUBS /xgc;
print "xhook " when / \G (?&xhook) $RX_SUBS /xgc;
print "script " when / \G (?&script) $RX_SUBS /xgc;
print "style " when / \G (?&style) $RX_SUBS /xgc;
print "comment " when / \G (?&comment) $RX_SUBS /xgc;
print "tag " when / \G (?&tag) $RX_SUBS /xgc;
print "untag " when / \G (?&untag) $RX_SUBS /xgc;
print "nasty " when / \G (?&nasty) $RX_SUBS /xgc;
print "text " when / \G (?&nontag) $RX_SUBS /xgc;
default {
die "UNCLASSIFIED: " .
substr($_, pos || 0, (length > 65) ? 65 : length);
}
}
}
Sehen Sie, wie einfach das zu lesen ist?
Wie geschrieben, identifiziert es jedes Stück HTML und sagt, wo es dieses Stück gefunden hat. Sie können es leicht modifizieren, um mit einem bestimmten Stücktyp oder für bestimmte Typen als diese zu tun, was Sie wollen.
Ich habe keine fehlgeschlagenen Testfälle (links :): Ich habe diesen Code erfolgreich auf mehr als 100.000 HTML-Dateien ausgeführt - jede einzelne konnte ich schnell und einfach in die Hände bekommen. Darüber hinaus habe ich es auch für Dateien ausgeführt, die speziell dafür entwickelt wurden, naive Parser zu brechen.
Dies ist kein naiver Parser.
Oh, ich bin sicher, dass es nicht perfekt ist, aber ich habe es noch nicht geschafft, es zu brechen. Ich denke, selbst wenn etwas passieren würde, wäre das Update aufgrund der klaren Struktur des Programms leicht zu integrieren. Sogar Regex-schwere Programme sollten strukturiert sein.
Lassen Sie mich nun, da dies nicht möglich ist, auf die Frage des OP eingehen.
Demo zur Lösung der Aufgabe des OP mithilfe von Regexes
Das kleine html_input_rx
Programm, das ich unten einbinde, erzeugt die folgende Ausgabe, sodass Sie sehen können, dass das Parsen von HTML mit regulären Ausdrücken für das, was Sie tun möchten, einwandfrei funktioniert:
% html_input_rx Amazon.com-_Online_Shopping_for_Electronics,_Apparel,_Computers,_Books,_DVDs_\&_more.htm
input tag #1 at character 9955:
class => "searchSelect"
id => "twotabsearchtextbox"
name => "field-keywords"
size => "50"
style => "width:100%; background-color: #FFF;"
title => "Search for"
type => "text"
value => ""
input tag #2 at character 10335:
alt => "Go"
src => "http://g-ecx.images-amazon.com/images/G/01/x-locale/common/transparent-pixel._V192234675_.gif"
type => "image"
Analysieren Sie Eingabe-Tags, siehe Keine bösen Eingaben
Hier ist die Quelle für das Programm, das die obige Ausgabe erzeugt hat.
#!/usr/bin/env perl
#
# html_input_rx - pull out all <input> tags from (X)HTML src
# via simple regex processing
#
# Tom Christiansen <tchrist@perl.com>
# Sat Nov 20 10:17:31 MST 2010
#
################################################################
use 5.012;
use strict;
use autodie;
use warnings FATAL => "all";
use subs qw{
see_no_evil
parse_input_tags
input descape dequote
load_patterns
};
use open ":std",
IN => ":bytes",
OUT => ":utf8";
use Encode qw< encode decode >;
###########################################################
parse_input_tags
see_no_evil
input
###########################################################
until eof(); sub parse_input_tags {
my $_ = shift();
our($Input_Tag_Rx, $Pull_Attr_Rx);
my $count = 0;
while (/$Input_Tag_Rx/pig) {
my $input_tag = $+{TAG};
my $place = pos() - length ${^MATCH};
printf "input tag #%d at character %d:\n", ++$count, $place;
my %attr = ();
while ($input_tag =~ /$Pull_Attr_Rx/g) {
my ($name, $value) = @+{ qw< NAME VALUE > };
$value = dequote($value);
if (exists $attr{$name}) {
printf "Discarding dup attr value '%s' on %s attr\n",
$attr{$name} // "<undef>", $name;
}
$attr{$name} = $value;
}
for my $name (sort keys %attr) {
printf " %10s => ", $name;
my $value = descape $attr{$name};
my @Q; given ($value) {
@Q = qw[ " " ] when !/'/ && !/"/;
@Q = qw[ " " ] when /'/ && !/"/;
@Q = qw[ ' ' ] when !/'/ && /"/;
@Q = qw[ q( ) ] when /'/ && /"/;
default { die "NOTREACHED" }
}
say $Q[0], $value, $Q[1];
}
print "\n";
}
}
sub dequote {
my $_ = $_[0];
s{
(?<quote> ["'] )
(?<BODY>
(?s: (?! \k<quote> ) . ) *
)
\k<quote>
}{$+{BODY}}six;
return $_;
}
sub descape {
my $string = $_[0];
for my $_ ($string) {
s{
(?<! % )
% ( \p{Hex_Digit} {2} )
}{
chr hex $1;
}gsex;
s{
& \043
( [0-9]+ )
(?: ;
| (?= [^0-9] )
)
}{
chr $1;
}gsex;
s{
& \043 x
( \p{ASCII_HexDigit} + )
(?: ;
| (?= \P{ASCII_HexDigit} )
)
}{
chr hex $1;
}gsex;
}
return $string;
}
sub input {
our ($RX_SUBS, $Meta_Tag_Rx);
my $_ = do { local $/; <> };
my $encoding = "iso-8859-1"; # web default; wish we had the HTTP headers :(
while (/$Meta_Tag_Rx/gi) {
my $meta = $+{META};
next unless $meta =~ m{ $RX_SUBS
(?= http-equiv )
(?&name)
(?&equals)
(?= (?"e)? content-type )
(?&value)
}six;
next unless $meta =~ m{ $RX_SUBS
(?= content ) (?&name)
(?&equals)
(?<CONTENT> (?&value) )
}six;
next unless $+{CONTENT} =~ m{ $RX_SUBS
(?= charset ) (?&name)
(?&equals)
(?<CHARSET> (?&value) )
}six;
if (lc $encoding ne lc $+{CHARSET}) {
say "[RESETTING ENCODING $encoding => $+{CHARSET}]";
$encoding = $+{CHARSET};
}
}
return decode($encoding, $_);
}
sub see_no_evil {
my $_ = shift();
s{ <! DOCTYPE .*? > }{}sx;
s{ <! \[ CDATA \[ .*? \]\] > }{}gsx;
s{ <script> .*? </script> }{}gsix;
s{ <!-- .*? --> }{}gsx;
return $_;
}
sub load_patterns {
our $RX_SUBS = qr{ (?(DEFINE)
(?<nv_pair> (?&name) (?&equals) (?&value) )
(?<name> \b (?= \pL ) [\w\-] + (?<= \pL ) \b )
(?<equals> (?&might_white) = (?&might_white) )
(?<value> (?"ed_value) | (?&unquoted_value) )
(?<unwhite_chunk> (?: (?! > ) \S ) + )
(?<unquoted_value> [\w\-] * )
(?<might_white> \s * )
(?<quoted_value>
(?<quote> ["'] )
(?: (?! \k<quote> ) . ) *
\k<quote>
)
(?<start_tag> < (?&might_white) )
(?<end_tag>
(?&might_white)
(?: (?&html_end_tag)
| (?&xhtml_end_tag)
)
)
(?<html_end_tag> > )
(?<xhtml_end_tag> / > )
) }six;
our $Meta_Tag_Rx = qr{ $RX_SUBS
(?<META>
(?&start_tag) meta \b
(?:
(?&might_white) (?&nv_pair)
) +
(?&end_tag)
)
}six;
our $Pull_Attr_Rx = qr{ $RX_SUBS
(?<NAME> (?&name) )
(?&equals)
(?<VALUE> (?&value) )
}six;
our $Input_Tag_Rx = qr{ $RX_SUBS
(?<TAG> (?&input_tag) )
(?(DEFINE)
(?<input_tag>
(?&start_tag)
input
(?&might_white)
(?&attributes)
(?&might_white)
(?&end_tag)
)
(?<attributes>
(?:
(?&might_white)
(?&one_attribute)
) *
)
(?<one_attribute>
\b
(?&legal_attribute)
(?&might_white) = (?&might_white)
(?:
(?"ed_value)
| (?&unquoted_value)
)
)
(?<legal_attribute>
(?: (?&optional_attribute)
| (?&standard_attribute)
| (?&event_attribute)
# for LEGAL parse only, comment out next line
| (?&illegal_attribute)
)
)
(?<illegal_attribute> (?&name) )
(?<required_attribute> (?#no required attributes) )
(?<optional_attribute>
(?&permitted_attribute)
| (?&deprecated_attribute)
)
# NB: The white space in string literals
# below DOES NOT COUNT! It's just
# there for legibility.
(?<permitted_attribute>
accept
| alt
| bottom
| check box
| checked
| disabled
| file
| hidden
| image
| max length
| middle
| name
| password
| radio
| read only
| reset
| right
| size
| src
| submit
| text
| top
| type
| value
)
(?<deprecated_attribute>
align
)
(?<standard_attribute>
access key
| class
| dir
| ltr
| id
| lang
| style
| tab index
| title
| xml:lang
)
(?<event_attribute>
on blur
| on change
| on click
| on dbl click
| on focus
| on mouse down
| on mouse move
| on mouse out
| on mouse over
| on mouse up
| on key down
| on key press
| on key up
| on select
)
)
}six;
}
UNITCHECK {
load_patterns();
}
END {
close(STDOUT)
|| die "can't close stdout: $!";
}
Los geht's! Nichts dazu! :) :)
Nur Sie können beurteilen, ob Ihre Fähigkeiten mit regulären Ausdrücken einer bestimmten Analyseaufgabe gewachsen sind. Jeder hat ein anderes Können und jede neue Aufgabe ist anders. Für Jobs, bei denen Sie einen genau definierten Eingabesatz haben, sind reguläre Ausdrücke offensichtlich die richtige Wahl, da es trivial ist, einige zusammenzustellen, wenn Sie eine eingeschränkte Teilmenge von HTML haben. Sogar Regex-Anfänger sollten diese Jobs mit Regexes erledigen. Alles andere ist übertrieben.
Jedoch , sobald die HTML weniger genagelt beginnen immer, wenn es in einer Weise zu verzweigen beginnt kann man nicht vorhersagen , aber die sind vollkommen legal, wenn Sie mehr verschiedene Arten von Dingen oder mit komplexeren Abhängigkeiten entsprechen haben, werden Sie irgendwann einen Punkt erreichen , wo Sie müssen härter arbeiten, um eine Lösung zu erzielen, die reguläre Ausdrücke verwendet, als wenn Sie eine Parsing-Klasse verwenden müssten. Wo dieser Break-Even-Punkt liegt, hängt wiederum von Ihrem eigenen Komfortniveau mit Regexen ab.
Also was soll ich tun?
Ich werde Ihnen nicht sagen, was Sie tun müssen oder was Sie nicht tun können. Ich denke das ist falsch. Ich möchte Ihnen nur die Möglichkeiten vorstellen, Ihre Augen ein wenig öffnen. Sie können wählen, was Sie tun möchten und wie Sie es tun möchten. Es gibt keine Absoluten - und niemand kennt Ihre eigene Situation so gut wie Sie selbst. Wenn etwas zu viel Arbeit zu sein scheint, ist es es vielleicht. Das Programmieren sollte Spaß machen , wissen Sie. Wenn nicht, machen Sie es möglicherweise falsch.
Man kann mein html_input_rx
Programm auf eine beliebige Anzahl gültiger Arten betrachten. Eine davon ist, dass Sie tatsächlich HTML mit regulären Ausdrücken analysieren können . Aber ein anderer ist, dass es viel, viel, viel schwieriger ist, als fast jeder jemals denkt, dass es so ist. Dies kann leicht zu dem Schluss führen, dass mein Programm ein Beweis dafür ist, was Sie nicht tun sollten, weil es wirklich zu schwer ist.
Dem werde ich nicht widersprechen. Wenn alles, was ich in meinem Programm mache, nach einem Studium für Sie keinen Sinn ergibt, sollten Sie sicherlich nicht versuchen, Regexes für diese Art von Aufgabe zu verwenden. Für spezifisches HTML sind reguläre Ausdrücke großartig, aber für generisches HTML sind sie gleichbedeutend mit Wahnsinn. Ich verwende ständig Parsing-Klassen, insbesondere wenn es sich um HTML handelt, das ich nicht selbst generiert habe.
Regexes optimal für kleine HTML-Analyseprobleme, pessimal für große
Selbst wenn mein Programm als Beispiel dafür dient, warum Sie keine regulären Ausdrücke zum Parsen von allgemeinem HTML verwenden sollten - was in Ordnung ist, weil ich es irgendwie so gemeint habe -, sollte es dennoch ein Augenöffner sein, damit mehr Menschen das schrecklich Gemeinsame brechen und böse, böse Angewohnheit, unlesbare, unstrukturierte und nicht wartbare Muster zu schreiben.
Muster müssen nicht hässlich sein und sie müssen nicht hart sein. Wenn Sie hässliche Muster erstellen, ist dies eine Reflexion über Sie, nicht über sie.
Phänomenal exquisite Regex-Sprache
Ich wurde gebeten, darauf hinzuweisen, dass meine professionelle Lösung für Ihr Problem in Perl geschrieben wurde. Bist du überrascht? Hast du es nicht bemerkt? Ist diese Offenbarung eine Bombe?
Es ist wahr, dass nicht alle anderen Tools und Programmiersprachen in Bezug auf Regexes so bequem, ausdrucksstark und leistungsstark sind wie Perl. Es gibt da draußen ein großes Spektrum, von denen einige besser geeignet sind als andere. Im Allgemeinen ist es einfacher, mit den Sprachen zu arbeiten, die reguläre Ausdrücke als Teil der Kernsprache und nicht als Bibliothek ausgedrückt haben. Ich habe nichts mit regulären Ausdrücken gemacht, was Sie beispielsweise in PCRE nicht tun könnten, obwohl Sie das Programm anders strukturieren würden, wenn Sie C verwenden würden.
Irgendwann werden andere Sprachen in Bezug auf reguläre Ausdrücke aufholen, wo Perl jetzt ist. Ich sage das, weil damals, als Perl anfing, niemand so etwas wie Perls Regexe hatte. Sagen Sie alles, was Sie möchten, aber hier hat Perl eindeutig gewonnen: Alle haben Perls Regexe kopiert, wenn auch in unterschiedlichen Stadien ihrer Entwicklung. Perl war Pionier bei fast (nicht allen, aber fast) allem, worauf Sie sich heute in modernen Mustern verlassen, unabhängig davon, welches Werkzeug oder welche Sprache Sie verwenden. Also werden die anderen irgendwann aufholen.
Aber sie werden nur aufholen, wo Perl irgendwann in der Vergangenheit war, so wie es jetzt ist. Alles schreitet voran. In regulären Ausdrücken, wenn nichts anderes, wohin Perl führt, folgen andere. Wo wird Perl sein, wenn alle anderen endlich aufholen, wo Perl jetzt ist? Ich habe keine Ahnung, aber ich weiß, dass auch wir umgezogen sein werden. Wahrscheinlich sind wir näher an Perls Art, Muster herzustellen .
Wenn Sie so etwas mögen, es aber in Perl₅ verwenden möchten, könnte Sie Damian Conways wundervolles Regexp :: Grammars- Modul interessieren . Es ist absolut fantastisch und lässt das, was ich hier in meinem Programm getan habe, genauso primitiv erscheinen wie meine Muster, die Menschen ohne Leerzeichen oder alphabetische Bezeichner zusammenpressen. Hör zu!
Einfacher HTML-Chunker
Hier ist die vollständige Quelle für den Parser, von dem ich das Herzstück zu Beginn dieses Beitrags gezeigt habe.
Ich schlage nicht vor , dass Sie dies für eine streng getestete Parsing-Klasse verwenden sollten. Aber ich habe es satt, dass Leute so tun, als könne niemand HTML mit regulären Ausdrücken analysieren, nur weil sie es nicht können. Sie können es eindeutig, und dieses Programm ist ein Beweis für diese Behauptung.
Sicher, es ist nicht einfach, aber es ist möglich!
Und versucht , so zu tun , ist eine schreckliche Verschwendung von Zeit, weil gute Parsing Klassen existieren , die Sie sollten für diese Aufgabe verwenden. Die richtige Antwort für Leute, die versuchen, willkürliches HTML zu analysieren, ist nicht, dass es unmöglich ist. Das ist eine einfache und unaufrichtige Antwort. Die richtige und ehrliche Antwort ist, dass sie es nicht versuchen sollten, weil es zu mühsam ist, es von Grund auf neu herauszufinden. Sie sollten sich nicht den Rücken brechen, um ein Rad neu zu erfinden, das perfekt funktioniert.
Auf der anderen Seite ist HTML, das in eine vorhersehbare Teilmenge fällt, sehr einfach mit regulären Ausdrücken zu analysieren. Es ist kein Wunder, dass die Leute versuchen, sie zu benutzen, denn für kleine Probleme, Spielzeugprobleme, könnte nichts einfacher sein. Aus diesem Grund ist es so wichtig, die beiden Aufgaben - spezifisch und generisch - zu unterscheiden, da diese nicht unbedingt denselben Ansatz erfordern.
Ich hoffe, dass wir in Zukunft hier eine fairere und ehrlichere Behandlung von Fragen zu HTML und Regexes sehen können.
Hier ist mein HTML-Lexer. Es wird nicht versucht, eine validierende Analyse durchzuführen. es identifiziert nur die lexikalischen Elemente. Sie können sich das eher als HTML-Chunker als als HTML-Parser vorstellen . Es ist nicht sehr verzeihend für kaputtes HTML, obwohl es einige sehr kleine Zulassungen in diese Richtung zulässt.
Selbst wenn Sie niemals selbst vollständiges HTML analysieren (und warum sollten Sie das? Es ist ein gelöstes Problem!), Enthält dieses Programm viele coole Regex-Bits, von denen ich glaube, dass viele Leute viel lernen können. Genießen!
#!/usr/bin/env perl
#
# chunk_HTML - a regex-based HTML chunker
#
# Tom Christiansen <tchrist@perl.com
# Sun Nov 21 19:16:02 MST 2010
########################################
use 5.012;
use strict;
use autodie;
use warnings qw< FATAL all >;
use open qw< IN :bytes OUT :utf8 :std >;
MAIN: {
$| = 1;
lex_html(my $page = slurpy());
exit();
}
########################################################################
sub lex_html {
our $RX_SUBS; ###############
my $html = shift(); # Am I... #
for (;;) { # forgiven? :)#
given ($html) { ###############
last when (pos || 0) >= length;
printf "\@%d=", (pos || 0);
print "doctype " when / \G (?&doctype) $RX_SUBS /xgc;
print "cdata " when / \G (?&cdata) $RX_SUBS /xgc;
print "xml " when / \G (?&xml) $RX_SUBS /xgc;
print "xhook " when / \G (?&xhook) $RX_SUBS /xgc;
print "script " when / \G (?&script) $RX_SUBS /xgc;
print "style " when / \G (?&style) $RX_SUBS /xgc;
print "comment " when / \G (?&comment) $RX_SUBS /xgc;
print "tag " when / \G (?&tag) $RX_SUBS /xgc;
print "untag " when / \G (?&untag) $RX_SUBS /xgc;
print "nasty " when / \G (?&nasty) $RX_SUBS /xgc;
print "text " when / \G (?&nontag) $RX_SUBS /xgc;
default {
die "UNCLASSIFIED: " .
substr($_, pos || 0, (length > 65) ? 65 : length);
}
}
}
say ".";
}
#####################
# Return correctly decoded contents of next complete
# file slurped in from the <ARGV> stream.
#
sub slurpy {
our ($RX_SUBS, $Meta_Tag_Rx);
my $_ = do { local $/; <ARGV> }; # read all input
return unless length;
use Encode qw< decode >;
my $bom = "";
given ($_) {
$bom = "UTF-32LE" when / ^ \xFf \xFe \0 \0 /x; # LE
$bom = "UTF-32BE" when / ^ \0 \0 \xFe \xFf /x; # BE
$bom = "UTF-16LE" when / ^ \xFf \xFe /x; # le
$bom = "UTF-16BE" when / ^ \xFe \xFf /x; # be
$bom = "UTF-8" when / ^ \xEF \xBB \xBF /x; # st00pid
}
if ($bom) {
say "[BOM $bom]";
s/^...// if $bom eq "UTF-8"; # st00pid
# Must use UTF-(16|32) w/o -[BL]E to strip BOM.
$bom =~ s/-[LB]E//;
return decode($bom, $_);
# if BOM found, don't fall through to look
# for embedded encoding spec
}
# Latin1 is web default if not otherwise specified.
# No way to do this correctly if it was overridden
# in the HTTP header, since we assume stream contains
# HTML only, not also the HTTP header.
my $encoding = "iso-8859-1";
while (/ (?&xml) $RX_SUBS /pgx) {
my $xml = ${^MATCH};
next unless $xml =~ m{ $RX_SUBS
(?= encoding ) (?&name)
(?&equals)
(?"e) ?
(?<ENCODING> (?&value) )
}sx;
if (lc $encoding ne lc $+{ENCODING}) {
say "[XML ENCODING $encoding => $+{ENCODING}]";
$encoding = $+{ENCODING};
}
}
while (/$Meta_Tag_Rx/gi) {
my $meta = $+{META};
next unless $meta =~ m{ $RX_SUBS
(?= http-equiv ) (?&name)
(?&equals)
(?= (?"e)? content-type )
(?&value)
}six;
next unless $meta =~ m{ $RX_SUBS
(?= content ) (?&name)
(?&equals)
(?<CONTENT> (?&value) )
}six;
next unless $+{CONTENT} =~ m{ $RX_SUBS
(?= charset ) (?&name)
(?&equals)
(?<CHARSET> (?&value) )
}six;
if (lc $encoding ne lc $+{CHARSET}) {
say "[HTTP-EQUIV ENCODING $encoding => $+{CHARSET}]";
$encoding = $+{CHARSET};
}
}
return decode($encoding, $_);
}
########################################################################
# Make sure to this function is called
# as soon as source unit has been compiled.
UNITCHECK { load_rxsubs() }
# useful regex subroutines for HTML parsing
sub load_rxsubs {
our $RX_SUBS = qr{
(?(DEFINE)
(?<WS> \s * )
(?<any_nv_pair> (?&name) (?&equals) (?&value) )
(?<name> \b (?= \pL ) [\w:\-] + \b )
(?<equals> (?&WS) = (?&WS) )
(?<value> (?"ed_value) | (?&unquoted_value) )
(?<unwhite_chunk> (?: (?! > ) \S ) + )
(?<unquoted_value> [\w:\-] * )
(?<any_quote> ["'] )
(?<quoted_value>
(?<quote> (?&any_quote) )
(?: (?! \k<quote> ) . ) *
\k<quote>
)
(?<start_tag> < (?&WS) )
(?<html_end_tag> > )
(?<xhtml_end_tag> / > )
(?<end_tag>
(?&WS)
(?: (?&html_end_tag)
| (?&xhtml_end_tag) )
)
(?<tag>
(?&start_tag)
(?&name)
(?:
(?&WS)
(?&any_nv_pair)
) *
(?&end_tag)
)
(?<untag> </ (?&name) > )
# starts like a tag, but has screwed up quotes inside it
(?<nasty>
(?&start_tag)
(?&name)
.*?
(?&end_tag)
)
(?<nontag> [^<] + )
(?<string> (?"ed_value) )
(?<word> (?&name) )
(?<doctype>
<!DOCTYPE
# please don't feed me nonHTML
### (?&WS) HTML
[^>]* >
)
(?<cdata> <!\[CDATA\[ .*? \]\] > )
(?<script> (?= <script ) (?&tag) .*? </script> )
(?<style> (?= <style ) (?&tag) .*? </style> )
(?<comment> <!-- .*? --> )
(?<xml>
< \? xml
(?:
(?&WS)
(?&any_nv_pair)
) *
(?&WS)
\? >
)
(?<xhook> < \? .*? \? > )
)
}six;
our $Meta_Tag_Rx = qr{ $RX_SUBS
(?<META>
(?&start_tag) meta \b
(?:
(?&WS) (?&any_nv_pair)
) +
(?&end_tag)
)
}six;
}
# nobody *ever* remembers to do this!
END { close STDOUT }