打印多个HTML文件

时间:2013-02-04 10:15:00

标签: perl html-parsing lwp

我对Perl比较陌生。我有一个URL列表,我想从中提取文本并将其打印在不同的文件中。以下是我的代码示例:

#!/usr/bin/perl -w

use strict;
use locale;
use warnings;
#use diagnostics;
use utf8;

binmode(STDIN, "encoding(utf8)");
binmode(STDOUT, "encoding(utf8)");
binmode(STDERR, "encoding(utf8)");

use LWP::Simple;
use HTML::Parse;

open (CLEANURL, '<:utf8', "clean_keyword_url_5.3.txt")  || die ("Cannot open File\n");
open(STORECODE, '>:utf8', "Bstored_keyword_url_5.3.txt")  || die ("Cannot open File\n");

my $url2parse;
my @arg = <CLEANURL>;
close (CLEANURL);

foreach my $arg(@arg) {
    $url2parse = parse_html(get($arg))->format;
    print STORECODE $url2parse;
}

close (STORECODE);
<{1>} clean_keyword_url_5.3.txt我有以下链接:

http://www.ladepeche.fr/article/2013/01/31/1548850-aulon-l-activite-est-paralysee.html#xtor=RSS-6

http://tdg.ch/monde/faits-divers/Deux-alpinistes-meurent-dans-une-avalanche-en-Isere/story/10446351

所以主要是法国或瑞士当地报纸。我想在一个单独的文件夹中打印每个链接,我尝试使用文件句柄数组并使用LWP::Simple的“getstore”方法执行此操作,但是我无法在所有链接。它会创建所有文件,但只打印每个文件中的一个URL的内容。我找不到有关在数组上运行LWP::Simple的任何信息,似乎每个人都只使用这个模块中的一个或两个URL。

我也有一个哈希的想法,如下所示:

#!/usr/bin/perl -w

use strict;
use locale;
use warnings;
#use diagnostics;
use utf8;

binmode(STDIN, "encoding(utf8)");
binmode(STDOUT, "encoding(utf8)");
binmode(STDERR, "encoding(utf8)");

use LWP::Simple;
use HTML::Parse;

open (CLEANURL, '<:utf8', "clean_keyword_url_5.3.txt") || die ("Cannot open File\n");
#open(STORECODE, '>:utf8', "Bstored_keyword_url_5.3.html")  || die ("Cannot open File\n");

my $url2parse;
my @arg = <CLEANURL>;
close (CLEANURL);

my @filehandles;
my $i;

for ($i = 0; $i<@arg; $i++){
    local *FILE;
    open (FILE, '>:utf8', "Bstored_keyword_url_5.3.$i.html")|| die;
    push (@filehandles, *FILE);
}

foreach my $arg(@arg) {
    $url2parse = parse_html(get($arg))->format;
    foreach my $file(@filehandles){
       my %hash = {key => $file};
       $hash{key} .= $val;
       print $file "$hash{key}";    
    }
}

#close (STORECODE);

您可能会注意到此代码无效。问题在于我无法解开它。

所以,如果您有任何想法,那将非常有帮助。 谢谢!!!

3 个答案:

答案 0 :(得分:0)

(见最后的更新1)

一般评论:

在编写Perl(严格,警告和显式编码)时,很高兴看到您正在尝试使用最佳实践。如果使用locale是好的可以辩论....有三件事你可以做得更好:

  1. 在最可能的范围内声明变量。
  2. 使用词法文件句柄
  3. 检查错误
  4. 您的第一个脚本

    • 将文件句柄打开为词法变量被认为是最佳做法:open my $fh, "<", $filename or die $!或其他。 $!包含失败的原因。将它包含在您的输出中,因为它包含对您的打开失败的实际共鸣的宝贵提示(缺少权利,没有此类路径等)。
    • 从您的文件中读取时,网址仍然有一个尾随换行符。请chomp @arg删除它们,或s/\s+$// for @arg;删除任何尾随空格字符。
    • LWP::Simple可能会失败。你应该检查一下你是否真的得到了答复:

      my $content = get $url;
      die "Didn't receive anything from $url" unless defined $content;
      

      我更愿意使用LWP::UserAgent,因为这样可以进行更多的错误检查:

      my $ua = LWP::UserAgent->new;
      ...;
      my $response = $ua->get($url);
      
      die "Request for $url failed: " . $response->status_line unless $response->is_sucess
      
      my $content = $response->decoded_content;
      
    • 解析HTML也是如此。查找模块文档,检查错误。

    第二个脚本

    第二个脚本的所有内容都相同。另外:

    • 在C风格的for循环中,迭代范围从o到@arg的最大索引。此外,变量$i仅在循环内使用,因此我们在此处声明:

      for my $i (0 .. $#arg) { ... }
      

      $# sigil给出了数组的最高索引。 ..是列表上下文中的范围运算符。即使你不想使用范围,你也可以将你的var声明为for(my $i = 0; $i < @arg; $i++) { ... }

    • local函数采用全局变量的名称,将其备份到特殊堆栈中,并允许您临时为该名称分配新值。一旦保留当前范围,旧的变量就会恢复(范围大致由花括号分隔)。不要使用它,除非你真的被迫(阅读“应对范围”)。请注意,barewords,typeglobs,typeglobs和filehandles的引用不是一回事。只需将文件句柄打开为词法变量,不要使用typeglobs,并且一些错误可能会消失。

      my @filehandles;
      for my $i (0 .. $#arg) {
        my $filename = "Bstored_keyword_url_5.3.$i.html";
        open my $fh, "<:utf8", $filename or die "Can't open $filename: $!";
        push @filehandles, $fh;
        # lexical filehandles are automatically closed once their reference count hits zero.
      }
      

      或者,有点高级,我们可以看到整数和文件句柄之间存在直接映射,并将数组表示为此映射。完全等同于上述:

      # how map works: OUTPUT = map { BLOCK } INPUT-LIST
      my @filehandles = map {
        # current value is in $_
        my $filename = "Bstored_keyword_url_5.3.$_.html";
        open my $fh, "<:utf8", $filename or die "Can't open $filename: $!";
        $fh; # last statement determines what is put into the array.
      } 0 .. $#arg;
      
    • 哈希很好。请注意,哈希值构建为偶数值列表。 {}构造匿名哈希引用,就像[]不构造数组一样,但是匿名数组引用。引用始终保留在标量中:

      my $hashref = { foo => 1, bar => 3 };
      say $hashref->{foo}; # dereference arrow needed
      

      my %hash = ( foo => 1, bar => 3 );
      say $hash{foo};
      

      parens只是为了排除优先级,他们不“创建”列表或数组。

    • 正如我刚刚使用的那样:say函数在perl 5.10或更高版本中可用。您可以使用use feature 'say'use 5.010(或更高版本号)激活它。它的工作原理与print完全相同,但会将输出分隔符$\(通常是换行符)附加到输出中。

    • 在最后一个循环中,您使用变量$val。那是从哪里来的?另外,不要将字符串附加到文件句柄。这将文件句柄字符串化,它(a)使其无法使用,(b)提供相当无用的字符串(可能是IO::File=GLOB(0xdeadbeef))。另外,如果只将它用于一个(!)键,请不要使用哈希。

    链接

    1. LWP::Simple documentation
    2. LWP::UserAgent documentation
    3. HTML::Parse表示已弃用。请参阅HTML::ParserHTML::TreeBuilder
    4. Coping with Scoping:为什么你永远不应该使用全局或“本地”变量。
    5. 更新1

      你的新代码也相当不错(我看到更多人从一开始就使用strict ...)。除非您忘记将任何内容放入$url2parse,并且您发现了一些用于循环的创造性用途; - )

      现在使用getstoremirror或其所谓的内容是一个好主意。这意味着我们不必手动打开文件句柄。

      这可以编码:

      ...;
      # ↓ lexical filehandles 'n stuff
      open my $CLEANURL, "<:utf8", ... or die ...;
      my $counter = 0;
      while(my $link = <$CLEANURL>) {
        chomp $link; # remove evil newlines
        my $status = mirror($link => "Cstored_keyword_url_5.3.$counter.txt");
        200 == $status or warn "WARNING: fetching $link failed with status $status";
        $counter++;
      }
      

      这只是一个循环而不是三个循环。当我使用mirror中的LWP::Simple函数(getstore也可以)时,我只需要传递一个文件名,而不关心文件句柄。

      while (my $line = <$filehandle>) { ... }通常用于Perl中逐行读取文件。在像这样的小程序上,这是无关紧要的,但是当你的数据扩展时它是一个好习惯......

      在手动代码中,上面的内容可能如下:

      ...;
      my $counter = 0;
      while (my $link = <$CLEANURL>) {
        chomp $link;
        my $filename = "Cstored_keyword_url_5.3.$counter.txt";
        open my $fh, ">:utf8", $filename or die "Couldn't open $filename: $!";
        my $content = get $link;
        if (defined $content) { print $fh $content }
        else                  { warn "WARNING: failed to fetch $link" }
        # $fh autocloses here
        $counter++;
      }
      

      我仍然赞成使用LWP::UserAgent,因为这可以为失败提供更多洞察力。

      一旦你能拿到&amp;正确处理您的URL,并行性可能是加快速度的下一步。

