删除脚本和样式标签中的所有内容
Posted
技术标签:
【中文标题】删除脚本和样式标签中的所有内容【英文标题】:Remove everything within script and style tags 【发布时间】:2013-12-03 15:37:15 【问题描述】:我有一个名为 $articleText
的变量,它包含 html 代码。 <script>
和 <style>
html 元素中有 script
和 style
代码。我想扫描$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 标记:<div> <script> var str = '</div>this does NOT get removed'; </script> </div>
【参考方案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
),直到不再有 (->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! :)以上是关于删除脚本和样式标签中的所有内容的主要内容,如果未能解决你的问题,请参考以下文章