Кажется, что другие ответы объяснили то, что я написал в своем учебном посте. Тем не менее, я хотел добавить, что я столкнулся с другим полезным методом в Mojo::DOM (фактически в классе Mojo::Collection), который называется pluck
. Этот метод уменьшает визуальную сложность
->map(sub{$_->text})
to
->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, а это означает, что возврат из pluck
будет коллекцией коллекций. Чтобы сгладить структуру, мне нужно вызвать 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::DOM и Mojo::Collection должно быть достаточно для анализа DOM. Я думаю, что часть вашей проблемы заключается в том, что вы не заметили взаимозависимости объектов DOM и Collection и поэтому ошибочно предположили, что все вызовы методов относятся к объектам DOM. Если вы внимательно прочитаете, то увидите, что некоторые методы DOM (возвращающие могут иметь более одного результата) возвращают объекты Collection, и find
является одним из таких методов.
person
Joel Berger
schedule
10.10.2012