【问题标题】:advanced perl syntax - Mojo::DOM高级 perl 语法 - Mojo::DOM
【发布时间】:2012-09-30 21:54:33
【问题描述】:

我正在尝试了解一些高级(对我而言)perl 语法,用于在this tutorial 之后使用 DOM 进行 html 解析:

say "div days:";
say $_->text for $dom->find('div.days')->each;

say "\nspan hours:";
say $_->text for $dom->find('span.hours')->each;

这个语法是什么意思?这是一个什么样的循环?经典建筑看起来像这样:for(i=0;i<10;i++){ code } 不是:{code} for (some_condition)

在这种情况下,“每个”关键字的含义是什么?它是否与each Perl builtin function 有共同之处,或者它是特定于 Mojo::DOM 的?我认为如果each 在 Mojo::Dom 下,则应该在 Mojo::DOM 主页上提及。但是我没有发现methods section of their site下面有提到each,所以它一定是Perl的内置函数。但是,这个内置的each 函数的语法完全不同——这怎么可能?

教程页面的另一个例子

say "Open Times:";
say for $dom->find('div.openTime')
            ->map(sub{$_->children->each})
            ->map(sub{$_->text})
            ->each;

mapsub 方法的问题与上述相同。

  • 能否以更“C 风格”的方式重写那些“Perlish”代码,以便我理解它?
  • 最重要的是:如何列出 Mojo::DOM 中包含的所有方法的参数和返回值?它必须以某种方式完成,因为我读到即使对于 Perl,也有带有智能感知(自动完成)的 IDE,所以这个 IDE 必须知道方法返回值类型等。

【问题讨论】:

  • 我在下面的完整答案中回答,但让我强调一下,您没有找到所有方法名称的原因是“缺失”的方法实际上是 Mojo::Collection 上的方法,它是一个容器用于持有多个 dom 对象的对象。再次见下文。
  • 如果以下任何答案对您有帮助,请花点时间接受。您可以通过单击左侧的复选标记来执行此操作。如果您需要帮助,请参阅 faq

标签: perl dom syntax html-parsing mojolicious


【解决方案1】:
say "Open Times:";
say for $dom->find('div.openTime')
            ->map(sub{$_->children->each})
            ->map(sub{$_->text})
            ->each;

所有这些关键字(findmapeach)实际上不是关键字,而是来自 Mojo::DOM 的方法。您可以通过-> 运算符识别它们。

在这种情况下,有几个methods have been chained together。只有当它们中的每一个都再次返回其对象(在此示例中为 $dom)时,这才有可能。这种链接经常用在 JavaScript 中,尤其是像 jQuery 这样的现代框架。它使代码易于阅读并节省操作。

基本上,您在一个链中应用多个交易。

  1. find所有元素'div.openTime'
  2. map(对每个都做一些事情)用给定的子(这是一个实际的 Perl sub):
    1. 获取当前元素的所有children作为一个集合
    2. 并列出其中的each(如,返回一个数组)
  3. map 他们有一个给定的子:
    1. 从元素中提取text 内容
  4. 并列出其中的each

所有这些都包含在后缀foreach 中(正如@Quentin 所说)。 say 是您可以使用 use features qw(say) 加载的功能。它结合了print 和换行符。

也许现在这里发生的事情更清楚了:

my $collection1 = $dom->find('div.oopenTime');

my $collection2 = $collection1->map(
  sub {
    my $collection = $_->children;
    return $collection->each;
  }
);

my collection3 = $collection2->map(
  sub {
    return $_->text;
  }
);

foreach my $text ($collection3) {
  say $text;
}

提供自动完成功能的 IDE 通常会扫描相关代码以了解对象具有的方法。看看How do I list available methods on a given object or package in Perl? 或者阅读模块的代码。更好的是:阅读文档

