我对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);
您可能会注意到此代码无效。问题在于我无法解开它。
所以,如果您有任何想法,那将非常有帮助。 谢谢!!!
答案 0 :(得分:0)
(见最后的更新1)
在编写Perl(严格,警告和显式编码)时,很高兴看到您正在尝试使用最佳实践。如果使用locale
是好的可以辩论....有三件事你可以做得更好:
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)
)。另外,如果只将它用于一个(!)键,请不要使用哈希。
你的新代码也相当不错(我爱看到更多人从一开始就使用strict
...)。除非您忘记将任何内容放入$url2parse
,并且您发现了一些用于循环的创造性用途; - )
现在使用getstore
或mirror
或其所谓的内容是一个好主意。这意味着我们不必手动打开文件句柄。
这可以编码:
...;
# ↓ 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方法。 再次感谢!!!