3

我正在尝试按照本教程了解使用 DOM 进行 html 解析的一些高级(对我而言)perl 语法:

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 内置函数有共同之处,或者它是特定于 Mojo::DOM 的?我认为如果each在 Mojo::Dom 下,应该在 Mojo::DOM 主页上提及。但我没有在他们网站each方法部分找到任何提及,所以它必须是 Perl 的内置函数。但是,这个内置each函数的语法完全不同——这怎么可能?

教程页面的另一个示例

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

mapsub方法相同的问题。

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

3 回答 3

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

所有这些关键字(find, map, each)实际上都不是关键字,而是来自 Mojo::DOM 的方法。您可以通过操作员识别它们->

在这种情况下,几种方法被链接在一起。只有当它们中的每一个都再次返回其对象(在本例中为$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 通常会扫描相关代码以了解对象具有的方法。看看如何在 Perl 中列出给定对象或包的可用方法?或阅读模块的代码。更好的是:阅读文档

于 2012-10-10T12:54:49.283 回答
5

这个语法是什么意思,这里发生了什么?

它是循环的后缀

for (@foo) {
    say $_
}

可以写成

say $_ for @foo;

在这种情况下,“每个”关键字的含义是什么

它是对象上的一个方法。它返回 Mojo::Collection 中的事物列表。

于 2012-10-10T12:21:05.507 回答
3

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

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

->pluck('text')

此外,我注意到至少我的一些each调用是无关的,并且在列表上下文中使用的 Mojo::Collection 将自动“按我的意思做” each 编辑:我检查了这个,实际上当用作字符串时,元素用换行符连接。因为这不是我想要的,所以我已经回了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;

请注意,我不使用该方法是->pluck('children')因为该children方法返回 Mojo::Collection 对象,这意味着返回 frompluck将是集合的集合。为了展平结构,我需要调用调用each结果,children因此我无法删除该特定->map调用。

但是,现在我想知道我是否不能一起避免这个麻烦?Mojo::DOM 对CSS3 选择器w3schools 参考)有很好的支持,我可能会尝试的一件事是不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<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 有很棒的(如果有时过于冗长)文档。从这里开始了解整个系统。具体阅读有关Mojo::DOMMojo::Collection应该足以处理 DOM 解析。我认为您的部分问题是您没有注意到 DOM 和 Collection 对象的相互依赖关系,因此您错误地认为所有方法调用都在 DOM 对象上。当您仔细阅读时,您会看到一些 DOM 方法(返回的可能不止一个结果)返回 Collection 对象,并且find就是这样一种方法。

于 2012-10-10T15:27:39.830 回答