【问题标题】:Extract InnerHTML from multiple lines in HTML从 HTML 中的多行中提取 InnerHTML
【发布时间】:2012-06-27 07:59:04
【问题描述】:

我正在使用 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";
}
}

上面代码sn-p的问题是:

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

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

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

任何帮助将不胜感激。

【问题讨论】:

标签: regex perl


【解决方案1】:

您应该使用渐进匹配从一行中提取所有匹配项。例如,如果 $line 包含字符串 This is a div,后跟 span,而您要提取 This is a div,后跟 a 和 span,你可以这样使用:

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

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

【讨论】:

  • 谢谢。您能否更详细地解释您使用的正则表达式?我知道 innerHTML 将存储在您正在打印的第二个捕获缓冲区中。但是,当我在代码中用您的正则表达式替换我的正则表达式时,它会给出一个空白文件。你能用一个例子详细说明吗?那真的很有帮助。你可以以我的代码为例,用你的正则表达式进行修改。
  • 对不起,我盲目地从某个地方复制了文本并写了正则表达式而没有正确看到它!现在清楚了吗?如果没有,我会解释。
  • 是的,谢谢。我编辑了我的帖子并添加了我要解析的 HTML 代码。我已经提到了两个 div 标签,并希望在它们之间提取 innerHTML。你能写下你的正则表达式来提取它们吗?这样,我也可以测试。目前,您的正则表达式是根据我的理解编写的,以提取所有标签之间的内容。我很快就会试试看。
  • 嗯,innerHTMl 不是两个标签之间的内容吗?就像如果元素是
    Hi, I am Cupid
    ,innerHTML 会不会是 Hi, I am Cupid
  • 那么你的问题是标签有属性吗?否则我的代码将提取两个 div 之间的字符串...如果您愿意,我可以提供处理带有属性的标签的代码...
【解决方案2】:

使用强大的 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 };
}

【讨论】:

  • 谢谢。我知道使用 HTML 解析器可以简化工作,也可以让代码在编码时更有效率。但是,我想编写一个不需要用户从 CPAN 安装额外包的脚本。但是,如果您可以多写一点来解释这段代码如何提取 div 标签之间的 innerHTML,将会很有帮助。
  • 为什么要编写一个不需要用户从 CPAN 安装额外包的脚本?忘记链接到文档:parsefindnodesas_HTMLcontent_array_ref
【解决方案3】:

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

首先,您可能希望删除&lt;script&gt;&lt;/script&gt; 标记之间的内容。

其次,你不能假设开始标签总是包含相同的文本,例如&lt;span class="myclass"&gt; 中的文本与 &lt;/span&gt; 中的文本不太一样。

我建议删除所有&lt;something&gt; 标签,不管那是什么类型的标签,并删除&lt;script&gt; 标签。

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

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

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

    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";

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-23
    • 1970-01-01
    • 2014-01-17
    • 2019-09-30
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    相关资源
    最近更新 更多