通过PHP选择性地替换某些HTML标记,同时保留一些

时间:2016-03-19 01:32:02

标签: php regex replace preg-replace

我有一个包含html代码的表单,由于MCE编辑器的自动格式化无法禁用,因此偶尔会有点乱。

我已经用PHP进行了一些简单的替换,但是其他一些我不太确定。

删除所有 <span>代码,例如<span style="font-family: inherit; font-weight: inherit; line-height: 1.3;">,包括</span>对应代码,但这些代码内容。< / p>

例如:<span style="font-family: inherit; font-weight: inherit; line-height: 1.3;">StackOverflow</span> 会变成 StackOverflow

唯一应该删除 NOT 的是以下内容:

<span class="MainLink" style="font-weight: bold"><a href="https://website.com/" style="color: #2f82de; text-decoration: none">link name</a></span>

所以基本上任何封装<a href...链接。

任何想法如何做到这一点,我认为我需要使用正则表达式来做,但可能有一个更容易/更好的方法。

3 个答案:

答案 0 :(得分:2)

要执行此操作,您需要一个Parser,而不是正则表达式(另请参阅The Famous Answer

从此示例开始,使用DOMDocumentDOMXpath

$dom = new DOMDocument();
libxml_use_internal_errors(1);
$dom->formatOutput = True;
$dom->loadHTML( $html );
$xpath = new DOMXPath( $dom );

while( $node = $xpath->query( '//span[not(contains(@class,"MainLink"))]' )->item(0) )
{
    $fragment = $dom->createDocumentFragment();
    while( $node->childNodes->length ) 
    {
        $fragment->appendChild( $node->childNodes->item(0) );
    }
    $node->parentNode->replaceChild( $fragment, $node );
}

echo $dom->saveHTML();

这一行:

while( $node = $xpath->query( '//span[not(contains(@class,"leave"))]' )->item(0) )

<span>属性中搜索不包含“leave”的每个class节点:如果找到此模式,则执行循环(->item(0))。

然后您创建一个新的DOMDocumentFragment,一个特殊的临时节点,您可以在其中添加所有子节点:

    while( $node->childNodes->length ) 
    {
        $fragment->appendChild( $node->childNodes->item(0) );
    }

将所有节点子节点移动到新片段后,将空<span>节点替换为片段。

可以帮助您的其他有用的XPath:

  • //span[not(a)]:选择所有<span>节点后面没有<a>子节点;
  • //span[not(contains(@class,"leave")) and not(contains(@class,"yes"))]:选择<span>属性中没有“离开”或“是”的所有class节点。

答案 1 :(得分:1)

试试这个:

$output = preg_replace('/<span[^>]*>(?!<a[ >])|(?<!\/a>)<\/span>/', '', $input);

Regex101 Tested

这个正则表达式有 两部分

  1. 删除之后没有<span>的任何<a
  2. 删除之前没有</span>的任何/a>
  3. 注意:此解决方案是对问题的快速解决方案,并假定有效的HTML。可能会出现一些无法正常运行的情况,但OP可能没有任何这些情况(例如a内的自动关闭span标记。有关 占用的方案的演示,请参阅Regex101。

答案 2 :(得分:1)

编辑以切换捕获组

我总觉得做这样的事情真的很棘手,因为往往有很多不可预见的案件可能需要处理,或者他们会回来咬我。

话虽这么说,这种正则表达式的挑战通常很有趣。

我可能会尝试这样的事情:

(?:<span[^>]*?>)(?!<a)(.*?)(?:<\/span>)

在此处采取行动:https://regex101.com/r/qY8pL5/3

它的作用是首先尝试匹配span标记的开头,并找到开始标记最有可能结束的位置。这被放入非捕获组中,因此可以将其丢弃。接下来,它确保接下来的两个字符不是锚标记,因为不应剥离包裹锚的范围。下一部分是一个捕获组,它尽可能懒惰地抓住每个角色,直到它达到一个结束范围标记。结束范围标记也收集在非捕获组中,因此可以将其丢弃。

这将匹配包含在锚点中的独立span标记和span标记。它不会匹配包装锚点的span标签。

在php中你会像这样实现它:

$final_string = preg_replace('/(?:<span[^>]*?>)(?!<a)(.*?)(?:<\/span>)/', '${1}', $string);

第一个参数是我们的正则表达式,第二个是我们想要用来代替正则表达式匹配的东西 - 在这种情况下,第一个(在这种情况下只有)捕获组被保留 - 最后我们传递字符串我们希望与之匹敌。

请注意@ fusion3k是迄今为止编写的最佳答案,而不是为进行任何真正的HTML解析提供全面的方法。