Node.js dom-parser tagRegExp正则表达式匹配项挂起:灾难性的回溯?

时间:2019-02-05 21:21:02

标签: javascript node.js regex dom

我正在使用node.js dom-parser,该分析器(不理想地)使用正则表达式将标签从DOM中拉出。

您可以在以下位置找到dom-parser: https://github.com/ershov-konst/dom-parser

有时,某些网页的HTML(例如https://www.ecosia.org/)会导致node.js应用程序挂起。我已经使用普通的香草匹配脚本进行了测试,发现tagRegExp导致脚本挂起(也许是由于灾难性的回溯?)

我实际上是用它来查找链接rel =“ canonical”和href =“ xyz”(如果有的话,ecosia则没有规范)。

tagRegExp:

/(<\/?[a-z][a-z0-9]*(?::[a-z][a-z0-9]*)?\s*(?:\s+[a-z0-9-_]+=(?:(?:'[\s\S]*?')|(?:"[\s\S]*?")))*\s*\/?>)|([^<]|<(?![a-z\/]))*/gi

纯JS测试脚本:

<script type="text/javascript">
var text = '... html source ...';
var text_esc = text
text_esc = text_esc.replace(/\</g, "&lt;");
text_esc = text_esc.replace(/\>/g, "&gt;");
var regex = /(<\/?[a-z][a-z0-9]*(?::[a-z][a-z0-9]*)?\s*(?:\s+[a-z0-9-_]+=(?:(?:'[\s\S]*?')|(?:"[\s\S]*?")))*\s*\/?>)|([^<]|<(?![a-z\/]))*/gi;
var found = text.match(regex);
var found_len = found.length;

document.write("Text: " + text_esc + "<br /><br />" + "Regex pattern: " + regex + "<br /><br />");

document.write("Matches: " + found_len + "<br /><br />");

for (var i=0;i<found_len;i++)
{
    found[i] = found[i].replace(/\</g, "&lt;");
    found[i] = found[i].replace(/\>/g, "&gt;");

    document.write("[" + i + "]: " + found[i] + "<br /><br />");
}
</script>

任何想法都值得欢迎。预先感谢。

1 个答案:

答案 0 :(得分:1)

问题是由[\s\S]*?模式和效率低下的(x|[^x])*类似模式引起的。

您可以使用

/(<\/?[a-z][a-z0-9]*(?::[a-z][a-z0-9]*)?\s*(?:\s+[a-z0-9-_]+=(?:'[^']*'|"[^"]*"))*\s*\/?>)|[^<]*(?:<(?![a-z\/])[^<]*)*/gi

'[\s\S]*?'变成'[^']*',其中[^']*是一个贪婪量化的否定字符类,与除'以外的任何字符相匹配,而"[\s\S]*?"在一样的方法。否定的字符类比.*?懒惰的字符类要好,因为它可以一次性匹配除指定字符之外的所有字符,并且正则表达式引擎不必在此模式之后尝试所有后续子模式,然后展开每个子模式失败后的时间。

([^<]|<(?![a-z\/]))*可以和[^<]*(?:<(?![a-z\/])[^<]*)*一样unrolled,它将匹配相同的文本,但是速度更快(与以前一样,带有贪婪的量词的否定字符类模式将更快地通过文本)

请注意,我还删除了几个冗余的非捕获组。