【问题标题】:improving LWP::Simple perl performance提高 LWP::Simple perl 性能
【发布时间】:2011-09-22 08:38:31
【问题描述】:

唉,我还有一个问题:

我的任务是阅读网页并从该网页中提取链接(使用 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 应用程序).​​.....但是,这似乎是不可接受的,所以我转向你们:)

有什么方法可以改进这个过程吗?

【问题讨论】:

    标签: perl html-parsing


    【解决方案1】:

    如果您并行执行 get()s 而不是顺序执行,您可能会看到速度提升 - 以更少的简单代码为代价。

    Parallel::ForkManager 是我要开始的地方(甚至在其文档中包括一个 LWP::Simple get() 示例),但在CPAN 上可以找到许多其他替代方案,包括相当过时的@987654323 @。

    【讨论】:

    • 这正是我想要的,谢谢。其他答案也很有用。谢谢大家的帮助:)
    • @Aelfhere,我打算在你删除之前发布一个解决你的 ForkManager 问题的方法。
    【解决方案2】:

    如果您想从服务器获取多个项目并快速完成,请使用 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
    

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:
          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);
          }
          

          【讨论】:

            【解决方案6】:

            您的问题是报废比 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)。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-06-08
              • 1970-01-01
              相关资源
              最近更新 更多