使用Web :: Scraper提取带有数据标记的HTML标签

时间:2018-11-02 23:49:52

标签: perl web-scraping

此代码的目的是解析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 :(得分:4)

#!/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 documentation总体上参差不齐,并且
  2. 它在at least one example中使用,

所以我对使用它并不感到难过。

我正在将$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方法时,都会运行代码块。从理论上讲,您可以在该处执行任何操作,但唯一明智的操作是调用processresult

process接受三个(或更多)参数。第一个参数是CSS(或以//id(开头的XPath)选择器。在这种情况下([data-reader],我们将选择所有具有data-reader属性的元素。

其余参数为键/值对。 scraper提供了一个隐式上下文(也称为“存储”),它只是放置结果的哈希。 “ key”参数指定提取结果应放在哪个哈希键下。如果“键”自变量以[]结尾,则将其删除并且该值不是单个结果,而是对结果数组的引用。

在这里,我们将list[]用作“ key”参数,这意味着我们将在隐藏的list键下累积结果。

“ value”参数指定我们要在密钥下存储的值。可能的值包括TEXT(节点的文本值)和@foo(所讨论元素的foo属性的值)。

这里我们使用的是哈希引用,这意味着我们要构造一个嵌套的subhash。哈希的每个键/值对均如上所述进行解释。我们获得了tag(包含由the tag method返回的标签名)和content(包含元素的文本值)的条目。

效果就像scrape包含以下循环:

my %stash;
for my $node (@found_nodes) {
    push @{$stash{list}}, {
        tag     => $node->tag,
        content => get_plain_text_somehow($node),
    };
}

通常scrape返回存储,但是如果scrape块包含result(必须是该块中的最后一条语句),则可以使它仅返回一个键(或者如果您通过result的多个字符串(仅包含键子集的哈希)。也就是说,由于result 'list',而不是

return \%stash;

我们有效地获得了

return $stash{list};