【问题标题】:URLs matching regexp very slow on some strings在某些字符串上匹配正则表达式的 URL 非常慢
【发布时间】:2012-08-13 21:52:07
【问题描述】:

这是我在某些字符串中查找 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

是的,我知道还有另一种解决方案可以使用非常简单的正则表达式(例如包含点的单词)并稍后使用 urlparse 来获取域,但是当我们在 URL 中没有协议时 urlparse 无法按预期工作:

>>> 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:// 也是一种解决方案(我仍然不能 100% 确定是否没有其他 urlparse 问题),但我很好奇这个正则表达式有什么问题

【问题讨论】:

  • 那个正则表达式让我的大脑受伤了
  • 我的直觉是,任何具有大量 h 或 f(或更长的子字符串)的搜索目标都与其中的模式开头相匹配。您是否考虑过尝试通过对空白进行标记来预处理字符串,然后针对这些标记运行更简单的正则表达式?尝试将事物统一起来并不总是最快的方法。
  • urlparse 按预期工作。只是您传递的不是 URL。 “example.com”不是 URL,“myshellserver:22”也不是 URL。您必须准备好接受这种方法有时会产生误报,如果是这样,那么简单的 word-with-a-dot 正则表达式就可以了。否则我同意 IamChuckB
  • 正则表达式不会让我头疼,但是请PLEASE在制定长正则表达式时使用python的r"""..."""多行字符串语法!所有这些引用让我头疼。 (附注:你的正则表达式可以通过几个修复来解决......)请参阅我关于 URI 正则表达式的文章:Regular Expression URI Validation 我正在努力回答这个问题......
  • 这篇文章的错别字太多,所以我放弃了我的答案。但是对于初学者来说,你想修复你的 URL_ALLOWED,它有一个未转义的破折号,包括从'$''_' 的范围。速度问题是由于子域表达式中的灾难性回溯式结构(即(a*)*)造成的。 www 后面还有一个未转义的点。

标签: python regex url performance


【解决方案1】:

我认为这是因为这部分

...         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')+'

应该会更好。

【讨论】:

  • 谢谢,当更改此行并修复错别字时,@ridgerunner 发现它运行得非常快:)
【解决方案2】:

仅供参考,您还可以使用 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
 )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-06
    • 2018-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-20
    相关资源
    最近更新 更多