Использование Web :: Scraper для извлечения тегов HTML с маркером данных - PullRequest
0 голосов
/ 03 ноября 2018

Цель этого кода - проанализировать файл HTML и вернуть содержимое, обернутое тегами с атрибутом data-reader.

Это работает как нужно, но я также хотел бы получить связанный HTML-тег, но я не знаю, как вернуть его в данных скраба.

Возможно ли это?

#!/usr/bin/perl -T

use strict;
use warnings;

use Web::Scraper;

my $html = do { local $/; <DATA> };

my $s = scraper {
  process '*', 'links[]' => '@data-reader';
  process '*', 'content[]' => 'text';
};

my $res = $s->scrape($html);

for my $i (0 .. @{ $res->{links} } ) {
  if ($res->{links}[$i]) {
    print "<??>$res->{content}[$i]</??>\n";
  }
}
exit;

__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>

Выход:

<??>Hello world</??>
<??>Paragraph Heading</??>

1 Ответ

0 голосов
/ 03 ноября 2018
#!/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>

Способность передать необработанную подпрограмму как спецификацию экстрактора, кажется недокументированной, но

  1. документация Web :: Scraper в целом пятнистая, а
  2. используется в хотя бы в одном примере ,

так что я не расстраиваюсь из-за его использования.

Я перекодирую $part->{content} как HTML, чтобы избежать проблем в случае, если кто-то, например,

<div data-reader="on">&lt;script&gt;alert(42)&lt;/script&gt;</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};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...