使用python将文本与大量正则表达式进行比较的最快方法是什么?

时间:2012-10-04 12:08:24

标签: python regex performance

我有一个正则表达式列表,我希望与他们共享的推文相匹配,以便我可以将它们与特定帐户相关联。如上所述的规则数量很少,它的速度非常快,但只要增加规则数量就会变得越来越慢。

import string, re2, datetime, time, array

rules = [
    [[1],["(?!.*ipiranga).*((?=.*posto)(?=.*petrobras).*|(?=.*petrobras)).*"]],
    [[2],["(?!.*brasil).*((?=.*posto)(?=.*petrobras).*|(?=.*petrobras)).*"]],
]

#cache compile
compilled_rules = []
for rule in rules:
    compilled_scopes.append([[rule[0][0]],[re2.compile(rule[1][0])]])

def get_rules(text):
    new_tweet = string.lower(tweet)
    for rule in compilled_rules:
        ok = 1
        if not re2.search(rule[1][0], new_tweet): ok=0
        print ok

def test():
    t0=datetime.datetime.now()
    i=0
    time.sleep(1)
    while i<1000000:
        get_rules("Acabei de ir no posto petrobras. Moro pertinho do posto brasil")
        i+=1
        t1=datetime.datetime.now()-t0
        print "test"
        print i
        print t1
        print i/t1.seconds

当我使用550规则进行测试时,我无法执行超过50个req​​s / s。这样做有更好的方法吗?我需要至少200 reqs / s

编辑: 在Jonathan的提示之后,我可以提高速度5倍,但是有点我的规则。请参阅以下代码:

scope_rules = {
    "1": {
        "termo 1" : "^(?!.*brasil)(?=.*petrobras).*",
        "termo 2" : "^(?!.*petrobras)(?=.*ipiranga).*",
        "termo 3" : "^(?!.*petrobras)(?=.*ipiranga).*",
        "termo 4" : "^(?!.*petrobras)(?=.*ipiranga).*",
        },
    "2": {
        "termo 1" : "^(?!.*ipiranga)(?=.*petrobras).*",
        "termo 2" : "^(?!.*petrobras)(?=.*ipiranga).*",
        "termo 3" : "^(?!.*brasil)(?=.*ipiranga).*",
        "termo 4" : "^(?!.*petrobras)(?=.*ipiranga).*",
        }
    }
compilled_rules = {}
for scope,rules in scope_rules.iteritems():
    compilled_rules[scope]={}
    for term,rule in rules.iteritems():
        compilled_rules[scope][term] = re.compile(rule)


def get_rules(text):
    new_tweet = string.lower(text)
    for scope,rules in compilled_rules.iteritems():
        ok = 1
        for term,rule in rules.iteritems():
            if ok==1:
                if re.search(rule, new_tweet):
                    ok=0
                    print "found in scope" + scope + " term:"+ term


def test():
    t0=datetime.datetime.now()
    i=0
    time.sleep(1)
    while i<1000000:
        get_rules("Acabei de ir no posto petrobras. Moro pertinho do posto ipiranga da lagoa")
        i+=1
        t1=datetime.datetime.now()-t0
        print "test"
        print i
        print t1
        print i/t1.seconds

cProfile.run('test()', 'testproof')

4 个答案:

答案 0 :(得分:4)

您的规则似乎是此处的罪魁祸首:由于两个.*由前瞻分隔,因此必须检查非常多的排列才能成功匹配(或排除匹配)。使用re.search()而不使用锚点会进一步加剧这种情况。此外,包含posto部分的替换是多余的 - 正则表达式匹配您的字符串中是否有任何posto,因此您可以完全放弃它。

例如,您的第一条规则可以重写为

^(?!.*ipiranga)(?=.*petrobras)

结果没有任何变化。如果您正在寻找确切的单词,可以使用单词边界进一步优化它:

^(?!.*\bipiranga\b)(?=.*\petrobras\b)

一些测量(使用RegexBuddy):

应用于字符串Acabei de ir no posto petrobras. Moro pertinho do posto brasil的第一个正则表达式使用正则表达式引擎大约4700步以找出匹配项。如果我取出s中的petrobras,则需要超过100.000步才能确定不匹配。

以230个步骤进行矿井匹配(并在260中失败),因此只需正确构建正则表达式即可获得20-400倍的加速。

答案 1 :(得分:1)

更进一步,我创建了一个Cython扩展来评估规则,现在它的速度非常快。我可以使用大约3000个正则表达式规则每秒执行大约70个请求

regex.pyx

import re2
import string as pystring

cpdef list match_rules(char *pytext, dict compilled_rules):
    cdef int ok, scope, term
    cdef list response = []
    text = pystring.lower(pytext)
    for scope, rules in compilled_rules.iteritems():
        ok = 1
        for term,rule in rules.iteritems():
            if ok==1:
                if re2.search(rule, text):
                    ok=0
                    response.append([scope,term])
    return response

python代码

import re2 as re
import datetime, time, cProfile

scope_rules = {1: {1 : "^(?!.*brasil)(?=.*petrobras).*", 2: "^(?!.*petrobras)(?=.*ipiranga).*",3 : "^(?!.*petrobras)(?=.*ipiranga).*",4 : "^(?!.*petrobras)(?=.*ipiranga).*",},2: {1 : "^(?!.*brasil)(?=.*petrobras).*", 2: "^(?!.*petrobras)(?=.*ipiranga).*",3 : "^(?!.*petrobras)(?=.*ipiranga).*",4 : "^(?!.*petrobras)(?=.*ipiranga).*",},}

compilled_rules = {}
for scope,rules in scope_rules.iteritems():
    compilled_rules[scope]={}
    for term,rule in rules.iteritems():
        compilled_rules[scope][term] = re.compile(rule)

def test():
    t0=datetime.datetime.now()
    i=0
    time.sleep(1)
    while i<1000000:
        mregex.match_rules("Acabei de ir no posto petrobras. Moro pertinho do posto brasil",compilled_rules)
        i+=1
        t1=datetime.datetime.now()-t0
        print t1
        print i/t1.seconds

cProfile.run('test()', 'testproof')

答案 2 :(得分:0)

除了优化你的正则表达式模式(这将产生巨大的差异),你可以尝试Google's RE2 - 它应该比Python的标准正则表达式模块更快。

它是用C ++完成的,但有PyRE2,Facebook的RE2的Python包装器:)

P.S。感谢您的提问,我在正则表达式匹配上找到了a great read

答案 3 :(得分:0)

除了@Tim Pietzcker的推荐

如果您有很多规则,那么尝试使用按共性分组的较小规则构建的分层方法可能是有意义的。

例如,上面的两个规则都匹配'posto'和'petrobras'。如果将这些正则表达式组合在一起列表中,然后将调度限定为该列表,则可以避免运行大量永不适用的规则。

在pseducode ....

# a dict of qualifier rules with list of rules 
rules = {
    "+ petrobras" : [
        "- ipiranga"
        "- brasil"
        "? posto"
    ] ,
}

for rule_qualifier in rules:
   if regex( rule_qualifier , text ):
       for rule in rule_qualifier:
           if regex( rule , text ):
               yay