【问题标题】:Delete the content between HTML tags including the tags themselves in Perl删除 HTML 标签之间的内容,包括 Perl 中的标签本身
【发布时间】:2012-09-24 00:47:33
【问题描述】:

大约有 100 个文件,我需要遍历每个文件并删除 <style></style> 之间的所有数据 + 也删除这些标签。

例如

<html>
<head> <title> Example </title> </head>
<style>
p{color: red;
background-color: #FFFF;
}
div {......
...
}
</style>
<body>
<p> hi I'm a paragraph. </p>
</body>
</html>

应该变成

<html>
<head> <title> Example </title> </head>
<body>
<p> hi I'm a paragraph. </p>
</body>
</html>

此外,在某些文件中,样式模式类似于

<style type="text/css"> blah </style>

<link rel="stylesheet" type="text/css" href="$url_path/gridsorting.css">

我需要删除所有 3 个模式。我如何在 Perl 中做到这一点?

【问题讨论】:

标签: regex perl html-parsing


【解决方案1】:
use strict;
use warnings;

use XML::LibXML qw( );

my $qfn = 'a.html';

my $doc  = XML::LibXML->load_html( location => $qfn );
my $root = $doc->documentElement();

for my $style_node ($root->findnodes('//style')) {
   $style_node->parentNode()->removeChild($style_node);
}

{
   open(my $fh, '>', $qfn)
      or die;
   print($fh $doc->toStringHTML());
}

它正确处理:

  • 标签中带有属性或空格的样式元素,
  • 跨越多行的样式元素,
  • 样式标签跨越多行,
  • 包含样式元素的一部分和其他内容的行,
  • 具有多个样式元素的文档,
  • 属性值中看起来像样式标签的东西,
  • 看起来像 CDATA 块中的样式标签,并且
  • 看起来像 cmets 中的样式标签。

截至本次更新,其他解决方案仅处理其中的 2 或 3 个。

【讨论】:

  • 输出是这样的:pastebin.com/uWAamD19。它添加了我不需要的 DOCTYPE 和 。
  • 如何删除 DOCTYPE 和 ?我不知道它为什么会自动生成。
  • 你不能。这就是它的作用。没有理由删除它们。
  • @fxzuz,您的更改不等效。我不想进口任何东西!省略参数周围的括号会导致许多问题,并且更难看出参数列表的结束位置。还原您的更改。
  • @fxzuz,你为什么只删除那些括号?您可以删除许多其他内容。你甚至与自己不一致!
【解决方案2】:

Ikegami 是对的,你真的应该至少使用一个 HTML/XML 解析器来完成这项任务。我个人喜欢使用Mojo::DOM 解析器。这是您的 HTML 的文档对象模型接口,它支持CSS3 selectors,使其在您需要时非常灵活。然而,这对它来说非常简单:

#!/usr/bin/env perl

use strict;
use warnings;

use Mojo::DOM;

my $content = <<'END';
<html>
<head> <title> Example </title> </head>
<style>
p{color: red;
background-color: #FFFF;
}
div {......
...
}
</style>
<body>
<p> hi I'm a paragraph. </p>
</body>
</html>
END

my $dom = Mojo::DOM->new( $content );
$dom->find('style')->pluck('remove');

print $dom;

pluck 方法有点令人困惑,但它实际上只是对每个结果对象执行方法的简写。类似的线可以是

$dom->find('style')->each(sub{ $_->remove });

这更容易理解,但不那么可爱。


阅读您的编辑后,您必须处理的不仅仅是基本形式,我必须进一步强调,这就是为什么您使用解析器来修改 HTML,而不是让您的正则表达式增长到荒谬的比例。

现在假设$content 变量也包含这些行

<link rel="stylesheet" type="text/css" href="$url_path/gridsorting.css">
<link rel="icon" href="somefile.jpg">

您要删除第一个而不是第二个的位置。您可以通过以下两种方式之一执行此操作。

$dom->find('link')->each( sub{ $_->remove if $_->{rel} eq 'stylesheet' } );

此机制使用对象方法(并且 Mojo::DOM 将属性公开为哈希键)仅删除具有 rel=stylesheetlink 标记。但是,您可以使用 CSS3 选择器仅 find 这些元素,并且由于 Mojo::DOM 具有完整的 CSS3 选择器支持,您可以这样做

