匹配regexp的URL在某些字符串上非常慢

时间:2012-08-17 12:35:53

标签: python regex url performance

这是我在某些字符串中查找URL的正则表达式(我需要域的组,因为进一步的操作基于域)我注意到在这个例子中有些字符串'fffffffff'很慢,有一些明显的东西我失踪了?

>>> URL_ALLOWED = r"[a-z0-9$-_.+!*'(),%]"
>>> URL_RE = re.compile(
...     r'(?:(?:https?|ftp):\/\/)?'  # protocol
...     r'(?:www.)?' # www
...     r'('  # host - start
...         r'(?:'
...             r'[a-z0-9]'  # first character of domain('-' not allowed)
...             r'(?:'
...                 r'[a-z0-0-]*'  #  characters in the middle of domain
...                 r'[a-z0-9]' #  last character of domain('-' not allowed)
...             r')*'
...             r'\.'  # dot before next part of domain name
...         r')+'
...         r'[a-z]{2,10}'  # TLD
...         r'|'  # OR
...         r'(?:[0-9]{1,3}\.){3}[0-9]{1,3}'  # IP address
...     r')' # host - end
...     r'(?::[0-9]+)?'  # port
...     r'(?:\/%(allowed_chars)s+/?)*'  # path
...     r'(?:\?(?:%(allowed_chars)s+=%(allowed_chars)s+&)*'  # GET params
...     r'%(allowed_chars)s+=%(allowed_chars)s+)?'  # last GET param
...     r'(?:#[^\s]*)?' % {  # anchor
...         'allowed_chars': URL_ALLOWED
...     },
...     re.IGNORECASE
... )
>>> from time import time
>>> strings = [
...     'foo bar baz',
...     'blah blah blah blah blah blah',
...     'f' * 10,
...     'f' * 20,
...     'f' * 30,
...     'f' * 40,
... ]
>>> def t():
...     for string in strings:
...             t1 = time()
...             URL_RE.findall(string)
...             print string, time() - t1
... 
>>> t()
foo bar baz 3.91006469727e-05
blah blah blah blah blah blah 6.98566436768e-05
ffffffffff 0.000313997268677
ffffffffffffffffffff 0.183916091919
ffffffffffffffffffffffffffffff 178.445468903

是的,我知道还有另一种解决方案可以使用非常简单的regexp(例如包含点的单词)并稍后使用urlparse来获取域名,但是当我们在URL中没有协议时urlparse不能正常工作:< / p>

>>> urlparse('example.com')
ParseResult(scheme='', netloc='', path='example.com', params='', query='', fragment='')
>>> urlparse('http://example.com')
ParseResult(scheme='http', netloc='example.com', path='', params='', query='', fragment='')
>>> urlparse('example.com/test/test')
ParseResult(scheme='', netloc='', path='example.com/test/test', params='', query='', fragment='')
>>> urlparse('http://example.com/test/test')
ParseResult(scheme='http', netloc='example.com', path='/test/test', params='', query='', fragment='')
>>> urlparse('example.com:1234/test/test')
ParseResult(scheme='example.com', netloc='', path='1234/test/test', params='', query='', fragment='')
>>> urlparse('http://example.com:1234/test/test')
ParseResult(scheme='http', netloc='example.com:1234', path='/test/test', params='', query='', fragment='')

是的,前面的http://也是一个解决方案(如果没有其他urlparse问题,我仍然不能100%确定)但我很好奇这个正则表达式有什么问题

2 个答案:

答案 0 :(得分:3)

我认为这是因为这一部分

...         r'(?:'
...             r'[a-z0-9]'  # first character of domain('-' not allowed)
...             r'(?:'
...                 r'[a-z0-0-]*'  #  characters in the middle of domain
...                 r'[a-z0-9]' #  last character of domain('-' not allowed)
...             r')*'
...             r'\.'  # dot before next part of domain name
...         r')+'

如果set_of_symbols#1和set_of_symbols#2具有相同的符号,则不应使用这样的结构([set_of_symbols#1] * [set_of_symbols#2])*。

请尝试使用以下代码:

...         r'(?:'
...             r'[a-z0-9]'  # first character of domain('-' not allowed)
...             r'[a-z0-0-]*'  #  characters in the middle of domain
...             r'(?<=[a-z0-9])' #  last character of domain('-' not allowed)
...             r'\.'  # dot before next part of domain name
...         r')+'

它应该更好。

答案 1 :(得分:0)

fyi,您还可以使用re.VERBOSE标志使其更具可读性

 URL_RE = re.compile(r"""
    (?:(?:https?|ftp):\/\/)?                            # protocol
    (?:www.)?                                           # www
    (                                                   # host - start
        (?:
            [a-z0-9]                                    # first character of domain('-' not allowed)
            (?:
                [a-z0-0-]*                              #  characters in the middle of domain
                [a-z0-9]                                #  last character of domain('-' not allowed)
            )*
            \.                                          # dot before next part of domain name
        )+
        [a-z]{2,10}                                     # TLD
        |                                               # OR
        (?:[0-9]{1,3}\.){3}[0-9]{1,3}                   # IP address
    )                                                   # host - end
    (?::[0-9]+)?                                        # port
    (?:\/%(allowed_chars)s+/?)*                         # path
    (?:\?(?:%(allowed_chars)s+=%(allowed_chars)s+&)*    # GET params
    %(allowed_chars)s+=%(allowed_chars)s+)?             # last GET param
    (?:#[^\s]*)?
""" % {  # anchor
         'allowed_chars': URL_ALLOWED
     },
     re.IGNORECASE|re.VERBOSE
 )