Oh Ya, Anda Dapat Menggunakan Regex untuk Mengurai HTML!
Untuk tugas yang Anda coba, regex benar - benar baik-baik saja!
Ini adalah benar bahwa kebanyakan orang meremehkan kesulitan parsing HTML dengan ekspresi reguler dan karena itu melakukannya buruk.
Tetapi ini bukan kelemahan mendasar yang terkait dengan teori komputasi. Konyol itu banyak dibiakkan di sekitar sini , tetapi jangan Anda percayai mereka.
Jadi sementara itu pasti bisa dilakukan (postingan ini berfungsi sebagai bukti keberadaan fakta yang tidak bisa dibantah ini), itu tidak berarti harus begitu .
Anda harus memutuskan sendiri apakah Anda siap untuk menulis apa yang dianggap sebagai pengurai HTML khusus dan khusus dari regex. Kebanyakan orang tidak.
Tapi saya . ☻
Solusi Parsing HTML Berbasis Regex Umum
Pertama saya akan menunjukkan betapa mudahnya untuk mem - parsing HTML sewenang-wenang dengan regex. Program lengkap ada di akhir postingan ini, tetapi inti dari pengurai adalah:
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);
}
}
}
Lihat betapa mudahnya membaca?
Seperti yang tertulis, ini mengidentifikasi setiap bagian dari HTML dan memberitahu di mana ia menemukan bagian itu. Anda dapat dengan mudah memodifikasinya untuk melakukan apa pun yang Anda inginkan dengan jenis karya yang diberikan, atau untuk jenis yang lebih khusus dari ini.
Saya tidak memiliki test case yang gagal (kiri :): Saya telah berhasil menjalankan kode ini pada lebih dari 100.000 file HTML - setiap yang saya dapat dengan cepat dan mudah saya dapatkan. Selain itu, saya juga menjalankannya pada file yang khusus dibuat untuk memecah parser yang naif.
Ini bukan parser yang naif.
Oh, aku yakin itu tidak sempurna, tapi aku belum berhasil memecahkannya. Saya pikir meskipun ada sesuatu, perbaikannya akan mudah dilakukan karena struktur program yang jelas. Bahkan program regex-berat harus memiliki struktur.
Nah, itu tidak mungkin, izinkan saya menjawab pertanyaan OP.
Demo Memecahkan Tugas OP Menggunakan Regex
Program kecil html_input_rx
yang saya sertakan di bawah ini menghasilkan output berikut, sehingga Anda dapat melihat bahwa parsing HTML dengan regex berfungsi dengan baik untuk apa yang ingin Anda lakukan:
% 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"
Masukkan Tag Input Input, See No Evil Input
Inilah sumber untuk program yang menghasilkan output di atas.
#!/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: $!";
}
Ini dia! Tidak ada apa-apa! :)
Hanya Anda yang bisa menilai apakah keahlian Anda dengan regex sesuai dengan tugas parsing tertentu. Tingkat keahlian setiap orang berbeda, dan setiap tugas baru berbeda. Untuk pekerjaan di mana Anda memiliki set input yang terdefinisi dengan baik, regex jelas merupakan pilihan yang tepat, karena itu sepele untuk menyatukan beberapa ketika Anda memiliki subset HTML yang terbatas untuk ditangani. Bahkan pemula regex harus menangani pekerjaan itu dengan regex. Ada lagi yang berlebihan.
Namun , begitu HTML mulai berkurang, setelah mulai bercampur dengan cara-cara yang tidak dapat Anda prediksi tetapi yang legal, setelah Anda harus mencocokkan lebih banyak hal yang berbeda atau dengan dependensi yang lebih kompleks, Anda akhirnya akan mencapai titik di mana Anda harus bekerja lebih keras untuk menghasilkan solusi yang menggunakan regex daripada menggunakan kelas parsing. Di mana titik impas jatuh tergantung lagi pada tingkat kenyamanan Anda sendiri dengan regex.
Jadi apa yang harus aku lakukan?
Saya tidak akan memberi tahu Anda apa yang harus Anda lakukan atau apa yang tidak dapat Anda lakukan. Saya pikir itu salah. Saya hanya ingin memberi Anda kemungkinan, buka sedikit mata Anda. Anda bisa memilih apa yang ingin Anda lakukan dan bagaimana Anda ingin melakukannya. Tidak ada yang absolut - dan tidak ada orang lain yang tahu situasi Anda sendiri sebaik Anda sendiri. Jika sesuatu sepertinya terlalu banyak bekerja, yah, mungkin memang begitu. Pemrograman harus menyenangkan , Anda tahu. Jika tidak, Anda mungkin salah melakukannya.
Orang dapat melihat html_input_rx
program saya dengan sejumlah cara yang valid. Salah satunya adalah Anda memang dapat mem - parsing HTML dengan ekspresi reguler. Tetapi yang lain adalah bahwa itu jauh, jauh, jauh lebih sulit daripada yang pernah dipikirkan oleh siapa pun. Ini dapat dengan mudah mengarah pada kesimpulan bahwa program saya adalah bukti dari apa yang tidak boleh Anda lakukan, karena itu terlalu sulit.
Saya tidak akan tidak setuju dengan itu. Tentu saja jika semua yang saya lakukan dalam program saya tidak masuk akal bagi Anda setelah beberapa penelitian, maka Anda seharusnya tidak mencoba menggunakan regex untuk tugas semacam ini. Untuk HTML spesifik, regex itu bagus, tetapi untuk HTML umum, itu sama saja dengan kegilaan. Saya menggunakan kelas parsing sepanjang waktu, terutama jika itu HTML saya belum membuat sendiri.
Regex optimal untuk masalah parsing HTML kecil , pessimal untuk yang besar
Sekalipun program saya dianggap sebagai ilustrasi mengapa Anda tidak boleh menggunakan regex untuk mem-parsing HTML umum - yang tidak apa-apa, karena saya agak bermaksud demikian - ☺ - masih harus menjadi pembuka mata sehingga lebih banyak orang mematahkan hal yang sangat umum dan kebiasaan menulis yang tidak menyenangkan, tidak terstruktur, dan tidak terpelihara.
Pola tidak harus jelek, dan tidak harus sulit. Jika Anda membuat pola jelek, itu adalah refleksi Anda, bukan mereka.
Bahasa Regex Fenomenal Indah
Saya telah diminta untuk menunjukkan bahwa solusi profesional saya untuk masalah Anda telah ditulis dalam Perl. Apakah kamu terkejut? Apakah kamu tidak memperhatikan? Apakah wahyu ini mengejutkan?
Memang benar bahwa tidak semua alat dan bahasa pemrograman lainnya cukup nyaman, ekspresif, dan kuat ketika datang ke regex seperti Perl. Ada spektrum besar di luar sana, dengan beberapa lebih cocok daripada yang lain. Secara umum, bahasa yang telah menyatakan regex sebagai bagian dari bahasa inti alih-alih sebagai perpustakaan lebih mudah digunakan. Saya tidak melakukan apa pun dengan regex yang tidak dapat Anda lakukan, katakanlah, PCRE, meskipun Anda akan menyusun program secara berbeda jika Anda menggunakan C.
Akhirnya bahasa-bahasa lain akan menyusul di mana Perl sekarang dalam hal regex. Saya mengatakan ini karena ketika Perl dimulai, tidak ada orang lain yang memiliki regex Perl. Katakan apa pun yang Anda suka, tetapi di sinilah Perl jelas menang: semua orang menyalin regex Perl meskipun pada berbagai tahap perkembangan mereka. Perl memelopori hampir (tidak semua, tetapi hampir) segala sesuatu yang Anda andalkan dalam pola modern saat ini, tidak peduli alat atau bahasa apa yang Anda gunakan. Jadi akhirnya yang lain akan menyusul.
Tapi mereka hanya akan mengejar dimana Perl di masa lalu, sama seperti sekarang. Semuanya maju. Dalam regex jika tidak ada yang lain, di mana Perl memimpin, yang lain mengikuti. Di mana Perl akan setelah semua orang akhirnya mengejar ke tempat Perl sekarang? Saya tidak tahu, tapi saya tahu kita juga akan pindah. Mungkin kita akan lebih dekat dengan gaya pola kerajinan Perl₆ .
Jika Anda menyukai hal semacam itu tetapi ingin menggunakannya dalam Perl₅, Anda mungkin tertarik pada modul Regexp :: Grammars yang luar biasa dari Damian Conway . Ini benar-benar luar biasa, dan membuat apa yang saya lakukan di sini dalam program saya tampak sama primitifnya dengan saya membuat pola-pola yang dijejali orang tanpa spasi putih atau pengidentifikasi alfabet. Saksikan berikut ini!
Chunker HTML sederhana
Berikut adalah sumber lengkap untuk parser yang saya perlihatkan bagian tengahnya dari pada awal posting ini.
Saya tidak menyarankan Anda harus menggunakan ini di atas kelas parsing yang teruji. Tapi saya bosan dengan orang-orang yang berpura-pura tidak ada yang bisa menguraikan HTML dengan regex hanya karena mereka tidak bisa. Anda jelas bisa, dan program ini adalah bukti dari pernyataan itu.
Tentu, itu tidak mudah, tapi itu adalah mungkin!
Dan mencoba melakukannya adalah pemborosan waktu, karena ada kelas parsing yang baik yang harus Anda gunakan untuk tugas ini. Jawaban yang tepat untuk orang yang mencoba menguraikan HTML sewenang-wenang bukanlah bahwa itu tidak mungkin. Itu adalah jawaban yang lancar dan tidak jujur. Jawaban yang benar dan jujur adalah bahwa mereka tidak boleh mencobanya karena terlalu merepotkan untuk mencari tahu dari awal; mereka tidak harus mematahkan punggungnya untuk berusaha menemukan roda yang berfungsi dengan baik.
Di sisi lain, HTML yang termasuk dalam subset yang dapat diprediksi sangat mudah diurai dengan regex. Tidak heran orang mencoba menggunakannya, karena untuk masalah kecil, masalah mainan mungkin, tidak ada yang lebih mudah. Itu sebabnya sangat penting untuk membedakan dua tugas - spesifik vs generik - karena ini tidak selalu menuntut pendekatan yang sama.
Saya berharap di masa depan di sini untuk melihat perlakuan yang lebih adil dan jujur atas pertanyaan tentang HTML dan regex.
Inilah lexer HTML saya. Itu tidak mencoba melakukan pengesahan parse; itu hanya mengidentifikasi unsur-unsur leksikal. Anda mungkin menganggapnya lebih sebagai chunker HTML daripada parser HTML. Ini tidak terlalu memaafkan HTML yang rusak, meskipun membuat beberapa kelonggaran sangat kecil ke arah itu.
Bahkan jika Anda tidak pernah menguraikan HTML lengkap sendiri (dan mengapa Anda harus melakukannya? Ini adalah masalah yang dipecahkan!), Program ini memiliki banyak bit regex keren yang saya percaya banyak orang dapat belajar banyak dari. Nikmati!
#!/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 }