我正在使用CURL来发出数千个请求。在我的代码中,我将cookie设置为特定值,然后读入页面上的值。这是我的 Perl 代码:
#!/usr/bin/perl
my $site = "http://SITENAME/?id=";
my $cookie_name = "cookienum123";
print $fh "#\t\tValue\n";
for my $i ('1'..'10000') {
my $output = `curl -s -H "Cookie: $cookie_name=$i" -L $site$i | grep -Eo "[0-9]+"`;
print "$i\t\t$output\n";
}
所以从1到10000,我将cookienum123设置为该值并从页面读取整个响应。然后我使用grep来提取#。我现在的代码工作正常,但我想知道是否有更快或更有效的方法我可以做到这一点。
请注意,这不必作为Perl脚本完成(我也可以使用Windows批处理文件,Unix shell脚本等)。
编辑1月18日:使用笔记添加了赏金"所需的答案应该包括Perl中同时运行数千个卷曲请求的方式,但它需要比当前运行的速率更快地运行。它必须在最后将输出写入单个文件,但顺序无关紧要。"下面的一些评论提到了 fork ,但我不知道如何将它应用到我的代码中。我是Perl的新手,因为这是我的第一个程序。
答案 0 :(得分:1)
这里有一个令人尴尬的并行问题。这些非常适合并行化,因为不需要线程间依赖或通信。
在perl中执行此操作有两种主要方式 - 线程或分叉。我会通常为你正在做的事情建议基于线程的并行处理。这是一个选择问题,但我认为它更适合整理信息。
#!/usr/bin/perl
use strict;
use warnings;
use threads;
use Thread::Queue;
my $numthreads = 20;
my $site = "http://SITENAME/?id=";
my $cookie_name = "cookienum123";
my $fetch_q = Thread::Queue->new();
my $collate_q = Thread::Queue->new();
#fetch sub sits in a loop, takes items off 'fetch_q' and runs curl.
sub fetch {
while ( my $target = $fetch_q->dequeue() ) {
my $output =
`curl -s -H "Cookie: $cookie_name=$target" -L $site$target | grep -Eo "[0-9]+"`;
$collate_q->enqueue($output);
}
}
#one instance of collate, which exists to serialise the output from fetch.
#writing files concurrently can get very messy and build in race conditions.
sub collate {
open( my $output_fh, ">", "results.txt" ) or die $!;
print {$output_fh} "#\t\tValue\n";
while ( my $result = $collate_q->dequeue() ) {
print {$output_fh} $result;
}
close($output_fh);
}
## main bit:
#start worker threads
my @workers = map { threads->create( \&fetch ) } 1 .. $numthreads;
#collates results.
my $collater = threads->create( \&collate );
$fetch_q->enqueue( '1' .. '10000' );
$fetch_q->end();
foreach my $thr (@workers) {
$thr->join();
}
#end collate_q here, because we know all the fetchers are
#joined - so no more results will be generated.
#queue will then generate 'undef' when it's empty, and the thread will exit.
$collate_q->end;
#join will block until thread has exited, e.g. all results in the queue
#have been 'processed'.
$collater->join;
这将生成20个工作线程,它们将并行运行,并在退出文件时收集结果。作为替代方案,您可以使用Parallel::ForkManager
执行类似的操作,但对于面向数据的任务,我个人更喜欢线程。
您可以使用'整理' sub来对任何数据进行后处理,例如对其进行排序,计数,等等。
我还要指出 - 使用curl
和grep
作为系统调用并不理想 - 我已按原样离开,但建议查看{{1}并允许perl处理文本处理,因为它非常擅长。
答案 1 :(得分:1)
我很确定以下内容会做你想要的,但是同时请求10000个服务器的服务器并不是很有礼貌。实际上,通过遍历给定URL的id来获取站点的数据也听起来不是很友好。我没有测试过以下内容,但它应该让你99%的方式(可能是某处的语法/用法错误)。
有关详细信息,请参阅:
#!/usr/bin/perl
use warnings;
use strict;
use Mojo::UserAgent;
use Mojo::IOLoop;
my $site = 'http://SITENAME/?id=';
my $cookie_name = 'cookienum123';
#open filehandle and write file header
open my $output_fh, q{>}, 'results.txt'
or die $!;
print {$output_fh} "#\t\tValue\n";
# Use Mojo::UserAgent for concurrent non-blocking requests
my $ua = Mojo::UserAgent->new;
#create your requests
for my $i (1..10000) {
#build transaction
my $tx = $ua->build_tx(GET => "$site$i");
#add cookie header
$tx->req->cookies({name => $cookie_name, value => $i});
#start "GET" with callback to write to file
$tx = $ua->start( $tx => sub {
my ($ua, $mojo) = @_;
print {$output_fh} $i . "\t\t" . $mojo->res->dom->to_string;
});
}
# Start event loop if necessary
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
#close filehandle
close $output_fh;