3

这是我在某个字符串中查找 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 问题)但我很好奇这个正则表达式有什么问题

4

2 回答 2

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

它应该工作得更好。

于 2012-08-17T14:59:12.097 回答
0

仅供参考,您还可以使用 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
 )
于 2012-08-17T16:01:11.073 回答