"can I parse this HTML in a single regex". And the answer is yes!
... with a BIG emphasis on this HTML. HTML is a beast to parse correctly, due to its inheritance from SGML, and due to the error correction / guessing algorithms used in most browsers. Simple regular expressions may work as long as the HTML has a well-known format and does not use too many SGML or encoding tricks.
Just yesterday, I stubled over this nice piece of valid(!) HTML, hand-crafted to defeat most simple-minded string parsers and regular expressions:
<h1>My-IP-Service</h1>
<h1 class="myip"><!--- >
A comment about the abuse of their service they want to prevent ...
<a href="/netze/tools/whois-abfrage/?rm=whois_formular">nicht ermittel
+bar</a>
<a href="/netze/tools/whois-abfrage/?rm=whois_formular">127.0.0.1</a>
<a href="/netze/tools/whois-abfrage/?rm=whois_formular">198.18.0.15</a
+>
< --><a href="/netze/tools/whois-abfrage/?rm=whois_formular">9<!--
+ >226.180.195.155
< -->2<!-- >
253.159.244.9
< -->.<!-- >
253.239.61.182< -->2<!-- >230.121.254.208
< -->2<!-- >
251.168.157.152
< -->4<!-- >
254.121.189.15< -->.<!-- >
237.24.153.213< -->8<!-- >246.217.119.248
< -->.<!-- >
245.167.107.28
< -->1<!-- >226.204.198.25
< -->1<!-- >
233.167.179.189
< -->7<!-- >
228.193.179.191< --></a></h1>
(From: http://www.heise.de/netze/tools/ip)
Returning the correct IP address (92.224.8.117 in this case) from this piece of HTML is not impossible, and with enough effort, someone may be able to write a regexp that does the job for this special obfuscation. But with HTML::Parser, it is essentially a no-brainer requiring about 10 lines of code (Sorry, Heise ...). And unless the author finds a way to confuse HTML::Parser without breaking browsers, it will not fail when the author modifies the obfuscation. (Well, changing the H1 tag or its class attribute would break this special implementation.)
#!/usr/bin/perl -w
use strict;
use LWP::Simple;
use HTML::Parser;
my $ip='';
my $wanted=0;
HTML::Parser->new(
api_version => 3,
start_h => [sub { $wanted=1 if ($_[0] eq 'h1') && ($_[1]->{'class'
+} eq 'myip') }, 'tagname,attr'],
end_h => [sub { $wanted=0 if $_[0] eq 'h1' }, 'tagname'],
text_h => [sub { $ip.=$_[0] if $wanted }, 'dtext'],
)->parse(get('http://www.heise.de/netze/tools/ip'));
print "$ip\n";
So, DON'T use regular expressions to parse HTML or XML. Except perhaps in very special cases where you control how the HTML/XML is generated.
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
|