有效的方法来制作成千上万的卷曲请求

时间:2015-01-16 00:29:41

标签: perl curl cookies

我正在使用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的新手,因为这是我的第一个程序。

2 个答案:

答案 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来对任何数据进行后处理,例如对其进行排序,计数,等等。

我还要指出 - 使用curlgrep作为系统调用并不理想 - 我已按原样离开,但建议查看{{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;