从HTML中的多行提取InnerHTML

时间:2012-06-26 02:09:48

标签: regex perl

我使用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标签。

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

    任何帮助都将不胜感激。

3 个答案:

答案 0 :(得分: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 };
}

答案 1 :(得分:1)

您应该使用渐进式匹配从一行中提取所有匹配项。例如,如果 $ line 包含字符串这是一个div,后跟一个范围,并且您要提取这是一个div,后跟一个< / strong>和 span ,您可以使用以下内容:

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

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

答案 2 :(得分:0)

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

首先,您可能希望删除<script></script>代码之间的内容。

其次,您不能假设开始标记始终包含相同的文本,例如<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";