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

Prevent Catastrophic Backtracking in Regex

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

出于某些原因,当我 运行 编码时,它卡住了并且在抓取大约 60k 个请求后无法继续,我将我的数据库中的网站 URL 标记为 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等多种组合。

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

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

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

regex demo

使模式更加具体和线性,摆脱尽可能多的 alternations as possible, use 和字符 class。

详情:

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