$dom->find('link[rel=stylesheet]')->pluck('remove'); 

CSS3 选择器语句可以用逗号连接以查找与任一选择器匹配的所有标签,因此我们可以简单地包含该行

$dom->find('style, link[rel=stylesheet]')->pluck('remove');

并一举摆脱所有令人反感的样式表!

【讨论】:

  • 你能告诉我我正在监考考试,除了考虑 CSS3 选择器之外别无他法。 :-)
  • 遗憾的是我必须使用 Perl 5.8 :/
  • 我公司不让我使用任何版本的 perl > 5.8.5
  • 虽然我可以想象一家公司对使用特定版本的 Perl 进行实时共享程序(想想 webapps 或 db 交互)有严格的政策,但您正在做的是一次性的管理任务。如果他们阻止您使用正确的工具来完成工作,那么他们就是在阻止您完成工作。
  • 我以某种方式做到了,但是是的,我必须为此编写一个糟糕的代码。心情不好:|无论如何,谢谢你的回答,它会在未来帮助我(也许)。干杯! :)
【解决方案3】:

另一种可能的解决方案是使用HTML::TreeBuilder

#!/usr/bin/perl

use strict;
use warnings;
use HTML::TreeBuilder 5; # Ensure weak references in use

foreach my $file_name (@ARGV) {
  my $tree = HTML::TreeBuilder->new; # empty tree
  $tree->parse_file($file_name);
  # print "Hey, here's a dump of the parse tree of $file_name:\n";
  # $tree->dump; # a method we inherit from HTML::Element
  foreach my $e ($tree->look_down(_tag => "style")) {
      $e->delete();
  }
  foreach my $e ($tree->look_down(_tag => "link", rel => "stylesheet")) {
      $e->delete();
  }
  print "And here it is, bizarrely rerendered as HTML:\n",
    $tree->as_HTML, "\n";

  # Now that we're done with it, we must destroy it.
  $tree = $tree->delete; # Not required with weak references
}

【讨论】:

    【解决方案4】:

    一种使用sed的方式:

    sed '/<style>/,/<\/style>/d' file.txt
    

    结果:

    <html>
    <head> <title> Example </title> </head>
    <body>
    <p> hi I'm a paragraph. </p>
    </body>
    </html>
    

    【讨论】:

    • 假设在一行中除了样式元素之外什么都没有。假设&lt;style&gt;...&lt;/style&gt; 不出现在属性值、CDATA 和 cmets 中。等
    • @ikegami:你是对的。我对输入做了一些假设。应该使用 XML 解析器 +1
    【解决方案5】:
    perl -lne 'print unless(/<style>/.../<\/style>/)' your_file
    

    测试如下:

    > cat temp
    <html>
    <head> <title> Example </title> </head>
    <style>
    p{color: red;
    background-color: #FFFF;
    }
    div {......
    ...
    }
    </style>
    <body>
    <p> hi I'm a paragraph. </p>
    </body>
    </html>
    
    
    > perl -lne 'print unless(/<style>/.../<\/style>/)' temp
    <html>
    <head> <title> Example </title> </head>
    <body>
    <p> hi I'm a paragraph. </p>
    </body>
    </html>
    > 
    

    如果你想就地做,那么:

    perl -i -lne 'print unless(/<style>/.../<\/style>/)' your_file
    

    【讨论】:

    • 假设在一行中除了样式元素之外什么都没有。假设样式元素没有属性。假设&lt;style&gt;...&lt;/style&gt; 不出现在属性值、CDATA 和 cmets 中。等
    【解决方案6】:

    我想出了一个办法,你可以试试下面的:

    #! /usr/bin/perl -w
    use strict;
    my $line = << 'END';
    <html>
    <head> <title> Example </title> </head>
    <style>
    p{color: red;
    background-color: #FFFF;
    }
    div {......
    ...
    }
    </style>
    <body>
    <p> hi I'm a paragraph. </p>
    </body>
    </html>
    END
    
    $line =~ s{<style[^>]*.*?</style>.}{}gs;
    print $line;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-06
      • 1970-01-01
      • 2011-10-31
      • 1970-01-01
      相关资源
      最近更新 更多