【问题标题】:How to parse between <div class ="foo"> and </div> easily in Perl如何在 Perl 中轻松解析 <div class ="foo"> 和 </div>
【发布时间】:2011-12-25 10:40:27
【问题描述】:

我想将网站解析为 Perl 数据结构。 首先我加载页面

use LWP::Simple;
my $html = get("http://f.oo");

现在我知道了两种处理方法。 首先是正则表达式和模块。

我从阅读HTML::Parser 开始,并找到了一些示例。 但是根据 Perl 知识,我不太确定。

我的代码示例继续

my @links;

my $p = HTML::Parser->new();
$p->handler(start => \&start_handler,"tagname,attr,self");
$p->parse($html);

foreach my $link(@links){
  print "Linktext: ",$link->[1],"\tURL: ",$link->[0],"\n";
}

sub start_handler{
  return if(shift ne 'a');
  my ($class) = shift->{href};
  my $self = shift;
  my $text;
  $self->handler(text => sub{$text = shift;},"dtext");
  $self->handler(end => sub{push(@links,[$class,$text]) if(shift eq 'a')},"tagname");
}

我不明白为什么会有两次轮班。第二个应该是自指针。但第一个让我认为自引用已经是 shiftet,用作哈希,href 的值存储在$class 中。有人可以解释一下这条线(my ($class) = shift-&gt;{href};)吗?

除此之外,我不想解析所有的 URL,我想将 &lt;div class ="foo"&gt;&lt;/div&gt; 之间的所有代码放入一个字符串中,其中有很多代码,特别是其他 &lt;div&gt;&lt;/div&gt; 标记。所以我或一个模块必须找到正确的结束。 之后我打算再次扫描字符串,找到特殊的类,比如&lt;h1&gt;,&lt;h2&gt;, &lt;p class ="foo2"&gt;&lt;/p&gt;等。

我希望这些信息可以帮助你给我一些有用的建议,并且请记住,首先我想要一个简单易懂的方法,这在第一级不一定是一个很好的表现!

【问题讨论】:

  • 不要使用正则表达式! HTML 不正常!
  • 看到有人使用 HTML 解析器而不是正则表达式来解析 HTML 多么令人耳目一新:p +1 只是为了那个
  • FWIW: my ($class) = shift-&gt;{href}; href 哈希成员。本来可以写成my $ref = shift; my $class = $ref-&gt;{"href"};
  • HTML::Parser 是必需的吗?使用实现标准 DOM 方法的东西(例如HTML::TagParser),您可能会简化很多。
  • 但是href来自哪里?首先我认为@_[0] 是shiftet。它是一个刚刚获得子程序名称的字符串。然后我猜它可能是一个标量变量$foo,它是一个指针。但是下一行会将@_[1] 放入$self,这对我来说没有意义。

标签: html perl parsing


【解决方案1】:

HTML::Parser 与其说是解析器,不如说是一个分词器。它给你留下了很多艰苦的工作。您是否考虑过使用HTML::TreeBuilder(使用HTML::Parser)或XML::LibXML(支持HTML 的优秀库)?

【讨论】:

    【解决方案2】:

    使用HTML::TokeParser::Simple

    根据您的描述未经测试的代码:

    #!/usr/bin/env perl
    
    use strict; use warnings;
    
    use HTML::TokeParser::Simple;
    
    my $p = HTML::TokeParser::Simple->new(url => 'http://example.com/example.html');
    
    my $level;
    
    while (my $tag = $p->get_tag('div')) {
        my $class = $tag->get_attr('class');
        next unless defined($class) and $class eq 'foo';
    
        $level += 1;
    
        while (my $token = $p->get_token) {
            $level += 1 if $token->is_start_tag('div');
            $level -= 1 if $token->is_end_tag('div');
            print $token->as_is;
            unless ($level) {
                last;
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      没必要这么复杂。您可以使用 CSS selectorsMojo::UserAgent 在 DOM 中检索和查找元素:

      say Mojo::UserAgent->new->get('http://f.oo')->res->dom->find('div.foo');
      

      或者,遍历找到的元素:

      say $_ for Mojo::UserAgent->new->get('http://f.oo')->res->dom
          ->find('div.foo')->each;
      

      或者,使用回调循环:

      Mojo::UserAgent->new->get('http://f.oo')->res->dom->find('div.foo')->each(sub {
        my ($count, $el) = @_;
        say "$count: $el";
      });
      

      【讨论】:

      • 好像我的 Mac 没有安装 Mojo::UserAgent,这意味着我们的网络服务器也没有这个。 TokeParser::Simple 也是如此。但不管怎么说。发现要解析的站点不是xhtml,只好自己走。
      • Mojo::UserAgent 不是核心的一部分,但安装起来很简单:“curl -L cpanmin.us | perl - Mojolicious”。如果你把自己限制在核心,你就会错过 Perl 的主要好处,这将是不幸的。此外,如果您的文档是任何形式的 HTML,Mojo::DOM 应该处理它;它适用于实际使用,而不是严格的 xml 标签。
      【解决方案4】:

      根据文档,处理程序的签名是(\%attr, \@attr_seq, $text)。共有三个班次,每个参数一个班次。

      my ($class) = shift->{href};
      

      相当于:

      my $class;
      my %attr_seq;
      my $attr_seq_ref;
      
      $attr_seq_ref = shift;
      %attr_seq = %$attr_seq_ref;
      $class = $attr_seq{'href'};
      

      【讨论】:

      • 我明白了。但是条件是什么?它不叫另一个转变吗?当它以...&lt;a ... 开头时,为什么它只是一个“a”?
      • 正如我所说,这里有三个班次,而不是两个班次:一个在if,一个用于属性(其中一个分配给$class),一个用于变成@ 987654327@。测试条件测试标签名称 - 解析器本身将处理 &lt;
      • 如果ifcounts,那么我看到五个班次。其中两个处于一种状态。如果只有三个,那么条件转换不会从数组中拉出一些东西,或者?
      • 只有三个。底部两个在子函数中,因此它们不会在同一个@_ 上运行。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-10
      • 2016-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-11
      相关资源
      最近更新 更多