删除脚本和样式标签中的所有内容

Posted

技术标签:

【中文标题】删除脚本和样式标签中的所有内容【英文标题】:Remove everything within script and style tags 【发布时间】:2013-12-03 15:37:15 【问题描述】:

我有一个名为 $articleText 的变量,它包含 html 代码。 <script><style> html 元素中有 scriptstyle 代码。我想扫描$articleText 并删除这些代码。如果我还可以删除实际的 html 元素 <script></script><style></style>,我也会这样做。

我想我需要使用正则表达式,但我不熟练。

有人可以帮忙吗?

我希望我可以提供一些代码,但就像我说的我不擅长正则表达式,所以我没有任何东西可以展示。

我不能使用 DOM。我需要专门针对这些特定标签使用正则表达式

【问题讨论】:

对于那些认为正则表达式不可能与 html 和谐相处的人,这里有一个来自反反对者部门<(?P<tagname>style|script)[^>]*>(?:(?:(?!<(?&tagname)[^>]*>|</(?&tagname)>).)|(?R))*</(?&tagname)> 的解决方案。请参阅带有一些解释的demo。用有效的 html 打破它,我会在一个答案上奖励你 50 代表(赏金)。 @HamZa - 用<style> <!-- </style> --> * display: none </style>测试它 @Ωmega 必须保留my promise 大声笑 @HamZa 出于好奇,为什么要如此努力地去做从根本上是不好的做法?你以为你把它锁起来了,欧米茄一口气把它弄坏了。这将不断发生,一遍又一遍。这就是为什么人们根本不在 HTML 上使用 RegEx。反对它的建议不是***的编程,它是基于这样一个事实,即你正在发现困难的方式——因为 HTML 不是一种常规语言,所以有太多潜在的问题。 @HamZa 我在我的个人资料中说得对:我反对使用真的很酷的东西 X,因为它“为你做所有的事情!!!111”,buuut....在这种情况下, DomDocument 是一个非常酷的东西。只要你在这方面的工作是本着有趣和学习的精神,那么我同意。 [讽刺]我知道我的认可对你很重要[/讽刺]:P 【参考方案1】:

不要在 HTML 上使用 RegEx。 php 提供了一个解析 DOM 结构的工具,称为 DomDocument。

<?php
// some HTML for example
$myHtml = '<html><head><script>alert("hi mom!");</script></head><body><style>body  color: red; </style><h1>This is some content</h1><p>content is awesome</p></body><script src="someFile.js"></script></html>';

// create a new DomDocument object
$doc = new DOMDocument();

// load the HTML into the DomDocument object (this would be your source HTML)
$doc->loadHTML($myHtml);

removeElementsByTagName('script', $doc);
removeElementsByTagName('style', $doc);
removeElementsByTagName('link', $doc);

// output cleaned html
echo $doc->saveHtml();

function removeElementsByTagName($tagName, $document) 
  $nodeList = $document->getElementsByTagName($tagName);
  for ($nodeIdx = $nodeList->length; --$nodeIdx >= 0; ) 
    $node = $nodeList->item($nodeIdx);
    $node->parentNode->removeChild($node);
  

你可以在这里试试:https://eval.in/private/4f225fa0dcb4eb

文档

DomDocument - http://php.net/manual/en/class.domdocument.php DomNodeList - http://php.net/manual/en/class.domnodelist.php DomDocument::getElementsByTagName - http://us3.php.net/manual/en/domdocument.getelementsbytagname.php

【讨论】:

这没有得到最后一个脚本标签...只有第一次出现。 这正是我想要的。谢谢! 请注意,这会在使用 loadHTML() 时破坏 DOMDocument 解析,因为 javascript 字符串中有 HTML 标记:&lt;div&gt; &lt;script&gt; var str = '&lt;/div&gt;this does NOT get removed'; &lt;/script&gt; &lt;/div&gt;【参考方案2】:

即使是正则表达式也不是这种任务的好工具,对于小而简单的任务它可能会起作用。


如果您只想删除标签的内部文本,请使用:

preg_replace('/(<(script|style)\b[^>]*>).*?(<\/\2>)/is', "$1$3", $txt);

查看演示here

如果你还想删除标签,上面代码中的替换字符串将为空,所以只需""

【讨论】:

似乎有人对所有正则表达式的答案投了反对票,我的哀悼 是的,真可惜,尤其是当提问者专门要求正则表达式答案时。 @HamZa - 我已经习惯了。几个减分对我的声誉没有影响。 OP 要求正则表达式解决方案,所以我给了他一个。我的回答清楚地表明正则表达式不是正确的工具,所以我认为反对者甚至是坏读者,或者只是那些愤怒的人之一:) 当然,我明白了。但是如果没有某种理由,“我不能使用 DOM”就和“我不能使用字母 'e'”一样有效——愚蠢的人为限制。当有人问“我如何使用 ______ 来做到这一点”时,我的第一个问题是“你甚至需要使用 ______”吗?很多时候,提问者不知道存在更好的东西,或者错误地认为它太难处理了。嗯。再说一次,可能不值得投反对票,但我也有 DV,所以我怀疑有人试图成为“战略”。 @Chris - 我也明白你的意思。当然,OP 应该在他/她的帖子中更加具体。具有讽刺意味的是,他/她的帖子(到目前为止)没有得到反对意见,但我看到很多反对意见的答案。【参考方案3】:

我认为这应该可以满足您的需要(假设没有嵌套的脚本和样式标签):

preg_replace('/(<script[^>]*>.+?<\/script>|<style[^>]*>.+?<\/style>)/is', '', $articleText);

【讨论】:

