Perl,如何从url并行获取数据?
我需要从许多Web数据提供程序中获取一些数据,这些数据提供程序不公开任何服务,因此我必须编写类似这样的内容,例如使用WWW :: Mechanize:
use WWW::Mechanize; @urls = ('http://www.first.data.provider.com','http://www.second.data.provider.com','http://www.third.data.provider.com'); %results = {}; foreach my $url (@urls) { $mech = WWW::Mechanize->new(); $mech->get($url); $mech->form_number(1); $mech->set_fields('user' => 'myuser',pass => 'mypass'); $resp = $mech->submit(); $results{$url} = parse($resp->content()); } consume(%results); 是否有一些(可能是简单的;-)方式将数据提取到一个共同的%结果变量,同时,即:并行地,来自所有提供者? 解决方法
Perl中应避免使用线程.使用线程主要是为了
在Windows上模拟UNIX样式的fork;除此之外,它毫无意义. (如果你关心,实现使这个事实非常清楚.在perl中, 如果你想在同一个过程中同时做很多事情,那么 AnyEvent(和EV,事件, 这种方法的缺点是你必须重写你的 所以,因为我们不想重写 考虑到这一点,让我们看一些代码.这是一个开始的程序 我们将从通常的Perl脚本样板开始: #!/usr/bin/env perl use strict; use warnings; 然后我们将加载Coro特定的模块: use Coro; use Coro::LWP; use EV; Coro在幕后使用事件循环;它会为你挑选一个 然后我们将加载我们工作所需的模块,这只是: use WWW::Mechanize; 现在我们准备编写我们的程序了.首先,我们需要一个URL列表: my @urls = ( 'http://www.google.com/','http://www.jrock.us/','http://stackoverflow.com/',); 然后我们需要一个函数来生成一个线程并完成我们的工作.做一个 sub start_thread($) { my $url = shift; return async { say "Starting $url"; my $mech = WWW::Mechanize->new; $mech->get($url); printf "Done with $url,%d bytesn",length $mech->content; }; } 所以这是我们计划的核心.我们只是提出正常的LWP计划 现在我们只需要启动线程: start_thread $_ for @urls; 最后,我们想要开始处理事件: EV::loop; 就是这样.当你运行它时,你会看到一些输出,如: Starting http://www.google.com/ Starting http://www.jrock.us/ Starting http://stackoverflow.com/ Done with http://www.jrock.us/,5456 bytes Done with http://www.google.com/,9802 bytes Done with http://stackoverflow.com/,194555 bytes 正如您所看到的,请求是并行进行的,而您没有 更新 您在原始帖子中提到要限制并行运行的HTTP请求数.一种方法是使用信号量, 信号量就像一个计数器.当您想使用信号量保护的资源时,您可以“降低”信号量.这会减少计数器并继续运行程序.但是如果当你试图降低信号量时计数器为零,你的线程/协同程序将进入睡眠状态,直到它不为零.当计数再次上升时,您的线程将在信号量下唤醒并继续.最后,当您使用信号量保护的资源时,您“上调”信号量并为其他线程提供运行的机会. 这使您可以控制对共享资源的访问,例如“发出HTTP请求”. 您需要做的就是创建HTTP请求线程将共享的信号量: my $sem = Coro::Semaphore->new(5); 5意味着“让我们在阻止之前’向下’调用’5次”,换句话说,“让5个并发的HTTP请求”. 在我们添加任何代码之前,让我们谈谈可能出现的问题.可能发生的一件坏事是一个线程“向下” – 信号量,但从来没有“向上” – 当它完成时它.然后没有什么可以使用那个资源,你的程序可能最终什么都不做.有很多方法可以实现.如果你写了一些代码,比如$sem-> down;做一点事; $sem-> up,你可能觉得安全,但是如果“做某事”会引发异常怎么办?那么信号量就会被遗忘,这很糟糕. 幸运的是,Perl可以很容易地拥有范围Guard对象,当持有对象的变量超出范围时,它将自动运行代码.我们可以将代码设置为$sem-> up,然后我们将永远不必担心在我们不打算时持有资源. Coro :: Semaphore集成了守卫的概念,这意味着你可以说我的$guard = $sem->守卫,并且当控制器离开你叫守卫的范围时,它会自动向下移动信号量并向上移动. 考虑到这一点,我们要做的就是限制并行请求的数量是保护我们使用HTTP的协同程序顶部的信号量: async { say "Waiting for semaphore"; my $guard = $sem->guard; say "Starting"; ...; return result; } 处理意见: 如果您不希望您的程序永远存在,那么有一些选择.一种是在另一个线程中运行事件循环,然后在每个工作线程上加入.这使您可以将结果从线程传递到主程序: async { EV::loop }; # start all threads my @running = map { start_thread $_ } @urls; # wait for each one to return my @results = map { $_->join } @running; for my $result (@results) { say $result->[0],': ',$result->[1]; } 您的线程可以返回如下结果: sub start_thread($) { return async { ...; return [$url,length $mech->content]; } } 这是在数据结构中收集所有结果的一种方法.如果您不想返回内容,请记住所有协同程序共享状态.所以你可以把: my %results; 在程序的顶部,让每个协同程序更新结果: async { ...; $results{$url} = 'whatever'; }; 当所有协程都运行完毕后,您的哈希将填充结果.但是,您必须加入每个协程才能知道答案何时准备就绪. 最后,如果您将此作为Web服务的一部分,您应该使用一个像coroine一样的Web服务器,如Corona.这将在协程中运行每个HTTP请求,允许您并行处理多个HTTP请求,此外还有能够并行发送HTTP请求.这将很好地利用内存,CPU和网络资源,并且很容易维护! (你基本上可以将我们的程序从上面切换到处理HTTP请求的协同程序;可以创建新的协同程序并在协程内加入.) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |