改进LWP ::简单的perl性能

时间:2011-06-24 21:06:45

标签: perl html-parsing

唉,我还有一个问题:

我的任务是阅读网页并从该页面中提取链接(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 个答案:

答案 0 :(得分:5)

如果你并行执行get()而不是顺序执行get(),你可能会看到速度提升 - 代价是不那么简单的代码。

Parallel::ForkManager是我要开始的地方(甚至在其文档中包含一个LWP :: Simple {{1}}示例),但在CPAN上还有很多其他选择,包括相当陈旧的LWP::Parallel::UserAgent

答案 1 :(得分:4)

如果要从服务器获取多个项目并快速执行此操作,请使用TCP Keep-Alive。放弃简单化的LWP::Simple并使用常规LWP::UserAgentkeep_alive选项。这将设置连接缓存,因此当从同一主机获取更多页面时,不会产生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 :(得分:2)

WWW::Mechanize是一项伟大的工作,如果您正在查看模块,我还建议Web::Scraper

我们提供的链接都有文档,可以帮助您快速上手。

答案 3 :(得分:0)

在等待来自网络的响应时,很有可能阻止http get请求。使用asynchronous http library并查看是否有帮助。

答案 4 :(得分: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);
}

答案 5 :(得分:0)

您的问题是报废比I / O密集型更耗费CPU。虽然这里的大多数人会建议你使用更多的CPU,但我会尝试展示Perl被用作“粘合”语言的巨大优势。 每个人都同意Libxml2是一个优秀的XML / HTML解析器。此外,libcurl是一个很棒的下载代理。 但是,在Perl Universe中,许多scraper都基于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();

我看到平均5倍的速度增加只是通过在我曾经维护过的几个刮刀中加上这3条线。 但是,当您使用HTML :: TokeParser时,我建议您尝试使用Web :: Scraper :: LibXML(加上LWP :: Protocol :: Net :: Curl,它会影响LWP :: Simple Web :: Scraper)。

相关问题