【讨论】:

    【解决方案2】:

    这个语法是什么意思,这是怎么回事?

    这是一个postfix for loop

    for (@foo) {
        say $_
    }
    

    可以写成

    say $_ for @foo;
    

    还有“每个”关键字在这种情况下的含义

    它是a method on the object。它返回 Mojo::Collection 中的事物列表。

    【讨论】:

      【解决方案3】:

      似乎其他答案已经解释了我在教程帖子中写的内容。也就是说,我想补充一点,我已经掌握了 Mojo::DOM(实际上是 Mojo::Collection 类)中另一个有用的方法,称为 pluck。这种方法降低了视觉复杂度

      ->map(sub{$_->text})
      

      ->pluck('text')
      

      此外,我注意到我的 each 调用中至少有一些是无关的,并且在列表上下文中使用的 Mojo::Collection 将自动“按我的意思”和 each。 s> 编辑:我检查了这个,实际上当用作字符串时,元素用换行符连接。由于这不是我想要的,我已经回复了我的each 电话。

      这里所说的只是我现在如何编写相同的教程脚本:

      #!/usr/bin/env perl
      
      use strict;
      use warnings;
      
      use 5.10.0;
      use Mojo::DOM;
      
      my $dom = Mojo::DOM->new(<<'HTML');
      <div class="box notranslate" id="venueHours">
      <h5 class="translate">Hours</h5>
      <div class="status closed">Currently closed</div>
      <div class="hours">
        <div class="timespan">
          <div class="openTime">
            <div class="days">Mon,Tue,Wed,Thu,Sat</div>
            <span class="hours"> 10:00 AM–6:00 PM</span>
          </div>
        </div>
        <div class="timespan">
          <div class="openTime">
            <div class="days">Fri</div>
            <span class="hours"> 10:00 AM–9:00 PM</span></div>
          </div>
          <div class="timespan">
            <div class="openTime">
              <div class="days">Sun</div>
              <span class="hours"> 10:00 AM–5:00 PM</span>
            </div>
          </div>
        </div>
      </div>
      HTML
      
      say "div days:";
      say for $dom->find('div.days')->pluck('text')->each;
      
      say "\nspan hours:";
      say for $dom->find('span.hours')->pluck('text')->each;
      
      say "\nOpen Times:";
      say for $dom->find('div.openTime')
                  ->map(sub{$_->children->each})
                  ->pluck('text')
                  ->each;
      

      请注意,我不使用-&gt;pluck('children'),因为children 方法返回一个Mojo::Collection 对象,这意味着来自pluck 的返回将是一个集合的集合。为了展平结构,我需要在children 调用的结果上调用each,因此我无法删除那个特定的-&gt;map 调用。

      但是,现在我想知道我是否不能一起避免这个麻烦? Mojo::DOM 对 CSS3 selectors (w3schools reference) 有很好的支持,我可能会尝试的一件事是不要直接选择父级 (div.openTime),而是在选择器中选择其子级。

      say "\nOpen Times:";
      say for $dom->find('div.openTime > *')->pluck('text')->each;
      

      所以这里有一个很好的教训:允许选择器为您提供几乎您想要的集合,这样您就不必在以后对其进行转换。


      回答你最后的问题:

      翻译一下

      say for $dom->find('div.openTime')
                  ->map(sub{$_->children->each})
                  ->map(sub{$_->text})
                  ->each;
      

      对于更多 C-esque Perl(虽然我不会把它带到 for(i=0;i&lt;10;i++){ ... } 极端)它可能看起来像

      my @open_times = $dom->find('div.openTime')->each;
      
      my @all_children;
      foreach my $elem ( @open_times ) {
        my @children = $elem->children->each;
        push @all_children, @children;
      }
      
      my @texts;
      foreach my $child ( @all_children ) {
        push @texts, $child->text;
      }
      
      foreach my $text ( @texts ) {
        print $text . "\n";
      }
      

      我相信您会明白为什么我更喜欢 Mojo(对象链接)方式。

      关于您的第二个问题:Mojolicious 有很棒的(如果有时过于冗长)文档。开始here了解整个系统。具体阅读Mojo::DOMMojo::Collection 应该足以处理DOM 解析。我认为您的部分问题是您没有注意到 DOM 和 Collection 对象的相互依赖关系,因此您错误地认为所有方法调用都在 DOM 对象上。仔细阅读会发现,一些 DOM 方法(返回的可能不止一个结果)返回 Collection 对象,find 就是这样一种方法。

      【讨论】:

        猜你喜欢
        • 2013-06-30
        • 2014-07-26
        • 1970-01-01
        • 2015-08-18
        • 2017-09-04
        • 1970-01-01
        • 2011-10-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多