Perl HTML :: Tokeparser在标签之间获取原始html

dor*_*thy 2 perl html-parsing

我正在使用TokeParser来提取标签内容.

...
$text = $p->get_text("/td") ;
...
Run Code Online (Sandbox Code Playgroud)

通常它会返回清理的文本.我想要的是返回td和之间的/td所有其他html元素.怎么做.

我正在使用本教程中的示例.谢谢

在这个例子中,

my( $tag, $attr, $attrseq, $rawtxt) = @{ $token };
Run Code Online (Sandbox Code Playgroud)

我相信有一些诀窍$rawtxt.

sim*_*que 5

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>
Run Code Online (Sandbox Code Playgroud)

该程序将解析该__DATA__部分中的HTML文档,并在<td>和之间打印包括HTML在内的所有内容</td>.它将打印一行<td>.让我们一步一步地完成它.

  • 阅读完文档后,我了解到HTML :: TokeParser中的每个标记都有一个与之关联的类型.有六种类型:S,E,T,C,DPI.医生说:

    此方法将返回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]
    
    Run Code Online (Sandbox Code Playgroud)
  • 我们想要访问$text存储在这些令牌中的内容,因为没有其他方法可以获取看起来像HTML标记的内容.因此,我创建了一个调度表来处理它们%dispatch.它存储了一堆稍后调用的代码引用.

  • 我们从中读取文档__DATA__,这对于这个例子很方便.
  • 首先,我们需要<td>使用该get_tag方法获取s .@ nrathaus的评论指出了我的方式.它将在打开后将解析器移动到下一个标记<td>.我们不关心什么get_tag返回,因为我们只想要后面的令牌<td>.
  • 我们使用该方法get_token获取下一个令牌并使用它执行操作:

    • 但我们只想这样做,直到找到相应的结束</td>.如果我们看到,我们标记next了外部while循环TD.
    • 此时,continue被调用并打印换行符.
    • 如果我们不在最后,魔术就会发生:调度表; 正如我们之前看到的,令牌数组ref中的第一个元素保存了类型.每个类型都有一个代码ref %dispatch.我们称它为并通过完整的数组引用$token通过去$coderef->(@args).我们在当前行上打印结果.

      这将产生这样的东西<strong>,foo,</strong>等在每次运行.

请注意,这仅适用于一张桌子.如果表中有一个表(类似的东西<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
Run Code Online (Sandbox Code Playgroud)

这种方法也可以处理像魅力这样的多维表.