HTML::TokeParser 没有执行此操作的内置功能。但是,可以单独查看<td>s 之间的每个令牌。
#!/usr/bin/perl
use strictures;
use HTML::TokeParser;
use 5.012;
# dispatch table with subs to handle the different types of tokens
my %dispatch = (
S => sub { $_[0]->[4] }, # Start tag
E => sub { $_[0]->[2] }, # End tag
T => sub { $_[0]->[1] }, # Text
C => sub { $_[0]->[1] }, # Comment
D => sub { $_[0]->[1] }, # Declaration
PI => sub { $_[0]->[2] }, # Process Instruction
);
# create the parser
my $p = HTML::TokeParser->new( \*DATA ) or die "Can't open: $!";
# fetch all the <td>s
TD: while ( $p->get_tag('td') ) {
# go through all tokens ...
while ( my $token = $p->get_token ) {
# ... but stop at the end of the current <td>
next TD if ( $token->[0] eq 'E' && $token->[1] eq 'td' );
# call the sub corresponding to the current type of token
print $dispatch{$token->[0]}->($token);
}
} continue {
# each time next TD is called, print a newline
print "\n";
}
__DATA__
<html><body><table>
<tr>
<td><strong>foo</strong></td>
<td><em>bar</em></td>
<td><font size="10"><font color="#FF0000">frobnication</font></font>
<p>Lorem ipsum dolor set amet fooofooo foo.</p></td>
</tr></table></body></html>
该程序将解析__DATA__ 部分中的HTML 文档并打印包括<td> 和</td> 之间的HTML 在内的所有内容。每个<td> 将打印一行。让我们一步一步来。
-
阅读documentation 后,我了解到HTML::TokeParser 中的每个标记都有一个与之关联的类型。有六种类型:S、E、T、C、D 和 PI。医生说:
此方法将返回在 HTML 文档中找到的下一个标记,或者
文件末尾的undef。令牌作为数组返回
参考。数组的第一个元素将是一个字符串,表示
此标记的类型:“S”表示开始标签,“E”表示结束标签,“T”表示
文本,“C”表示注释,“D”表示声明,“PI”表示过程
指示。令牌数组的其余部分取决于类型
这个:
["S", $tag, $attr, $attrseq, $text]
["E", $tag, $text]
["T", $text, $is_data]
["C", $text]
["D", $text]
["PI", $token0, $text]
我们想要访问存储在这些令牌中的$text,因为没有其他方法可以获取看起来像 HTML 标记的内容。因此,我创建了一个dispatch table 来在%dispatch 中处理它们。它存储了一堆稍后调用的代码引用。
- 我们从
__DATA__读取文档,方便这个例子。
- 首先,我们需要使用
get_tag方法获取<td>s。 @nrathaus 的评论向我指出了这一点。它会将解析器移动到打开<td> 之后的下一个标记。我们不关心 get_tag 返回什么,因为我们只想要 <td> 之后的令牌。
-
我们使用方法get_token 来获取下一个令牌并用它做一些事情:
请注意,这只适用于一张桌子。如果表中有一个表(例如<td> ... <td></td> ... </td>),这将中断。您必须对其进行调整以记住它的深度。
另一种方法是使用miyagawa 出色的Web::Scraper。这样,我们的代码就少了很多:
#!/usr/bin/perl
use strictures;
use Web::Scraper;
use 5.012;
my $s = scraper {
process "td", "foo[]" => 'HTML'; # grab the raw HTML for all <td>s
result 'foo'; # return the array foo where the raw HTML is stored
};
my $html = do { local $/ = undef; <DATA> }; # read HTML from __DATA__
my $res = $s->scrape( $html ); # scrape
say for @$res; # print each line of HTML
这种方法还可以像魅力一样处理多维表格。