【问题标题】:Get contents from HTML tag using MyParser in Perl在 Perl 中使用 MyParser 从 HTML 标记中获取内容
【发布时间】:2012-11-06 10:39:00
【问题描述】:

我有一个html如下:

<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body bgcolor="white">

<h1>foo.c</h1>

<form method="post" action=""
        enctype="application/x-www-form-urlencoded">
  Compare this file to the similar file: 
  <select name="file2">

    <option value="...">...</option>


  </select>
  <input type="hidden" name="file1" value="foo.c" /><br>
  Show the results in this format: 
</form>
<hr>

<p>
<pre>
some code
</pre>

我需要获取输入名称 = 'file' 的值和 HTML pre 标记的内容。我不知道 perl 语言,通过谷歌搜索我写了这个小程序(我认为它不是“优雅”):

#!/usr/bin/perl

package MyParser;
use base qw(HTML::Parser);

#Store the file name and contents obtaind from HTML Tags
my($filename, $file_contents);

#This value is set at start() calls
#and use in text() routine..
my($g_tagname, $g_attr);


#Process tag itself and its attributes
sub start {
    my ($self, $tagname, $attr, $attrseq, $origtext) = @_;

    $g_tagname = $tagname;
    $g_attr = $attr;
}

#Process HTML tag body
sub text {
    my ($self, $text) = @_;

    #Gets the filename
    if($g_tagname eq "input" and $g_attr->{'name'} eq "file1") {
    $filename = $attr->{'value'};
    }

    #Gets the filecontents
    if($g_tagname eq "pre") {
    $file_contents = $text;
    }
}

package main;

#read $filename file contents and returns
#note: it works only for text/plain files.
sub read_file {
    my($filename) = @_;
    open FILE, $filename or die $!;
    my ($buf, $data, $n);
    while((read FILE, $data, 256) != 0) {
    $buf .= $data;
    }
    return ($buf);
}


my $curr_filename = $ARGV[0];
my $curr_file_contents = read_file($curr_filename);

my $parser = MyParser->new;
$parser->parse($curr_file_contents);

print "filename: ",$filename,"file contents: ",$file_contents;

然后我调用./foo.pl html.html 但我从$filename$file_contents 变量中得到空值。

如何解决这个问题?

【问题讨论】:

  • 始终使用use strict; use warnings;

标签: perl html-parsing


【解决方案1】:

您通常不想使用纯 HTML::Parser,除非您正在编写自己的解析模块或做一些通常很棘手的事情。在这种情况下,HTML::TreeBuilder 是 HTML::Parser 的子类,是最容易使用的。

另外,请注意 HTML::Parser 有一个 parse_file 方法(而 HTML::TreeBuilder 使用 new_from_file 方法使其变得更加容易,因此您不必执行所有这些 read_file 业务(此外,还有比您选择的方法更好的方法,包括File::Slurp 和旧的do { local $/; &lt;$handle&gt; } 技巧。

use HTML::TreeBuilder;

my $filename = $ARGV[0];
my $tree = HTML::TreeBuilder->new_from_file($filename);

my $filename = $tree->look_down(
    _tag => 'input',
    type => 'hidden',
    name => 'file1'
)->attr('value');

my $file_contents = $tree->look_down(_tag => 'pre')->as_trimmed_text;

print "filename: ",$filename,"file contents: ",$file_contents;

有关look_downattras_trimmed_text 的信息,请参阅HTML::Element 文档; HTML::TreeBuilder 都是元素,并且可以与元素一起使用。

【讨论】:

    【解决方案2】:

    使用HTML::TreeBuilder::XPath Perl 模块(很少行):

    #!/usr/bin/env perl
    use strict; use warnings;
    use HTML::TreeBuilder::XPath;
    
    my $tree = HTML::TreeBuilder::XPath->new_from_content( <> );
    print $tree->findvalue( '//input[@name="file1"]/@value' );
    print $tree->findvalue( '//pre/text()' );
    

    用法

    ./script.pl file.html
    

    输出

    foo.c
    some code
    

    注意事项

    • 过去,我使用HTML::TreeBuilder 模块进行一些网络抓取。现在,我不能回到复杂性。 HTML::TreeBuilder::XPath 使用有用的 Xpath 表达式发挥所有魔力。
    • 您可以使用new_from_file 方法打开文件或文件句柄而不是new_from_content,参见perldoc HTML::TreeBuilderHTML::TreeBuilder::XPath 继承自HTML::TreeBuilder 的方法)
    • 此处允许以这种方式使用&lt;&gt;,因为HTML::TreeBuilder::new_from_content() 专门允许以这种方式读取多行。大多数构造函数不允许这种用法。您应该提供一个标量,或者使用其他方法。

    【讨论】:

    • 添加了 usage 部分,并将 DATA 技巧 替换为 diamond operator 以打开文件作为参数。跨度>
    • 未来读者请注意:此处允许以这种方式使用&lt;&gt;,因为HTML::TreeBuilder::new_from_content() 专门允许以这种方式读取多行。大多数构造函数不允许这种用法,需要do { local $/; &lt;&gt; } 将所有内容读入一个变量(参数)。
    【解决方案3】:

    与往常一样,有不止一种方法可以做到这一点。以下是如何使用 DOM ParserMojolicious 来完成此任务:

    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    use Mojo::DOM;
    
    # slurp all lines at once into the DOM parser
    my $dom = Mojo::DOM->new(do { local $/; <> });
    
    print $dom->at('input[name=file1]')->attr('value');
    print $dom->at('pre')->text;
    

    输出:

    foo.c
    some code
    

    【讨论】:

    • 由于 OP 有一个作为参数给出的输入文件,因此“魔术打开”菱形运算符 (do { local $/; &lt;&gt; }) 或使用 Mojo::Util::slurp($ARGV[0]) 在这里更有意义。否则,很好的演示!
    • @memowe 感谢您的回答,因为它只是帮助我解决了类似的问题,但您的回答中有错字 - 应该是 ->attr('value') 而不是 attrs。问候
    • @AndyLorenz 是的,你是对的。过去是attrs,代码有效。随时更新它。 :)
    • @memowe 我尝试编辑您的答案,但因 javsacript 错误而失败! chrome 和 IE 中的相同错误 - 似乎是涉及少于 6 个字符的更改的编辑的编码逻辑中的问题。
    猜你喜欢
    • 2013-05-11
    • 2015-02-08
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-04
    相关资源
    最近更新 更多