答案 1 :(得分:0)

感谢您的快速回答,这非常准确。它有助于真正理解我的脚本问题是什么。我的代码现在看起来像这样:

#!/usr/bin/perl -w

use strict;
use locale;
use warnings;
#use diagnostics;
use utf8;

binmode(STDIN, "encoding(utf8)");
binmode(STDOUT, "encoding(utf8)");
binmode(STDERR, "encoding(utf8)");

use LWP::Simple;
use HTML::Parse;

open (CLEANURL, '<:utf8', "clean_keyword_url_5.3.txt")  || die ("Cannot open File\n");
#open(STORECODE, '>:utf8', "Bstored_keyword_url_5.3.txt")  || die ("Cannot open File\n");

my $url2parse;
my @arg = <CLEANURL>;
close (CLEANURL);

my @filehandles;
my $fh;
for my $i (0 .. $#arg) {
    my $filename = "Cstored_keyword_url_5.3.$i.txt";
    open $fh, ">:utf8", $filename or die "Can't open $filename: $!";
    push @filehandles, $fh;
    foreach my $arg(@arg) {
        $url2parse = get($arg);
        foreach $arg($url2parse){
            print $fh $url2parse;
        }   
    }
}



   #close (STORECODE);

我的问题发生在LWP :: Simple模块的get方法中。 LWP :: UserAgent也是如此(虽然我将把它用于错误重用)。所有链接都存储在同一个变量中:$url2parse。到目前为止,代码将为链接创建文件,但它只会在一个$arg(链接)上循环。我必须找到一种方法来在不同的链接中分离$ url2parse。使用getstore方法(看起来像这样:$url2parse = gestore($arg, $filename))也只会在一个$arg上循环。

再次感谢您的回答,这对于理解perl语法非常有用,但是如果您对如何处理这个$url2parse有任何线索,那就太棒了......我我也在努力,所以如果我在发布之前得到答案。

答案 2 :(得分:0)

感谢amon,这是更新后的代码:

#!/usr/bin/perl -w

use strict;
use locale;
use warnings;
#use diagnostics;
use utf8;

binmode(STDIN, "encoding(utf8)");
binmode(STDOUT, "encoding(utf8)");
binmode(STDERR, "encoding(utf8)");

use LWP::Simple;
use HTML::Parse;

open (CLEANURL, '<:utf8', "clean_keyword_url_5.3.txt")  || die ("Cannot open File\n");
open (STORECODE, '>:utf8', "all_articles.txt")  || die ("Cannot open File\n");

my $counter = 0;

while(my $link = <CLEANURL>){
    chomp ($link);
    my $filename = "cstored_keyword_url_5.3.$counter.txt";
    open (my $fh, ">:utf8", $filename) || die "Couldn't open $filename: $!";
    my $content = get($link);
    unless (defined $link){
        warn "WARNING: Failed to fetch $link";
    }
    $content = parse_html($content) || die "Couldn't parse $content";
    my $text = $content->format;
    if (defined $text){
       print $fh $text;
       print STORECODE $text;
    }else{
       warn "WARNING: Failed to fetch $link";
    }
    $counter++; 
}

close (CLEANURL);
close (STORECODE);

正如我在原始帖子中所说的那样,&#34; clean_keyword_url_5.3.txt&#34;包含新闻文章的URL。它必须是一行一行的URL才能使用LWP :: Simple中的get方法。 再次感谢!!!