正则表达式以匹配除<p>和</ p>之外的所有HTML标记

正则表达式以匹配除<p>和</ p>之外的所有HTML标记

Regex to match all HTML tags except

and

我需要在Perl中使用正则表达式匹配并删除所有标签。 我有以下几点:

1
<\\\\??(?!p).+?>

但这仍与结束

标记匹配。 关于如何与结束标记匹配的任何提示?

注意,这是在xhtml上执行的。


如果您坚持使用正则表达式,那么在大多数情况下,这样的方法将起作用:

1
2
# Remove all HTML except"p" tags
$html =~ s{<(?>/?)(?:[^pP]|[pP][^\\s>/])[^>]*>}{}g;

说明:

1
2
3
4
5
6
7
8
9
10
11
s{
  <             # opening angled bracket
  (?>/?)        # ratchet past optional /
  (?:
    [^pP]       # non-p tag
    |           # ...or...
    [pP][^\\s>/] # longer tag that begins with p (e.g., [cc lang="perl"])
  )
  [^>]*         # everything until closing angled bracket
  >             # closing angled bracket
 }{}gx; # replace with nothing, globally

但实际上,请您省去一些麻烦,而改用解析器。 CPAN有几个合适的模块。这是使用HTML :: TokeParser模块的示例,该模块具有功能强大的HTML :: Parser CPAN发行版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use strict;

use HTML::TokeParser;

my $parser = HTML::TokeParser->new('/some/file.html')
  or die"Could not open /some/file.html - $!";

while(my $t = $parser->get_token)
{
  # Skip start or end tags that are not"p" tags
  next  if(($t->[0] eq 'S' || $t->[0] eq 'E') && lc $t->[1] ne 'p');

  # Print everything else normally (see HTML::TokeParser docs for explanation)
  if($t->[0] eq 'T')
  {
    print $t->[1];
  }
  else
  {
    print $t->[-1];
  }
}

HTML :: Parser接受文件名,打开的文件句柄或字符串形式的输入。将以上代码包装在库中并使目标可配置(即,不仅像上述那样print)并不难。结果将比尝试使用正则表达式更加可靠,可维护,并且可能更快(HTML :: Parser使用基于C的后端)。


在我看来,尝试用HTML解析器以外的任何其他东西解析HTML只是在痛苦的世界。 HTML是一种非常复杂的语言(这是创建XHTML的主要原因之一,它比HTML简单得多)。

例如,这:

1
2
3
4
<HTML /
  <HEAD /
    <TITLE / > /
    <P / >

是完整的,100%格式正确,100%有效的HTML文档。 (好吧,它缺少DOCTYPE声明,但除此之外……)

它在语义上等同于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
  <head>
   
      >
   
  </head>
  <body>
    <p>

      >
   
</p>
  </body>
</html>

但是,您仍然必须处理有效的HTML。您当然可以设计一个正则表达式来解析它,但是,正如其他人已经建议的那样,使用实际的HTML解析器非常容易。


我想出了这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<(?!\\/?p(?=>|\\s.*>))\\/?.*?>

x/
<           # Match open angle bracket
(?!         # Negative lookahead (Not matching and not consuming)
    \\/?     # 0 or 1 /
    p           # p
    (?=     # Positive lookahead (Matching and not consuming)
    >       # > - No attributes
        |       # or
    \\s      # whitespace
    .*      # anything up to
    >       # close angle brackets - with attributes
    )           # close positive lookahead
)           # close negative lookahead
            # if we have got this far then we don't match
            # a p tag or closing p tag
            # with or without attributes
\\/?         # optional close tag symbol (/)
.*?         # and anything up to
>           # first closing tag
/

现在,这将处理带有或不带有属性的p标签以及结尾的p标签,但将匹配带有或不带有属性的前置和相似标签。

它不会去除属性,但是我的源数据不会将它们放入。稍后我可能会更改它以执行此操作,但是现在就足够了。


