2

我正在使用 Perl 连接到站点,解析其 HTML 并提取标签之间的 innerHTML。在尝试高级概念之前,我先尝试更简单的概念。

我使用 LWP::UserAgent 向站点发送我的 HTTP GET 请求并接收我的响应。

我将响应存储在数组中,如下所示:

@res = ($ua->request($req))->content;

编辑:要解析的 HTML:

<div class="new"> this is Line 1 </div>
<div>
      this is Line 2 </div>

现在,我解析 HTTP 响应中的每一行并提取标签之间的文本:

foreach $line(@res)
{
chomp $line;
if($line =~ /<div[^>]*?>(.*)<\/div>/)
{
    $match = $1;
    print OUTPUT $match."\n";
}
}

上述代码片段的问题是:

  1. 它只匹配第一个成功匹配的 innerHTML。它不会打印所有成功的匹配项。我不知道为什么,循环应该按照我的方式工作。每次成功匹配后,变量 $match 的值都应该被捕获缓冲区的内容覆盖。

  2. 如果标签跨越多行,它将无法提取 innerHTML 之间的文本。第一行有开始 div 标签,下一行有 innerHTML ,下一行有结束 div 标签。

我无法在这篇文章中编写 HTML,所以给出了描述。

任何帮助,将不胜感激。

4

3 回答 3

3

使用强大的 HTML 解析器:

use HTML::TreeBuilder::XPath qw();

my $tree = HTML::TreeBuilder::XPath->new;
$tree->parse($http_response->content);

for my $node ($tree->findnodes('//div')) {
    print $_->as_HTML for @{ $node->content_array_ref };
}
于 2012-06-26T11:26:18.730 回答
1

您应该使用渐进匹配从一行中提取所有匹配项。例如,如果$line包含字符串This is a div,后跟 span,并且您要提取This is a div,后跟 aspan,则可以使用以下内容:

print "$2\n" while $line =~ /<(.*?)>(.*?)<\/\g{1}>/g;

当然,您也想解析嵌套元素,这将变得更加困难和棘手。根据你的第二个问题,你需要多线模式。最好的办法是使用 \s 修饰符,这将强制 . 也匹配换行符。或者,也许您可​​以通过将文件句柄直接分配给标量变量来将所有行合并在一起。

于 2012-06-26T05:18:36.500 回答
0

如果你想让它足够通用并适合实际应用,那就有点复杂了。

首先,您可能想要摆脱<script></script>标签之间的内容。

其次,你不能假设开始标签总是包含相同的文本,例如 text in与 in<span class="myclass">不完全相同</span>

我建议删除所有<something>标签,无论那是哪种标签,并删除<script>标签。

您可能无法仅使用一个超级智能的正则表达式,您宁愿使用多个正则表达式来完成这项工作。

这是我整理的一个小脚本,在 cnn.com 上运行良好(作为非平凡输入的示例)。我试图保留换行符,只是为了很好地打印它,并删除了空行——但显然,所有这些可能都不是必需的。

我在这里也做了一些肮脏的把戏,\n用一个虚拟\\\\NN字符串隐藏(<script>否则全局替换将不起作用)。

    my $text = "";
    foreach my $line (@res)
    {
        chomp $line;
        $text .= $line . "\\\\NN"; # Hiding the \n's
    }

    $text =~ s/(<script(\s[^<]*)?>.*?<\/script>)//gi;
    $text =~ s/<.*?>/ /g;

    # Beautify it... :)
    $text =~ s/\s{2,}/ /g;
    $text =~ s/\s*\\\\NN\s*/\\\\NN/g;
    $text =~ s/(\\\\NN){2,}/\\\\NN/g;
    $text =~ s/\\\\NN/\n/g;

    print $text."\n";
于 2012-06-26T07:59:43.600 回答