使用Perl从目录中的文件中剥离HTML

时间:2012-08-05 16:34:20

标签: html perl

我几天前问了一个关于从PERL文件中删除HTML的问题。我是一个n00b,我已经在网站上搜索了我的问题的答案......但不幸的是我找不到任何东西......这可能是因为我和我没有相关的事情。我在看的时候看到了答案。

所以,情况就是这样。我有一个大约20 GB的文本文件的目录。我想从每个文件中删除HTML并将每个文件输出到唯一的文本文件。我已经编写了下面的程序,这似乎可以解决目录中的前12个文本文件(总共有大约12,000个文本文件)......但是......我遇到了几个问题。第一个问题是,在解析了第12个文本文件之后,我开始收到关于深度递归的警告......然后不久之后程序退出,因为我的内存不足。我想我的编程效率非常低。所以,我想知道你们中是否有人发现我的代码有任何明显的错误,这会让我的内存耗尽。 ......一旦我解决了问题,希望我能够做出贡献。

#!/usr/bin/perl -w
#use strict;
use Benchmark;
#get the HTML-Format package from the package manager.
use HTML::Formatter;
#get the HTML-TREE from the package manager
use HTML::TreeBuilder;
use HTML::FormatText;
$startTime = new Benchmark;
my $direct="C:\\Directory";
my $slash='\\';

opendir(DIR1,"$direct")||die "Can't open directory";
my @New1=readdir(DIR1);

foreach $file(@New1)
{

if ($file=~/^\./){next;}
#Initialize the variable names.
my $HTML=0;
my $tree="Empty";
my $data="";
#Open the file and put the file in variable called $data

{
    local $/;
    open (SLURP, "$direct$slash"."$file") or die "can't open $file: $!"; 
    #read the contents into data
    $data = <SLURP>; 

    #close the filehandle called SLURP
    close SLURP or die "cannot close $file: $!";
    if($data=~m/<HTML>/i){$HTML=1;}
    if($HTML==1)
        {
            #the following steps strip out any HTML tags, etc.
            $tree=HTML::TreeBuilder->new->parse($data);
            $formatter=HTML::FormatText->new(leftmargin=> 0, rightmargin=>60);
            $Alldata=$formatter->format($tree); 
        }
}
#print
my $outfile = "out_".$file;
open (FOUT, "> $direct\\$outfile");
print FOUT "file: $file\nHTML: $HTML\n$Alldata\n","*" x 40, "\n" ;
close(FOUT);

}


$endTime = new Benchmark;
$runTime = timediff($endTime, $startTime);
print ("Processing files took ", timestr($runTime));

3 个答案:

答案 0 :(得分:2)

@New1中的文件列表占用了大量空间。

此外,如果您使用的是较早版本的HTML::TreeBuilder,那么此类的对象可能需要删除,因为它们过去不受自动Perl垃圾回收的影响。

这是一个避免这两个问题的程序,通过逐步读取目录,并使用HTML::FormatText->format_string格式化文本,隐式删除它创建的任何HTML::TreeBuilder个对象。

此外,File::Spec在构建绝对文件路径方面做得更为繁琐,而且它是一个核心模块,因此不需要在您的系统上安装

use strict;
use warnings;

use File::Spec;
use HTML::FormatText;

my $direct = 'C:\Directory';

opendir my $dh, $direct or die "Can't open directory";

while ( readdir $dh ) {

  next if /^\./;

  my $file = File::Spec->catfile($direct, $_);
  my $outfile = File::Spec->catfile($direct, "out_$_");
  next unless -f $file;

  my $html = do {
    open my $fh, '<', $file or die qq(Unable to open "$file" for reading: $!);
    local $/;
    <$fh>;
  };

  next unless $html =~ /<html/i;

  my $formatted = HTML::FormatText->format_string(
      $html, leftmargin => 0, rightmargin => 60);

  open my $fh, '>', $outfile or die qq(Unable to open "$outfile" for writing: $!);

  print $fh "File: $file\n\n";
  print $fh "$formatted\n";
  print $fh "*" x 40, "\n" ;

  close $fh or die qq(Unable to close "$outfile" after writing: $!);
}

答案 1 :(得分:1)

您的previous问题的答案出了什么问题?

用于写入的打开文件而不检查返回码。你确定成功了吗?你在哪个目录创建了文件?

更好的方法是:

  • 逐个阅读文件
  • 剥离HTML
  • 在正确的目录中写出新文件并检查返回代码

类似的东西:

while ( my $file = readdir DIR ) {

    ....process file

    open my $newfile, '>', "$direct/out_$outfile" or die "cannot open $outfile: $!\n";

   ... etc
}

答案 2 :(得分:0)

如何减少此应用程序的内存占用量:

$tree = $tree->delete添加到循环结尾时,问题是否仍然存在?

perl垃圾收集器无法解析循环引用;所以你必须手动销毁树,这样就不会耗尽内存。

(参见http://metacpan.org/pod/HTML::TreeBuilder模块文档中的第一个例子)

您应该将readdir放在循环中。你编写它的方式,你首先阅读这个巨大的文件列表。当你说

my $file;
while (defined($file = readdir DIR1)) {..}

一次只能读取一个条目。应该节省一些额外的记忆。

关于风格的一些其他评论:

默认值

您为$tree提供默认值"Empty"。这完全没必要。如果要显示变量的未定义方式,请将其设置为undef,默认情况下为if ($file=~/^\./){next;} 。 Perl保证了这种初始化。

反斜杠

您使用反斜杠作为目录分隔符吗?别担心,只需使用普通斜线。除非你在DOS上,否则你也可以使用正常的斜杠,Windows并不是那么愚蠢。

语句修饰符

这一行

next if $file =~ /^\./;

可写得更具可读性

/<HTML>/i

随后使用parens

您使用parens作为函数参数列表是不可能的。除非存在歧义,否则可以省略所有内置函数的parens。我更喜欢避开它们,其他人可能会觉得它们更容易阅读。但请坚持一种风格!

更好的正则表达式

您测试是否存在html。如果我告诉你/<html/i标签可以有属性怎么办?您应该考虑测试if($data=~m/<HTML>/i){$HTML=1;} if($HTML==1) {...}

简化(删除另一个错误)

你的考试

$HTML = $data =~ /<html/i;
if ($HTML == 1) {...}

可以写成

$HTML = $data =~ /<html/i
if ($HTML) {...}

可以写成

if ($data =~ /<html/i) {...}

可折叠成

$HTML

您实现它的方式,HTML::FormatText变量从未重置为 false 值。因此,一旦文件包含html,所有后续文件也将被视为html。你可以通过在最内在的合理范围内定义你的变量来抵消这些问题。

使用HTML :: FormatText,向@pavel

致敬

充分利用您使用的模块。看看我在my $string = HTML::FormatText->format_file( 'test.html', leftmargin => 0, rightmargin => 50 ); 的示例中找到的内容:

use strict

您可以轻松地调整它以避免手动构建树。为什么你没有尝试过这种方法,就像@pavel在your other post告诉你的那样?本来可以节省你的记忆问题......

使用严格

你为什么评论$file?在学习语言时,尽可能多地发出致命警告非常重要。或者在编写固体代码时。那将迫使您明智地声明所有变量,如use warnings。而不是-w而不是close开关,这有点过时了。

做得好

但是检查{{1}} ;-)的返回值是一个非常大的“做得好”这是非常不可靠的!