在Perl中循环动态关联数组?

时间:2013-09-27 13:47:34

标签: html arrays perl

我正在处理一些html文档,每个文档都有一个链接列表,每个链接在打开时都有另一个链接列表,但是,每个链接列表可能都有来自另一个列表的链接,而且我只有一个哈希数组在开头my %list = ($link1 => 1);中的一个链接,我到目前为止所做的是打开现有链接并循环遍历html文档中的所有链接(以下代码显示已解析链接的html内容的变量$ tree):

for my $node ( $tree->look_down('_tag' => 'a'))
{
next unless $node;

my $link = $node->attr('href');
unless(exists($list{$link}))
{
    $list{$link} = 1;
}
}       

现在我要做的是循环遍历哈希表,因为每次出现新链接时它都会被提供。

我希望我一直很清楚。

编辑:

This是我从中获取链接的页面,当从列表中选择一个链接时,有时会在另一个列表中找不到链接,所以要确保我拥有来自不同列表的所有链接列表,i打开每个链接并循环遍历列表,当有新链接时我将其添加到哈希数组中。

更清楚的是,我的算法如下:

  • 我有一个带有一个链接my %list = ($link1 => 1);的哈希表,值1意味着该链接尚未打开,因此,我仍然没有检查它所拥有的链接列表。
  • 一旦我获得链接列表,我循环遍历它并检查哈希数组是否没有我正在循环的链接之一
  • 一旦我完成了上面的列表循环,我打开的第一个链接将更新为2 $list{$link}=2并传递到哈希表中的以下链接(请记住,每次都要提供哈希数组)找到一个新链接)

提前致谢

1 个答案:

答案 0 :(得分:3)

您似乎想要构建一个爬虫,并避免两次访问任何链接。因此,您要将访问过的链接添加到哈希。这是对的吗?

您的问题似乎是在您实际访问它们之前将新链接添加到所看到的链接,因此很难再次找到它们。在这种情况下,哈希是错误的数据结构。使用哈希来注册看到的链接(为了避免重复),但是使用数组作为未访问链接的队列:

my @queue = ('http://www.example.com/');     # start with at least one link in the queue
my %seen = ('http://www.example.com/' => 1); # this link is "known"

# look at the next link in the queue
while (defined(my $url = shift @queue)) {
  my $tree = some_magic($url);

  # 1. extract all <a>s
  # 2. extract the href value, skipping <a>s that don't have a href
  # 3. add them to the queue if not yet seen
  for my $link ($tree->look_down(...)) {
    my $href = $link->attr('href');
    next unless length $href;
    next if $seen{$href};

    $seen{$href} = 1;
    push @queue, $href;
  }
}

内部循环也可以写成

push @queue,
  grep { length($_) and not $seen{$_}++ }
  map  { $_->('href') }
  $tree->look_down(...);

还有一些问题:

  • 有些网址可能看起来不同,但相同。例如。空格通常编码为+%20。规范化URL以避免这种情况。
  • 网址可以包含未传输到服务器的片段部分#foo。如果您已经看过URL,则应该在测试之前删除该片段。
  • 用请求锤击服务器是不礼貌的。每次请求之间的sleep几秒钟就更好了。
  • 互联网是一个广阔的地方。您应该限制搜索的深度,并将其与@queue中的URL一起记录:

    my @queue = (['http://www.example.com/', 5]); # start with depth 5
    ...
    while(my $item = shift @queue) {
      my ($url, $depth) = @$item;
      ...
      $seen{$href} = 1;
      next if $depth <= 0;
      push @queue, [$href, $depth-1];
      ...
    }