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