删除HTML标记之间的内容,包括Perl中的标记本身

时间:2012-10-03 05:54:35

标签: regex perl html-parsing

大约有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中执行此操作?

6 个答案:

答案 0 :(得分:6)

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块中的样式标记的东西,
  • 在评论中看起来像样式标签的东西。

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

答案 1 :(得分:4)

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公开属性作为哈希键)仅删除具有link的{​​{1}}标记。但是,您可以将CSS3选择器仅用于rel=stylesheet这些元素,并且由于Mojo :: DOM具有完整的CSS3选择器支持,您可以执行

find

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

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

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

答案 2 :(得分:2)

另一种可能的解决方案是使用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
}

答案 3 :(得分:1)

使用sed的一种方式:

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

结果:

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

我想出了一种方法,你可以尝试以下方法:

#! /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;