【发布时间】: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