【问题标题】:Regex to match all HTML tags except <p> and </p>正则表达式匹配除 <p> 和 </p> 之外的所有 HTML 标记
【发布时间】:2010-09-06 23:45:40
【问题描述】:

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

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

但这仍然与结束 &lt;/p&gt; 标记匹配。关于如何与结束标签匹配的任何提示?

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

【问题讨论】:

标签: html regex perl


【解决方案1】:

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

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

解释:

s{
  <             # opening angled bracket
  (?>/?)        # ratchet past optional / 
  (?:
    [^pP]       # non-p tag
    |           # ...or...
    [pP][^\s>/] # longer tag that begins with p (e.g., <pre>)
  )
  [^>]*         # everything until closing angled bracket
  >             # closing angled bracket
 }{}gx; # replace with nothing, globally

但是说真的,省去一些麻烦,改用解析器吧。 CPAN 有几个合适的模块。这是一个使用 HTML::TokeParser 模块的示例,该模块附带功能强大的 HTML::Parser CPAN 分发:

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 接受文件名、打开文件句柄或字符串形式的输入。将上面的代码包装在一个库中并使目标可配置(即,不仅仅是上面的printing)并不难。结果将比尝试使用正则表达式更加可靠、可维护,并且可能更快(HTML::Parser 使用基于 C 的后端)。

【讨论】:

  • 使用出色的 HTML::TokeParser::Simple 模块让您更加头痛。 :-)
【解决方案2】:

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

例如,这个:

<HTML /
  <HEAD /
    <TITLE / > /
    <P / >

是一个完整的、100% 格式良好、100% 有效的 HTML 文档。 (嗯,它缺少 DOCTYPE 声明,但除此之外......)

语义上等价于

<html>
  <head>
    <title>
      &gt;
    </title>
  </head>
  <body>
    <p>
      &gt;
    </p>
  </body>
</html>

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

【讨论】:

  • 哇。我不相信你,但我使用 HTML 4.01 Strict 文档类型通过 W3 验证器运行它,并且它验证了。它会发出警告,但是哇。
  • örg,你是所以对!但是,如果输入集的约束非常好,那也还不错。但是,对于随机的,不使用解析类是愚蠢的。让别人做所有的辛苦工作!
【解决方案3】:

我想出了这个:

<(?!\/?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 标签,但将匹配带有或不带有属性的前标签和类似标签。

它没有去除属性,但我的源数据没有把它们放进去。我以后可能会改变它来做到这一点,但现在就足够了。

【讨论】:

  • 终于有人发布了关于正则表达式的答案和每个部分的解释。先生,您应该获得星际奖!
【解决方案4】:

不确定您为什么要这样做 - 用于 HTML 清理的正则表达式并不总是最好的方法(您需要记住清理属性等,删除 javascript: hrefs 等)...但是,一个正则表达式匹配不是&lt;p&gt;&lt;/p&gt;的HTML标签:

(&lt;[^pP].*?&gt;|&lt;/[^pP]&gt;)

详细:

(
    <               # < opening tag
        [^pP].*?    # p non-p character, then non-greedy anything
    >               # > closing tag
|                   #   ....or....
    </              # </
        [^pP]       # a non-p tag
    >               # >
)

【讨论】:

    【解决方案5】:

    我使用了 Xetius 正则表达式,它工作正常。除了一些 flex 生成的标签,它们可以是 :
    里面没有空格。我尝试在 \s 之后用一个简单的 ? 修复它,看起来它正在工作:

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

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

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

    【讨论】:

      【解决方案6】:

      Xetius,复活了这个古老的问题,因为它有一个没有被提及的简单解决方案。 (在为regex bounty quest 做一些研究时发现了你的问题。)

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

      #!/usr/bin/perl
      $regex = '(<\/?p[^>]*>)|<[^>]*>';
      $subject = 'Bad html <a> </I> <p>My paragraph</p> <i>Italics</i> <p class="blue">second</p>';
      ($replaced = $subject) =~ s/$regex/$1/eg;
      print $replaced . "\n";
      

      看到这个live demo

      参考

      How to match pattern except in situations s1, s2, s3

      How to match a pattern unless...

      【讨论】:

      • 这会找到所有标签,包括 p。我还需要找到除 img 之外的所有标签(不是单字母名称)。
      【解决方案7】:

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

      无论如何,我认为您想要匹配的是 ?(p.+|.*)(\s*.*)> 非贪婪(我不知道 perl 的正则表达式语法的变幻莫测,所以我无能为力)。我假设 \s 表示空格。也许不是。无论哪种方式,您都需要一些与标签名称偏移的属性相匹配的东西。但这比这更困难,因为人们经常将未转义的尖括号放在脚本和 cmets 中,甚至可能是引用的属性值,而您不想匹配这些值。

      正如我所说,我真的不认为正则表达式是适合这项工作的工具。

      【讨论】:

        【解决方案8】:

        由于 HTML 不是常规语言

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

        【讨论】:

          【解决方案9】:

          假设这将在 PERL 中像在声称使用 PERL 兼容语法的语言中一样工作:

          /&lt;\/?[^p][^&gt;]*&gt;/

          编辑:

          但是很遗憾,这与 &lt;pre&gt;&lt;param&gt; 标记不匹配。

          也许是这个?

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

          这应该包括&lt;p&gt; 也有属性的标签。

          【讨论】:

            【解决方案10】:

            您可能还希望在 p 标记中的“p”之前允许空格。不知道你多久会遇到这种情况,但 是完全有效的 HTML。

            【讨论】:

              【解决方案11】:

              原始的正则表达式可以轻松使用:

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

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

              (也就是说,我同意通常用正则表达式解析 HTML 不是可行的方法)。

              【讨论】:

                【解决方案12】:

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

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

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

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

                【讨论】:

                  【解决方案13】:

                  这对我有用,因为上面的所有解决方案对于以 p 开头的其他 html 标签(例如 param pre progress 等)都失败了。它也处理了 html 属性。

                  ~(<\/?[^>]*(?<!<\/p|p)>)~ig
                  

                  【讨论】:

                  • 虽然这在技术上是正确的,但它并不是真正的未来证明
                  • @Joshua , html 的最后 4 个版本一直是向后兼容的。此外,未来需要大约十年的时间才能提前通知,并且提供给 OP 的其他解决方案也有其局限性。感谢您的评论!
                  • 尽管它们是向后兼容的,但它忽略了经常添加和支持新标签的事实。
                  • 感谢 cmets。我更新了它,效果很好。
                  【解决方案14】:

                  您可能还应该删除

                  标记上的所有属性,因为坏人可能会执行以下操作:

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

                  执行此操作的最简单方法是使用人们在此处建议的正则表达式来搜索具有属性的 &ltp> 标记,并将其替换为不具有属性的

                  标记。只是为了安全起见。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2023-03-17
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多