有效地搜索大量 URL

Posted

技术标签:

【中文标题】有效地搜索大量 URL【英文标题】:Efficiently searching a large list of URLs 【发布时间】:2016-06-23 17:19:37 【问题描述】:

我正在构建一个网络爬虫,它必须爬取数百个网站。我的爬虫保留了已爬取的网址列表。每当爬虫要爬取新页面时,它首先搜索已爬取的 url 列表,如果已经列出,则爬虫跳到下一个 url,依此类推。一旦 url 被抓取,它就会被添加到列表中。

目前,我正在使用二进制搜索来搜索 url 列表,但问题是一旦列表变大,搜索变得非常缓慢。所以,我的问题是,我可以使用什么算法来搜索 url 列表(列表的大小每天增长到大约 20k 到 100k)。

Crawler 目前是用 Python 编码的。但我打算将它移植到 C++ 或其他更好的语言上。

【问题讨论】:

为什么要标记java?另外,也许阅读一下 Trie。 鉴于您的列表已经排序,因为您使用的是二进制搜索,我认为您没有比二进制搜索更好的解决方案。您是否尝试过为程序的计算密集型部分计时。我的猜测是瓶颈可能不是搜索算法,而是排序算法? 你总是可以尝试使用字典——字典查找非常有效,因为它们是散列而不是检查字符串匹配(这真的很糟糕,因为 URL 通常会以几个相同的开头)。哈希搜索会更快,因为无论如何字符串比较都很慢。 如果你有足够的内存,你可以散列url然后腌制它 @StefanPochmann 太傻了,这就是我问堆栈溢出问题的原因 【参考方案1】:

您必须在某个时候决定您希望抓取的列表有多大。多达几千万个项目,您可能只需将 URL 存储在哈希映射或字典中,这样您就可以进行 O(1) 查找。

在任何情况下,平均 URL 长度约为 80 个字符(这是我五年前运行分布式爬虫时的经验),每 GB 只能获得大约 1000 万个 URL。因此,您必须开始考虑压缩数据或允许在一段时间后重新抓取。如果您每天只添加 100,000 个 URL,那么您需要 100 天才能抓取 1000 万个 URL。这可能足以让我们重新抓取。

如果这些是您的限制,那么我会建议使用 URL 键入的简单字典或哈希映射。该值应包含上次爬网日期以及您认为与保留相关的任何其他信息。将该数据结构限制为 1000 万个 URL。它可能会占用将近 2 GB 的空间,还有字典开销等等。

您必须定期修剪它。我的建议是设置一个每天运行一次的计时器,并清除超过 X 天前抓取的所有 URL。在这种情况下,您可能会将 X 设置为 100。这样您可以在 100 天内每天访问 100,000 个 URL。

如果您开始谈论每天处理数百万个 URL 的高容量爬虫,那么您就会涉及到更多涉及的数据结构和管理复杂性的创造性方法。但从你提问的语气来看,这不是你感兴趣的。

【讨论】:

问题说“每天增长大约 20k 到 100k”,但不是“增长” @StefanPochmann:我最初认为意思是“成长”。可能是因为我不明白为什么人们会担心小到 100 K 的数字。我误解了,OP 真的在问如何存储多达 100K 的 URL 列表并有效地搜索它。 @JimMischel 我的真正意思是我们抓取了数百个大型网站(ebay 等),有时我们会获得 20k 的新页面,有时多达 100k。然后将这些爬取后的新页面添加到列表中。所以正确的词应该是“Grows by 20k to 100k”。【参考方案2】:

我认为在将您的值放入二进制搜索列表之前对其进行散列 - 这将摆脱字符串比较的可能瓶颈,交换为 int 相等检查。它还保持 O(log2(n)) 二进制搜索时间 - 如果您在运行之间使用 python 的内置 hash(),您可能无法获得一致的结果,但是 - 它是特定于实现的。在一次运行中,它将是一致的。始终可以选择实现自己的哈希,这也可以在会话之间保持一致。

【讨论】:

还有哈希冲突的问题。您需要一个 64 位哈希。在几百万个 URL 之后,与 32 位散列的冲突数量将是可怕的。

以上是关于有效地搜索大量 URL的主要内容,如果未能解决你的问题,请参考以下文章

有效地解析具有部分的大量日志

如果不存在,Android SQLite 会有效地添加大量数据

使用不同的列集更有效地更新大量行?

正则表达式是不是有效地搜索 int 列?

使用 PHP 有效地使用 Google App Engine 发送大量电子邮件?

如何通过键从 URL 中有效地删除查询字符串?