如何使用正则表达式匹配多个脚本标签?

14

我正在尝试返回文本中所有<p>标签的内容。我目前使用以下表达式,但它只捕获第一个<p>标签的内容,并忽略其后的任何标签。

这是html的样例:

    <script type="text/javascript">
        alert('1');
    </script>

    <div>Test</div>

    <script type="text/javascript">
        alert('2');
    </script>

我的正则表达式看起来像这样:

//scripttext contains the sample
re = /<script\b[^>]*>([\s\S]*?)<\/script>/gm;
var scripts  = re.exec(scripttext);

当我在IE6上运行此代码时,它返回2个匹配项。第一个包含完整的"script"标记,第二个包含alert('1')。
当我在http://www.pagecolumn.com/tool/regtest.htm上运行它时,它给我2个结果,每个结果只包含脚本标记。

你是在 JavaScript 中实际编写正则表达式吗?能否包含匹配代码。 - cdm9002
使用RegexBuddy 3.2.1,这个很好用。它可以捕获两个标签的内容。 - Phoexo
我正在使用 /gm。我稍微修改了正则表达式。现在它返回2个结果,每个结果都包含一个脚本标签,但它包括HTML。<script\b[^>]>([\s\S]?)</script>/gm如何只返回内容? - Geuis
6个回答

47

这里的“问题”在于exec的工作方式。它只匹配第一次出现,但将当前索引(即插入符号位置)存储在正则表达式的lastIndex属性中。为了获取所有匹配项,只需将正则表达式应用于字符串,直到无法匹配为止(这是一种非常常见的方法):

var scripttext = ' <script type="text/javascript">\nalert(\'1\');\n</script>\n\n<div>Test</div>\n\n<script type="text/javascript">\nalert(\'2\');\n</script>';

var re = /<script\b[^>]*>([\s\S]*?)<\/script>/gm;

var match;
while (match = re.exec(scripttext)) {
  // full match is in match[0], whereas captured groups are in ...[1], ...[2], etc.
  console.log(match[1]);
}

<script>alert('</script>. Damn it, foiled again!');</script> - Svante
@Svante,这个怎么样? :) - kangax
1
@kangax,@Svante想说的是,你的正则表达式在他的代码示例中会失败。因为他得到了一个包含</script>的字符串值。 - Jekis

5

不要使用正则表达式解析HTML。HTML不是一个正则语言。使用DOM的强大功能。这样更容易,因为它是正确的工具。

var scripts = document.getElementsByTagName('script');

总有理由想要手动解析字符串中的DOM。例如,如果您尝试使用innerHTML,IE8会吹走脚本标记。如果我正在使用模块化小部件和HTML模板构建应用程序,这将成为一个问题。 - user2867288
1
有时候在将HTML字符串转换为DOM之前,需要对其进行清理。 - Yuval A.
@YuvalA.:两种可能性:1. 这是无效的HTML;那么你需要一个"标签汤解析器"。2. 这是有效的HTML;那么你需要一个HTML解析器。无论哪种情况,你都可以在解析后使用简单的查询语法。 - Svante
如果您只想删除脚本,可以使用例如 jQuery.parseHTML。 - Svante
@Svante,jQuery.parseHTML不会删除内联事件处理程序。我曾经制作了一个Firefox扩展程序,从Wikipedia API获取HTML字符串并从中创建DOM。Mozilla的人一直拒绝它,因为缺乏净化。HTML解析器总是首先从字符串创建DOM结构,而他们简单地不允许在将字符串转换为DOM之前“清理”它... - Yuval A.
好的,那么你无论如何都需要使用一个HTML解析器。 - Svante

3
尝试使用全局标志:

document.body.innerHTML.match(/<script.*?>([\s\S]*?)<\/script>/gmi)

编辑:添加了多行和不区分大小写标志(出于显而易见的原因)。


或者,如果您正在使用正则表达式函数,请确保其配置为捕获所有匹配项。其中一些需要多次调用,或者需要额外的参数或差异函数进行调用。 - TheJacobTaylor
@TheJacobTaylor 这似乎有点模糊。除了 new RegExp 之外,你指的是哪个正则表达式函数? - Justin Johnson
@Justin Johnson 我的评论部分是受到上面有关正则表达式所使用的语言的问题的驱动。由于我不确定,而且他们只得到了一个结果,我认为他们可能会因为调用错误的函数而受到影响。例如,在 PHP 中,preg_match 和 preg_match_all 将返回第一个或所有匹配项。 - TheJacobTaylor
啊,非常好。我猜是JavaScript。当我看到这个问题时,它被标记为这样,但我不确定。 - Justin Johnson

1
第一组包含标签的内容。
编辑:难道你不需要用引号将正则表达式语句括起来吗?像这样:
re = "/<script\b[^>]*>([\s\S]*?)<\/script>/gm";

不,你不需要。在Javascript中,“/…/”表示正则表达式。如果必须将其构建为字符串,则必须在其构造上更加明确。例如:/<script\b[^>]*>([\s\S]*?)<\/script>/g 等同于 new RegExp("<script\b[^>]*>([\s\S]*?)<\/script>", "g") - Justin Johnson

0

试试这个

for each(var x in document.getElementsByTagName('script');
     if (x && x.innerHTML){
          var yourRegex = /http:\/\/\.*\.com/g;
          var matches = yourRegex.exec(x.innerHTML);
             if (matches){
          your code
 }}

这个问题已经有一个被接受的答案,可以完成所需的任务。 - random_user_name

0
在 .Net 中,有一个 submatch 方法,在 PHP 中是 preg_match_all,可以解决您的问题。在 Javascript 中没有这样的方法。但你可以自己创造。
在以下网站测试 http://www.pagecolumn.com/tool/regtest.htm 选择 $1elements 方法将返回您想要的内容。

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