PHP 中没有 g 修饰符。匹配时,您有preg_match(),它将仅匹配第一次出现。在 javascript 等其他语言中,您使用 g 修饰符来匹配所有内容。在 php 中,您只需使用另一个函数 preg_match_all()。现在更换怎么样? preg_replace() 默认替换所有内容。您可以添加第四个参数来限制它。 对不起,我太习惯写 javascript 正则表达式了。我已经更新了答案。 我忘记了一些重要的事情,不要忘记使用勉强(不贪婪)的量词。将.+ 替换为.+?。无论如何+1 :)【参考方案4】:

这里是示例数据:

$in = '
<html>
    <head>
        <script type="text/javascript">window.location="somehwere";</script>
        <style>
            .someCSS border:1px solid black;
        </style>
    </head>
    <body>
        <p>....</p>
        <div>
            <script type="text/javascript">document.write("bad stuff");</script>
        </div>
        <ul>
            <li><style type="text/css">#moreCSS font-weight:900;</style></li>
        </ul>
    </body>
</html>';

现在是拼写版本:

$dom = new DOMDocument('1.0','UTF-8');
$dom->loadHTML($in);

removeByTag($dom,'style');
removeByTag($dom,'script');

var_dump($dom->saveHTML());

function removeByTag($dom,$tag) 
    $nodeList = $dom->getElementsByTagName($tag);
    removeAll($nodeList);


function removeAll($nodeList) 
    for ( $i = $nodeList->length; --$i >=0; ) 
        removeSelf($nodeList->item($i));
    


function removeSelf($node) 
    $node->parentNode->removeChild($node);

还有一个替代品(做同样的事情,只是没有函数声明):

$dom = new DOMDocument('1.0','UTF-8');
$dom->loadHTML($in);

for ( $list = $dom->getElementsByTagName('script'), $i = $list->length; --$i >=0; ) 
    $node = $list->item($i);
    $node->parentNode->removeChild($node);


for ( $list = $dom->getElementsByTagName('style'), $i = $list->length; --$i >=0; ) 
    $node = $list->item($i);
    $node->parentNode->removeChild($node);


var_dump($dom->saveHTML());

诀窍是iterate backwards when deleting nodes。 getElementsByTagName 会为你遍历整个 DOM,所以你不必这样做(没有一个 hasChildNodes、nextSibling、nextChild 的东西)。

也许最好的解决方案介于这两个极端示例之间。


忍不住,这可能是我建议的最佳版本。它不包括一个增量器 ($i) 来搞砸事情,并从自下而上移除:

$dom = new DOMDocument('1.0','UTF-8');
$dom->loadHTML($in);

removeElementsByTagName($dom,'script');
removeElementsByTagName($dom,'style');

function removeElementsByTagName($dom,$tagName) 
    $list = $dom->getElementsByTagName($tagName);
    while ( $node = $list->item(0) ) 
        $node->parentNode->removeChild($node);
    


var_dump($dom->saveHTML());

当您删除节点时,它们会在父节点的子列表中上移,因此 1 变为 0,2 变为 1,依此类推。继续这样做 (while),直到不再有 (-&gt;item returns null) .还将它包装在一个可重用的函数中。

【讨论】:

第二个参数是关于允许的标签。这与他想要的相反...... @HamZa 完全忘记了那个,更新了一个正确的答案。【参考方案5】:

假设这既是为了不让您的设计被随机样式弄乱,又是为了保护您的网站免受用户脚本的影响,那么仅删除这些标签并不能保证您的安全。

考虑event attributes 的情况(例如:onmouseover、onclick):

<h1 onclick="console.log('user made this happen');">User Scripting Test</h1>

甚至更糟

<h1 onclick='function addCs-s-rule(a,b,c,d)"insertRule"in a?a.insertRule(b+""+c+"",d):"addRule"in a&&a.addRule(b,c,d)var style=document.createElement("style");style.appendChild(document.createTextNode("")),document.head.appendChild(style),sheet=style.sheet,addCs-s-rule(sheet,"*","color: #ff0!important");'>Messing with your styles!</h1>

有了这个,开始在文档中插入各种东西是相当简单的。

来自 David Walsh -https://davidwalsh.name/add-rules-stylesheets 的样式表模块的最后一个示例

唯一的解决方案

... 是使用经过验证的专门从事此操作的第三方库。我建议HTML Purifier。它将消除您的用户输入的样式、脚本和讨厌的事件属性。

【讨论】:

【参考方案6】:

执行此操作的正则表达式将非常迟钝,因为标签中可能存在标签,以及标签属性等混淆结构。

我建议在 DOM 中执行此操作(在 PHP 或 JavaScript 中),它可以通过实际解析来识别和删除不需要的标签。

【讨论】:

我不能。它必须是专门扫描这些标签的正则表达式。脚本或样式标签中不应有嵌套标签 @jkushner,是的...只需遍历 DOMDocument 并删除匹配 tagName "script" 和 "style" 的 DOMNode/DOMElement 对象 @zamnuts 听起来很棒。你能提供这个代码的答案吗? @jkushner 你能告诉我们为什么它必须是正则表达式吗? @jkushner 我已经演示过使用 DomDocument --- 不要使用 RegEx! :)

以上是关于删除脚本和样式标签中的所有内容的主要内容,如果未能解决你的问题,请参考以下文章

解析目录中的 html 文件并使用 BeautifulSoup 删除特定标签

Powershell 删除字符串内容中的 HTML 标签

Mongoose中的级联样式删除

Mongoose中的级联样式删除

删除ggplot中的所有x轴标签[重复]

HTML的HEAD标签问题