除了src之外,删除所有HTML属性

14

我想要移除所有标签属性,除了src属性。例如:

<p id="paragraph" class="green">This is a paragraph with an image <img src="/path/to/image.jpg" width="50" height="75"/></p>

将被返回为:

<p>This is a paragraph with an image <img src="/path/to/image.jpg" /></p>

我有一个正则表达式可以去除所有属性,但我想调整它以保留src。这是我目前的代码:

<?php preg_replace('/<([A-Z][A-Z0-9]*)(\b[^>]*)>/i', '<$1>', '<html><goes><here>');

1
你可以使用正则表达式解析HTML。不是所有的HTML都可以,但如果你确切地知道你正在接收什么,你可以使用正则表达式。这是一场由那些假设无限堆栈和内存在所有情况下都可用的人发起的宗教战争。 - PP.
6个回答

22

这可能符合您的需求:

$text = '<p id="paragraph" class="green">This is a paragraph with an image <img src="/path/to/image.jpg" width="50" height="75"/></p>';

echo preg_replace("/<([a-z][a-z0-9]*)(?:[^>]*(\ssrc=['\"][^'\"]*['\"]))?[^>]*?(\/?)>/i",'<$1$2$3>', $text);

// <p>This is a paragraph with an image <img src="/path/to/image.jpg"/></p>

正则表达式的分解:

/              # Start Pattern
 <             # Match '<' at beginning of tags
 (             # Start Capture Group $1 - Tag Name
  [a-z]         # Match 'a' through 'z'
  [a-z0-9]*     # Match 'a' through 'z' or '0' through '9' zero or more times
 )             # End Capture Group
 (?:           # Start Non-Capture Group
  [^>]*         # Match anything other than '>', Zero or More Times
  (             # Start Capture Group $2 - ' src="...."'
   \s            # Match one whitespace
   src=          # Match 'src='
   ['"]          # Match ' or "
   [^'"]*        # Match anything other than ' or " 
   ['"]          # Match ' or "
  )             # End Capture Group 2
 )?            # End Non-Capture Group, match group zero or one time
 [^>]*?        # Match anything other than '>', Zero or More times, not-greedy (wont eat the /)
 (\/?)         # Capture Group $3 - '/' if it is there
 >             # Match '>'
/i            # End Pattern - Case Insensitive

添加一些引用,并使用替换文本<$1$2$3>,它应该从格式正确的HTML标签中剥离任何非 src= 属性。

请注意:这并不一定适用于所有输入,因为反HTML + RegExp的人正如下面所聪明地指出的那样。有一些回退选项,尤其是<p style=">">将会变成<p>">和其他一些问题... 我建议查看Zend_Filter_StripTags作为PHP中完整的tag/attribute过滤器。


除非在属性值中出现 >,否则解析恶意 HTML 是很困难的。此外,您忘记转义 \ - SLaks
我忘记转义哪个 \ 了? - gnarf
@gnarf,你能否请解释一下这个问题?如果我需要/保留多于1个属性(例如srcheight),那么我应该如何修改你的正则表达式呢?我的情况与这个问题完全相同。 - Qazi
@qazi - 使用HTML解析器或操作器...正则表达式不适合此任务,因为src和height可以以任何顺序出现,还有许多其他原因,您不应该使用正则表达式来解析HTML。 - gnarf
@gnarf,我想忽略hrefscr,你能指导一下吗? - Muhammad Hassaan
我肯定不会喜欢维护这个正则表达式(而我很喜欢正则表达式)。 - mickmackusa

8

5
有些人遇到问题时会想,“我知道,我会使用正则表达式。”现在他们有两个问题了。 - fmark
2
你可以使用正则表达式解析HTML。不是所有的HTML都可以,但如果你确切地知道你正在接收什么,你可以使用正则表达式。这是一场由那些假设无限堆栈和内存在所有情况下都可用的人发起的宗教战争。 - PP.
5
有些人有一个可怕的习惯,不回答问题,反而沉迷于口号。这个问题应该被宗教右翼贬低,而不是赞同。 - PP.
3
当一些人面临问题时,他们会想,“我知道了,我引用Jamie Zawinski的话。” 现在他们有两个问题。这确实是需要专门的标记解析器/处理器来处理的问题,这是非常正确的。但正则表达式是一个非常好的工具,可以适用于许多任务,包括某些标记处理任务,因此完全排斥它们是愚蠢的。 - Weston C
1
我必须同意PP的观点。因为给出了教条主义的答案而被踩了。如果你确切地知道自己要做什么,使用正则表达式解析HTML是完全可能的。DOMDocument在某些情况下非常好用,但并非所有情况都适用。 - Ian McIntyre Silber
@SLaks 虽然我同意这种情绪,但这个答案并不太慷慨。也许您可以通过在 https://dev59.com/WXA75IYBdhLWcg3w790Y#65741427 的嵌套循环中添加条件表达式来改进您的答案。或者您可以允许我的答案为您传递火炬,并为其提供更短的路径到页面顶部。我向您发出这个呼吁,因为显然声望对您已经没有价值了。 - mickmackusa

1

好的,这是我用过的看起来运行良好的内容:

<([A-Z][A-Z0-9]*)(\b[^>src]*)(src\=[\'|"|\s]?[^\'][^"][^\s]*[\'|"|\s]?)?(\b[^>]*)>

欢迎随意挑剔。


我不想浪费时间去挖洞,尽管在解析HTML文档时,正则表达式很容易被挖洞。我希望你能考虑接受我的干净、专业和健壮的答案。这不是因为我在乎虚假的积分,而是因为我希望研究人员能在这个旧页面上找到最好的解决方案。 - mickmackusa

0

不要使用正则表达式来解析有效的HTML。仅当所有可用的DOM解析器都失败时,才使用正则表达式来解析HTML文档。我非常喜欢正则表达式,但是正则表达式是“DOM无知”的,它会悄悄地失败和/或改变您的文档。

一般来说,我更喜欢使用DOMDocument和XPath混合使用,以简明、直观的方式来定位文档实体。

除了几个小例外,XPath表达式与其在普通英语中的逻辑非常相似。

//@*[not(name()="src")]

  • 在文档的任何级别上(//
  • 查找任何属性(@*
  • 满足这些要求([]
  • 而不是(not()
  • 命名为“src”(name()="src"

这样做更易读、美观、可维护。

代码:(演示

$html = <<<HTML
<p id="paragraph" class="green">
    This is a paragraph with an image <img src="/path/to/image.jpg" width="50" height="75"/>
</p>
HTML;

$dom = new DOMDocument;
$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$xpath = new DOMXPath($dom);
foreach ($xpath->query('//@*[not(name()="src")]') as $attr) {
    $attr->parentNode->removeAttribute($attr->nodeName);
}
echo $dom->saveHTML();

输出:

<p>
    This is a paragraph with an image <img src="/path/to/image.jpg">
</p>

如果您想添加另一个免除属性,可以使用or

//@*[not(name()="src" or name()="href")]

@Hassaan 请看我的回答底部,以保留 srchtml 属性的表达式。 - mickmackusa
@Qazi 同样的 ping 给你。 - mickmackusa

0

很遗憾,我不确定如何回答这个关于PHP的问题。如果我使用Perl,我会按照以下方式操作:

use strict;
my $data = q^<p id="paragraph" class="green">This is a paragraph with an image <img src="/path/to/image.jpg" width="50" height="75"/></p>^;

$data =~ s{
    <([^/> ]+)([^>]+)> # split into tagtype, attribs
}{
    my $attribs = $2;
    my @parts = split( /\s+/, $attribs ); # separate by whitespace
    @parts = grep { m/^src=/i } @parts;   # retain just src tags
    if ( @parts ) {
        "<" . join( " ", $1, @parts ) . ">";
    } else {
        "<" . $1 . ">";
    }
}xseg;

print( $data );

返回

<p>This is a paragraph with an image <img src="/path/to/image.jpg"></p>

-1

如上所述,你不应该使用正则表达式来解析 HTML 或 XML。

如果它总是相同的,我会用 str_replace() 来处理你的示例。

$str = '<p id="paragraph" class="green">This is a paragraph with an image <img src="/path/to/image.jpg" width="50" height="75"/></p>';

$str = str_replace('id="paragraph" class="green"', "", $str);

$str = str_replace('width="50" height="75"',"",$str);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接