提高LWP::Simple Perl性能

3

哎呀,我又有一个问题:

我的任务是阅读网页并从该页面中提取链接(使用HTML::TokeParser很容易)。然后,他(我的老板)坚持要求我从这些链接中阅读并获取每个页面的一些详细信息,并将所有信息解析为XML文件,以便以后可以阅读。

因此,我可以简单地设置如下:

#!/usr/bin/perl -w

use     strict;
use     LWP::Simple; 
require HTML::TokeParser; 

$|=1;                        # un buffer

my $base = 'http://www.something_interesting/';
my $path = 'http://www.something_interesting/Default.aspx';
my $rawHTML = get($path); # attempt to d/l the page to mem

my $p = HTML::TokeParser->new(\$rawHTML) || die "Can't open: $!";

open (my $out, "> output.xml") or die;

while (my $token = $p->get_tag("a")) {

    my $url = $token->[1]{href} || "-";

    if ($url =~ /event\.aspx\?eventid=(\d+)/) {
        ( my $event_id = $url ) =~ s/event\.aspx\?eventid=(\d+)/$1/;
        my $text = $p->get_trimmed_text("/a");
        print $out $event_id,"\n";
        print $out $text,"\n";

        my $details = $base.$url;
        my $contents = get($details);

        # now set up another HTML::TokeParser, and parse each of those files.

    }
}

这个方法在页面上大约只有5个链接的情况下可能还可以,但是我正在尝试从 ~600 个链接中读取信息,并从每个页面获取信息。因此,我的方法需要花费很长时间... 我实在不知道需要多久才能完成,因为我从来没有让它完成过。
我的想法是只在需要时获取信息(例如,通过查找链接的 Java 应用程序获取信息),但是这种方法似乎不可行,所以我转向你们寻求帮助 :)
有没有办法改进这个过程呢?
6个回答

5
你可以尝试并行执行get()而不是按顺序执行,虽然代码会变得更加复杂,但你可能会看到速度提升。 Parallel::ForkManager 是我会推荐的起点(它甚至在文档中包含了一个LWP::Simple get() 的例子),但在CPAN上还有很多其他选择,其中包括相当陈旧的LWP::Parallel::UserAgent

这正是我所寻找的,谢谢。其他答案也很有用。感谢大家的帮助 :) - Aelfhere
@Aelfhere,你在删除问题之前,我正要发布一个ForkManager的解决方案。 - ikegami

4
如果你想从服务器获取多个项目并快速完成,可以使用TCP Keep-Alive。放弃简单的 LWP::Simple ,改用带有keep_alive选项的常规LWP::UserAgent 。这将设置一个连接缓存,因此当从同一主机获取更多页面时,您不会产生TCP连接建立开销。
use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common;

my @urls = @ARGV or die 'URLs!';
my %opts = ( keep_alive => 10 ); # cache 10 connections
my $ua = LWP::UserAgent->new( %opts );
for ( @urls ) {
        my $req = HEAD $_;
        print $req->as_string;
        my $rsp = $ua->request( $req );
        print $rsp->as_string;
}

my $cache = $ua->conn_cache;
my @conns = $cache->get_connections;
# has methods of Net::HTTP, IO::Socket::INET, IO::Socket

2
WWW::Mechanize是一个很棒的开端,如果你正在寻找模块,我还建议使用Web::Scraper。这两个都有我提供的链接文档,应该能帮助你快速上手。”

0
use strict;
use warnings;

use threads;  # or: use forks;

use Thread::Queue qw( );

use constant MAX_WORKERS => 10;

my $request_q  = Thread::Queue->new();
my $response_q = Thread::Queue->new();

# Create the workers.
my @workers;
for (1..MAX_WORKERS) {
   push @workers, async {
      while (my $url = $request_q->dequeue()) {
         $response_q->enqueue(process_request($url));
      }
   };
}

# Submit work to workers.
$request_q->enqueue(@urls);

# Signal the workers they are done.    
for (1..@workers) {
   $request_q->enqueue(undef);
}

# Wait for the workers to finish.
$_->join() for @workers;

# Collect the results.
while (my $item = $response_q->dequeue()) {
   process_response($item);
}

0

你的问题是爬虫比I/O密集型更需要CPU。虽然大多数人会建议你使用更多的CPU,但我将尝试展示Perl作为“粘合语言”的巨大优势。 众所周知Libxml2是一款出色的XML/HTML解析器。同样,libcurl也是一个很棒的下载代理。 然而,在Perl的世界里,许多爬虫都基于LWP::UserAgent和HTML::TreeBuilder::XPath(类似于HTML::TokeParser,同时又符合XPath标准)。 在这种情况下,您可以使用替换模块来处理通过libcurl/libxml2进行的下载和HTML解析:

use LWP::Protocol::Net::Curl;
use HTML::TreeBuilder::LibXML;
HTML::TreeBuilder::LibXML->replace_original();

我曾经在几个我用来维护的爬虫中添加了这3行代码,平均速度提高了5倍。但是,由于您正在使用HTML::TokeParser,我建议您尝试使用Web::Scraper::LibXML代替(再加上LWP::Protocol::Net::Curl,这会影响到LWP::Simple和Web::Scraper)。

0

很有可能是由于等待网络响应时阻塞了http get请求。使用异步http库,看看是否有帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接