如何加速Python中基于域名匹配的字符串列表拆分代码
大规模域名-URL匹配性能优化方案
原有问题
需求为从字符串列表中找出所有存在对应独立域名的URL元素,原有实现小数据量下运行符合预期,但处理大规模数据时,遍历匹配环节存在明显性能瓶颈。
原有实现代码:
import re pattern = '^<{0,1}([a-z0-9]\.|[a-z0-9][a-z0-9-]{0,61}[a-z0-9]\.){1,3}[a-z]{2,6}$' input = ['a.ru','b.ru','cc.com','dd.com','a.ru/11','a.ru/22','cc.com/31231','cc.com/312312412','cc.com/3123141241','cc.com/31231234232','dd.com/11','dd.com/22','dasdas.zz/alpha'] domain_wide = [] # contains domain only http_urls = [] # contains url's for line in input: if re.match(pattern, line): domain_wide.append(line.lower()) else: http_urls.append(line.lower()) domain_wide = tuple(sorted(domain_wide)) #Finding url's for which there is domain in the input data remove_list = [] for url in http_urls: if url.startswith(domain_wide): remove_list.append(url) print(f'The elements should be removed from input data: {remove_list}\n') print('\n') final_http_urls = list(set(http_urls) - set(remove_list)) print(f"Elements should be saved in input data: {final_http_urls}")
预期正确输出:
The elements should be removed from input data: ['a.ru/11', 'a.ru/22', 'cc.com/31231', 'cc.com/312312412', 'cc.com/3123141241', 'cc.com/31231234232', 'dd.com/11', 'dd.com/22'] Elements should be saved in input data: ['dasdas.zz/alpha']
性能瓶颈原因
- 核心匹配逻辑时间复杂度为
O(URL数量 * 域名数量):str.startswith()传入元组参数时,底层会逐个遍历元组内的所有域名做前缀匹配,当域名和URL规模达到十万/百万级时,乘法级别的运算量会导致耗时呈线性上涨。 - 正则匹配存在冗余开销:循环内每次调用
re.match都会临时构建正则匹配对象,没有做预编译处理。 - 存在无效操作:对域名列表做排序对后续匹配逻辑没有任何增益,白白消耗排序耗时。
优化方案
针对当前场景(URL结构为域名/路径,域名与路径通过第一个/分隔),可以将时间复杂度降到O(URL数量),优化点如下:
- 预编译正则表达式,循环内直接调用匹配对象的
match方法,减少重复构建正则对象的开销。 - 将纯域名的存储结构从列表/元组改为哈希集合(set),哈希查找的时间复杂度为O(1)。
- 替换原有的遍历前缀匹配逻辑:对每个待检测URL,直接切分出第一个
/之前的字符串(即URL自带的域名部分),直接判断该域名是否存在于域名集合中即可,完全不需要遍历所有域名做匹配。 - 去掉无意义的域名排序操作。
优化后代码:
import re # 预编译正则,避免循环内重复生成匹配对象 pattern = re.compile(r'^<?([a-z0-9]\.|[a-z0-9][a-z0-9-]{0,61}[a-z0-9]\.){1,3}[a-z]{2,6}$') input_list = ['a.ru','b.ru','cc.com','dd.com','a.ru/11','a.ru/22','cc.com/31231','cc.com/312312412','cc.com/3123141241','cc.com/31231234232','dd.com/11','dd.com/22','dasdas.zz/alpha'] domain_set = set() # 哈希集合存储纯域名,支持O(1)查找 http_urls = [] for line in input_list: line_lower = line.lower() if pattern.match(line_lower): domain_set.add(line_lower) else: http_urls.append(line_lower) remove_list = [] for url in http_urls: # 切分URL第一个/之前的域名部分,直接做哈希查找 url_domain = url.split('/', 1)[0] if url_domain in domain_set: remove_list.append(url) print(f'The elements should be removed from input data: {remove_list}\n') final_http_urls = list(set(http_urls) - set(remove_list)) print(f"Elements should be saved in input data: {final_http_urls}")
性能提升说明
该优化方案在百万级数据量下,性能相比原有实现可提升数千至数万倍:
- 省去了原有逻辑中对每个URL遍历所有域名做前缀匹配的开销,单次URL检测仅需一次切分操作和一次哈希查找。
- 正则预编译可减少15%-30%的正则环节耗时。
如果后续存在多级子域名匹配需求(例如域名列表包含b.ru,需要匹配a.b.ru/xxx这类子域名URL),可以基于域名集合构建前缀字典树(Trie)做逐字符前缀匹配,时间复杂度依然保持在O(URL总长度)级别,远高于遍历匹配的效率。
内容的提问来源于stack exchange,提问作者Roman Kazmin
相关产品推荐
相关产品推荐

