0

我对 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);

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

所以主要是法国或瑞士当地报纸。我想将每个链接打印在一个单独的文件夹中,我尝试使用一组文件句柄并使用 的“getstore”方法来执行此操作LWP::Simple,但是我无法在所有链接上进行循环。它会创建所有文件,但每个文件中只打印一个 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);

您可能会注意到此代码不起作用。问题是我无法完全理解它。

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

4

2 回答 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);

正如我在原始帖子中已经说过的,“clean_keyword_url_5.3.txt”包含新闻文章的 URL。它必须是一行一行的 URL 才能使用 LWP::Simple 中的 get 方法。再次感谢 !!!

于 2013-02-06T08:11:40.807 回答
0

(见最后更新1)

普通的留言:

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

  1. 在其最内部的可能范围内声明变量。
  2. 使用词法文件句柄
  3. 检查错误

你的第一个脚本

  • 将文件句柄打开到词法变量中被认为是最佳实践: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) { ... }
    

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

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

    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};
    

    括号仅用于排序优先级,它们不会“创建”列表或数组。

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

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

链接

  1. LWP::简单文档
  2. LWP::UserAgent 文档
  3. HTML::Parse说它已被弃用。请参阅HTML::ParserHTML::TreeBuilder
  4. 处理范围:为什么你永远不应该使用全局变量或“局部”变量。

更新 1

您的新代码也相当不错(我希望看到更多人strict从一开始就使用......)。除了你忘记把任何东西放进去$url2parse,而且你发现循环有一些创造性的用途;-)

使用getstoreormirror或现在所谓的东西是个好主意。这意味着我们不必手动打开文件句柄。

这可以编码:

...;
# ↓ 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 中通常用于逐行读取文件。在像这样的小程序上,这无关紧要,但是当您的数据扩展时,这是一个好习惯……</p>

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

...;
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,因为这样可以更深入地了解失败。

一旦您可以正确获取和处理您的 URL,并行性可能是加快处理速度的下一步。

于 2013-02-04T15:52:07.653 回答