Xetius,复活了这个古老的问题,因为它有一个未提及的简单解决方案。 (在进行正则表达式赏金任务研究时发现了您的问题。)

关于使用正则表达式解析html的所有免责声明,这是一种简单的方法。

1
2
3
4
5
6
7
8
9
#!/usr/bin/perl
$regex = '(<\\/?p[^>]*>)|<[^>]*>';
$subject = 'Bad html   <p>
My paragraph
</p> Italics <p class="blue">second
</p>'
;
($replaced = $subject) =~ s/$regex/$1/eg;
print $replaced ."\
"
;

观看此现场演示

参考

除情况s1,s2,s3之外如何匹配模式

除非...


我使用了Xetius regex,它工作正常。除了一些flex生成的标签之外,这些标签可以是:
里面没有空格。我试着用简单的方法修复它?在\ s之后,看起来好像正在工作:

1
<(?!\\/?p(?=>|\\s?.*>))\\/?.*?>

我用它来清除flex生成的html文本中的标签,所以我还添加了更多例外标签:

1
<(?!\\/?(p|a|b|i|u|br)(?=>|\\s?.*>))\\/?.*?>

不确定为什么要这样做-用于HTML净化的正则表达式并不总是最好的方法(您需要记住清理属性等,删除javascript:hrefs之类)...但是,可以匹配HTML的正则表达式不是

的标签:

(<[^pP].*?>|)

详细:

1
2
3
4
5
6
7
8
9
(
    <               # < opening tag
        [^pP].*?    # p non-p character, then non-greedy anything
    >               # > closing tag
|                   #   ....or....
    </              # </
        [^pP]       # a non-p tag
    >               # >
)

Since HTML is not a regular language

HTML不是,但HTML标记是HTML,它们可以用正则表达式充分描述。


由于HTML不是常规语言,因此我不希望正则表达式在匹配它方面做得很好。他们也许有能力完成这项任务(尽管我不敢相信),但我会考虑在其他地方寻找。我确定perl必须有一些现成的库来处理HTML。

无论如何,我认为您要匹配的是非贪婪(我不知道perl的regexp语法有多变,所以我无济于事进一步)。我假设\ s表示空白。也许不是。无论哪种方式,您都需要某种与标记名称偏移了空白的属性相匹配的属性。但这比这更困难,因为人们经常在脚本和注释甚至是引用的属性值中放上未转义的尖括号,而这是您不希望与之匹配的。

因此,正如我所说,我真的不认为正则表达式是完成这项工作的正确工具。


原始的正则表达式可以很容易地工作:

1
 <(?>/?)(?!p).+?>

问题是/? (或\?)在断言失败后放弃匹配的内容。在其周围使用非回溯组(?> ...)时要注意,它绝不会释放匹配的斜杠,因此(?!p)断言始终锚定在标记文本的开头。

(这就是说,我同意通常不使用正则表达式解析HTML)。


您可能还希望在p标记中的" p"之前留空白。不确定您会遇到这种情况的频率,但是

是完全有效的HTML。


假定这将在PERL中起作用,就像在声称使用PERL兼容语法的语言中一样:

/<\\/?[^p][^>]*>/

编辑:

不幸的是,但这与标记不匹配。

这也许吗?

1
/<\\/?(?!p>|p )[^>]+>/

那应该也包含具有属性的

标签。


试试这个,它应该可以工作:

1
/<\\/?([^p](\\s.+?)?|..+?)>/

说明:它匹配除" p"之外的单个字母,后跟一个可选的空格和更多字符,或多个字母(至少两个)。

/ EDIT:我已经添加了处理p标记中的属性的功能。


您可能还应该删除

上的所有属性。
标签,因为不好的人可以做类似的事情:

1
2
<p onclick="document.location.href='http://www.evil.com'">Clickable text
</p>

最简单的方法是使用在这里建议的正则表达式搜索带有属性的&ltp>标记,然后将其替换为

没有属性的标签。只是为了安全起见。


推荐阅读