防止正则表达式中的灾难性回溯

时间:2017-04-26 18:46:45

标签: python regex python-2.7 backtracking

我有一个代码可以抓取一百万个网站并检测其主页上的联系信息。

由于某些原因,当我运行代码时,它会卡住并且在抓取大约60k请求后不会继续,我将我的数据库中的网站网址标记为status=done

我已经多次运行代码,但却遇到了大约60k的请求。

它不会卡在某个网站上。

这是正在使用的正则表达式

    emails = re.findall('[\w\.-]+@[\w-]+\.[\w\.-]+', lc_body)
    mobiles = re.findall(r"(\(?(?<!\d)\d{3}\)?-? *\d{3}-? *-?\d{4})(?!\d)|(?<!\d)(\+\d{11})(?!\d)", lc_body)
    abns = re.findall('[a][-\.\s]??[b][-\.\s]??[n][-\:\.\s]?[\:\.\s]?(\d+[\s\-\.]?\d+[\s\-\.]?\d+[\s\-\.]?\d+)', lc_body)

    licences = re.findall(r"(Licence|Lic|License|Licence)\s*(\w*)(\s*|\s*#\s*|\s*.\s*|\s*-\s*|\s*:\s+)(\d+)", lc_body, re.IGNORECASE)

我的想法是licences正则表达式导致问题,我该如何简化?如何删除回溯?

我想找到所有可能的许可证号码。

它可以是License No: 2543License: 2543License # 2543License #2543License# 2543以及许多其他组合。

1 个答案:

答案 0 :(得分:1)

问题是由第三组造成的:(\s*|\s*#\s*|\s*.\s*|\s*-\s*|\s*:\s+) - 所有替代方案都以\s*开头。这会导致大量冗余回溯,因为这些替代方案可以在字符串中的相同位置匹配。 最佳做法是在交替组中使用在同一位置不匹配的替代方案。

现在,看一下你需要匹配的字符串,我建议使用

Lic(?:en[cs]e)?(?:\W*No:)?\W*\d+

请参阅regex demo

使模式更具体和线性,尽可能多地删除alternations,使用optional non-capturing groups和字符类。

<强>详情:

  • Lic(?:en[cs]e)? - Lic后跟1或0次出现((?:...)?是可选的non-capturing group,因为?量词匹配量化的1或0次出现enceense(字符类[sc]匹配sc并且效率高于(s|c)
  • (?:\W*No:)? - 一个非捕获组,匹配1或0次出现的0 +非单词字符(含\W*),后跟No:子字符串
  • \W*
  • \d+ - 一位或多位数。