Python URL的最长前缀匹配

Python URL的最长前缀匹配,python,url,trie,longest-prefix,Python,Url,Trie,Longest Prefix,我需要关于任何标准python包的信息,这些包可以用于URL上的“最长前缀匹配”。我已经看了两个标准包&'http://pypi.python.org/pypi/trie/0.1.1但它们对于URL上最长前缀匹配任务似乎没有用处 例如,如果我的集合有这些URL 1->http://www.google.com/mail , 2->http://www.google.com/document, 3->http://www.facebook.com等等 现在如果我搜索'http://www.goog

我需要关于任何标准python包的信息,这些包可以用于URL上的“最长前缀匹配”。我已经看了两个标准包&'http://pypi.python.org/pypi/trie/0.1.1但它们对于URL上最长前缀匹配任务似乎没有用处

例如,如果我的集合有这些URL 1->http://www.google.com/mail , 2->http://www.google.com/document, 3->http://www.facebook.com等等

现在如果我搜索'http://www.google.com/doc'然后它应该返回2并搜索'http://www.face'应该返回3

我想确认是否有任何标准python包可以帮助我完成这项工作,或者我应该实现一个用于前缀匹配的Trie

我不是在寻找一种正则表达式类型的解决方案,因为它不能随着URL数量的增加而伸缩


非常感谢。

下面的函数将返回最长匹配的索引。其他有用的信息也可以很容易地提取出来

from os.path import commonprefix as oscp

def longest_prefix(s, slist):
    pfx_idx = ((oscp([s, url]), i) for i, url in enumerate(slist))
    len_pfx_idx = map(lambda t: (len(t[0]), t[0], t[1]), pfx_idx)
    length, pfx, idx = max(len_pfx_idx)
    return idx

slist = [
    'http://www.google.com/mail',
    'http://www.google.com/document',
    'http://www.facebook.com',
]

print(longest_prefix('http://www.google.com/doc', slist))
print(longest_prefix('http://www.face', slist))

此示例适用于小url列表,但不能很好地扩展

def longest_prefix_match(search, urllist):
    matches = [url for url in urllist if url.startswith(search)]
    if matches:
        return max(matches, key=len)
    else:
        raise Exception("Not found")
使用该模块的实现

结果:

'http' -> ['http://www.facebook.com', 'http://www.google.com/document', 'http://www.google.com/mail']
'http://www.go' -> ['http://www.google.com/document', 'http://www.google.com/mail']
'http://www.fa' -> ['http://www.facebook.com']
'http://fail' -> []
或者使用相同的结果,但列表的顺序不同

from pytrie import StringTrie


url_list = [ 
    'http://www.google.com/mail',
    'http://www.google.com/document',
    'http://www.facebook.com',
]

url_trie = StringTrie()

for url in url_list:
    url_trie[url] = url 

searches = ("http", "http://www.go", "http://www.fa", "http://fail")

for search in searches:
    print "'%s' ->" % search, url_trie.values(prefix=search)
我开始认为从内存使用的角度来看,a会更好。这就是基数树的外观:

而trie看起来更像:

如果您愿意用RAM换取时间性能,那么可能会很有用。它具有很好的算法特性,例如它允许在线性时间内解决最长的公共子串问题

如果总是搜索前缀而不是任意子字符串,则可以在填充
SubstringDict()
时添加唯一的前缀:

这种使用
subfixtree
的方式似乎不太理想,但在我尝试过的数据上,它比[基于
.startswith()
]的构建时间快20-150倍(没有
SubstringDict()

要安装,请运行:

性能比较
subfixtree
vs.
pytrie
vs.
trie
vs.
datrie
vs.
startswith
-函数 安装程序 记录的时间是1000次搜索的3次重复中的最短时间。trie构建时间包含在所有搜索中,并分布在所有搜索中。搜索是在主机名集合(从1到1000000个项目)上执行的

搜索字符串的三种类型:

  • 不存在\u键
    -字符串不匹配
  • 稀有钥匙
    -百万分之二十左右
  • frequency_key
    -出现次数与集合大小相当
结果 一百万个URL的最大内存消耗:
| function    | memory, | ratio |
|             |     GiB |       |
|-------------+---------+-------|
| suffix_tree |   0.853 |   1.0 |
| pytrie      |   3.383 |   4.0 |
| trie        |   3.803 |   4.5 |
| datrie      |   0.194 |   0.2 |
| startswith  |   0.069 |   0.1 |
#+TBLFM: $3=$2/@3$2;%.1f
要复制结果

  • 罕见的密钥/不存在的密钥案例

    如果URL的数量少于10000,那么datrie是最快的,例如 N> 10000-
    suffextree
    速度更快,
    startwith
    平均速度明显较慢

  • 轴线:

    • 垂直(时间)刻度为~1秒(2**20微秒)
    • 横轴显示每种情况下的URL总数:N=1、10、100、1000、10000、100000和1000000(一百万)

  • 频密键

    最多N=100000
    datrie
    是最快的(对于一百万个URL,时间是 主要由trie施工时间决定)

    在找到的匹配项中查找最长的匹配项所花费的时间最多。因此,所有函数的行为都与预期类似

startswith
-时间性能与密钥类型无关

trie
pytrie
的行为相似

无trie构建时间的性能
  • datrie
    -最快、最合理的内存消耗

  • startswith
    在这里更为不利,因为其他方法不会因构建trie所需的时间而受到惩罚

  • datrie
    pytrie
    trie
    -对于稀有/不存在的密钥,几乎是O(1)(恒定时间)

拟合(近似)已知函数的多项式进行比较(与图中的对数/对数比例相同):


这将返回一个搜索索引,该索引不应匹配任何内容。很好,它没有检查零长度匹配。我没有时间润色它。非常感谢你的回复,但我不是在寻找正则表达式类的解决方案,因为它不能随着不同URL数量的增加而扩展。我正在寻找的是基于Trie的最长前缀匹配解决方案,其中字符串是URL。非常感谢您的回复,但我不寻找正则表达式类型的解决方案,因为它不能随着不同URL数量的增加而扩展。我正在寻找的是基于Trie的最长前缀匹配解决方案,其中字符串是URL。可能
return'
在这里更合适,而不是
raiseexception
@Amit fair。您如何存储URL列表?如果您在数据库中对它们进行索引,那么URL可能会提供一个简单而高效的解决方案@J.F.Sebastian好的,谢谢。@Stephen我没有将它们存储在数据库中,我有一个URL的列表,其中有一个唯一的随机数与之关联,现在我想将其存储在trie中,然后匹配一个新的URL并找出最接近的前缀匹配。这对您有帮助吗?帮助(str.startswith)如果搜索
http://www.google
?是否支持像“ww.google.com/d”这样的搜索?您希望它匹配整个搜索字符串,还是匹配搜索字符串中可能最长的前缀?换句话说,搜索“”会有效(返回1或2)还是失败?到目前为止,这是一个比我更好的解决方案。遗憾的是,标准python库中没有树实现。后缀树可以序列化吗?或者生成它们的速度太快,以至于无法
from SuffixTree import SubstringDict

substr_dict = SubstringDict()
for url in URLS: # urls must be ascii (valid urls are)
    assert '\n' not in url
    substr_dict['\n'+url] = url #NOTE: assume that '\n' can't be in a url

def longest_match(url_prefix, _substr_dict=substr_dict):
    matches = _substr_dict['\n'+url_prefix]
    return max(matches, key=len) if matches else ''
pip install SuffixTree -f https://hkn.eecs.berkeley.edu/~dyoo/python/suffix_trees
| function    | memory, | ratio |
|             |     GiB |       |
|-------------+---------+-------|
| suffix_tree |   0.853 |   1.0 |
| pytrie      |   3.383 |   4.0 |
| trie        |   3.803 |   4.5 |
| datrie      |   0.194 |   0.2 |
| startswith  |   0.069 |   0.1 |
#+TBLFM: $3=$2/@3$2;%.1f
| Fitting polynom              | Function          |
|------------------------------+-------------------|
| 0.15  log2(N)   +      1.583 | log2(N)           |
| 0.30  log2(N)   +      3.167 | log2(N)*log2(N)   |
| 0.50  log2(N)   +  1.111e-15 | sqrt(N)           |
| 0.80  log2(N)   +  7.943e-16 | N**0.8            |
| 1.00  log2(N)   +  2.223e-15 | N                 |
| 2.00  log2(N)   +  4.446e-15 | N*N               |