#!/usr/bin/perl
use strict;
use warnings;
use Web::Scraper;
use HTML::Entities;
my $html = do { local $/; <DATA> };
my $s = scraper {
process '[data-reader]', 'list[]' => {
tag => sub { $_->tag },
content => 'TEXT',
};
result 'list';
};
my $results = $s->scrape($html);
for my $part (@$results) {
print "<$part->{tag}>" . encode_entities($part->{content}) . "</$part->{tag}>\n";
}
__DATA__
<h1 data-reader="on">Hello <em>world</em></h1>
<h2>This is subheading</h2>
<h3 style="color:#000;" data-reader="on" class="phead">Paragraph Heading</h3>
Выход:
<h1>Hello world</h1>
<h3>Paragraph Heading</h3>
Способность передать необработанную подпрограмму как спецификацию экстрактора, кажется недокументированной, но
- документация Web :: Scraper в целом пятнистая, а
- используется в хотя бы в одном примере ,
так что я не расстраиваюсь из-за его использования.
Я перекодирую $part->{content}
как HTML, чтобы избежать проблем в случае, если кто-то, например,
<div data-reader="on"><script>alert(42)</script></div>
Если бы вы просто напечатали $part->{content}
, это дало бы вам <script>alert(42)</script>
, что, вероятно, не то, что вы хотите.
Подробнее:
my $s = scraper {
process '[data-reader]', 'list[]' => {
tag => sub { $_->tag },
content => 'TEXT',
};
result 'list';
};
scraper
берет блок кода и оборачивает его в объект. Каждый раз, когда вызывается метод scrape
этого объекта, запускается блок кода. Теоретически вы можете делать там все, что захотите, но единственными разумными вещами являются вызовы process
и result
.
process
принимает три (или более) аргумента. Первый аргумент - это селектор CSS (или XPath, если он начинается с //
или id(
). В этом случае ([data-reader]
) мы выбираем все элементы, которые имеют атрибут data-reader
.
Остальные аргументы являются парами ключ / значение. scraper
предоставляет неявный контекст (также известный как «stash»), который представляет собой просто хэш, в который помещаются результаты. Аргумент «ключ» указывает, под каким хэш-ключом следует поместить результаты извлечения. Если аргумент «ключ» заканчивается на []
, он удаляется, и значение является не единственным результатом, а ссылкой на массив результатов.
Здесь мы используем list[]
в качестве аргумента «ключ», что означает, что мы накапливаем результаты под ключом list
тайника.
Аргумент "value" указывает, какое значение мы хотим сохранить под нашим ключом. Возможные значения включают TEXT
(текстовое значение узла) и @foo
(значение атрибута foo
рассматриваемого элемента).
Здесь мы используем ссылку на хеш, что означает, что мы хотим создать вложенный хеш. Каждая пара ключ / значение нашего хэша интерпретируется как описано выше. Мы получаем записи для tag
(содержащие имя тега, возвращаемое методом tag
) и content
(содержащие текстовое значение нашего элемента).
Эффект такой, как если бы scrape
содержал следующий цикл:
my %stash;
for my $node (@found_nodes) {
push @{$stash{list}}, {
tag => $node->tag,
content => get_plain_text_somehow($node),
};
}
Обычно scrape
возвращает тайник, но если блок очистки содержит result
(который должен быть последним оператором в блоке), вы можете сделать так, чтобы он возвращал только один ключ (или если вы передаете несколько строк result
, хэш, содержащий только подмножество ключей). То есть из-за result 'list'
вместо
return \%stash;
мы эффективно получаем
return $stash{list};