如何检查任务是否已在python队列中?

如何检查任务是否已在python队列中?,python,multithreading,queue,Python,Multithreading,Queue,我正在使用线程和队列模块用Python编写一个简单的爬虫程序。我获取一个页面,检查链接并将它们放入队列中,当某个线程处理完页面后,它从队列中抓取下一个页面。我正在为我已经访问过的页面使用一个数组来过滤我添加到队列中的链接,但是如果有多个线程,并且它们在不同的页面上获得相同的链接,它们会将重复的链接放到队列中。那么,我如何才能确定某个url是否已经在队列中,以避免将其再次放在队列中呢?我解决这个问题的方法(实际上我是在Scala中完成的,而不是Python)是同时使用集合和队列,如果集合中不存在链

我正在使用线程和队列模块用Python编写一个简单的爬虫程序。我获取一个页面,检查链接并将它们放入队列中,当某个线程处理完页面后,它从队列中抓取下一个页面。我正在为我已经访问过的页面使用一个数组来过滤我添加到队列中的链接,但是如果有多个线程,并且它们在不同的页面上获得相同的链接,它们会将重复的链接放到队列中。那么,我如何才能确定某个url是否已经在队列中,以避免将其再次放在队列中呢?

我解决这个问题的方法(实际上我是在Scala中完成的,而不是Python)是同时使用集合和队列,如果集合中不存在链接,则只向队列(和集合)添加链接

集合和队列都封装在一个线程中,只向使用者线程公开一个类似队列的接口


编辑:如果访问的URL集需要扩大,其他人建议使用SQLite,这也是我正在考虑的。(目前每个爬网只有几百页,因此很容易放入内存。)但数据库本身也可以封装在集合中,因此消费者线程不需要知道它。

SQLite使用非常简单,非常适合。。。只是一个建议。

此外,您可以尝试使用字典,而不是一套。当集合较大时,对集合的操作往往会变得相当慢,而字典查找又好又快

My 2c.

使用:

url in q.queue
它返回真实的iff
url
在队列中

为什么只使用数组(理想情况下,字典会更好)来过滤您已经访问过的内容?一旦您将内容排入队列,就立即将其添加到您的数组/字典中,并且仅当它们不在数组/字典中时才将其添加到队列中。这样您就有3个简单的独立内容:

  • 尚未看到链接(既不在队列中也不在数组/dict中)
  • 计划访问的链接(在队列和数组/dict中)
  • 已访问的链接(在数组/目录中,不在队列中)

  • 如果您不关心项目的处理顺序,我会尝试使用
    Queue
    的子类,该子类在内部使用
    set

    class SetQueue(Queue):
    
        def _init(self, maxsize):
            self.maxsize = maxsize
            self.queue = set()
    
        def _put(self, item):
            self.queue.add(item)
    
        def _get(self):
            return self.queue.pop()
    
    正如Paul McGuire所指出的,这将允许在从“待处理”集中删除但尚未添加到“已处理”集中的项目之后添加重复的项目。要解决这个问题,您可以将这两个集合存储在
    队列
    实例中,但由于您使用较大的集合来检查项目是否已被处理,因此您也可以返回到
    队列
    ,它将正确排序请求

    class SetQueue(Queue):
    
        def _init(self, maxsize):
            Queue._init(self, maxsize) 
            self.all_items = set()
    
        def _put(self, item):
            if item not in self.all_items:
                Queue._put(self, item) 
                self.all_items.add(item)
    

    与单独使用集合相比,这种方法的优点是,
    队列的方法是线程安全的,因此不需要额外的锁定来检查另一个集合。

    而不是“已访问的页面数组”创建一个“已添加到队列的页面数组”

    put方法也需要被覆盖,如果没有,加入调用将永远阻止

    遗憾的是,我对卢卡斯·拉林斯克的最佳答案没有任何评论

    要为LukášLalinský的SetQueue的第二个变体添加对
    SetQueue.task_done()
    SetQueue.join()
    的支持,请将else Brachh添加到if:

    def _put(self, item):
        if item not in self.all_items:
            Queue._put(self, item);
            self.all_items.add(item);
        else:
            self.unfinished_tasks -= 1;
    

    已测试并与Python 3.4配合使用。

    这是
    SetQueue

    import Queue
    
    class SetQueue(Queue.Queue):
        def _init(self, maxsize):
            Queue.Queue._init(self, maxsize)
            self.all_items = set()
    
        def _put(self, item):
            if item not in self.all_items:
                Queue.Queue._put(self, item)
                self.all_items.add(item)
    
        def _get(self):
            item = Queue.Queue._get(self)
            self.all_items.remove(item)
            return item
    

    接下来是对卢卡斯·拉林斯克(LukášLalinský)后者的改进。 重要的区别在于,
    put
    被覆盖,以确保
    未完成的_任务
    是准确的,
    join
    按预期工作

    from queue import Queue
    
    class UniqueQueue(Queue):
    
        def _init(self, maxsize):
            self.all_items = set()
            Queue._init(self, maxsize)
    
        def put(self, item, block=True, timeout=None):
            if item not in self.all_items:
                self.all_items.add(item)
                Queue.put(self, item, block, timeout)
    

    我同意@Ben James。试着同时使用deque和set

    以下是代码:

    class SetUniqueQueue(Queue):
    
        def _init(self, maxsize):
            self.queue = deque()
            self.setqueue = set()
    
        def _put(self, item):
            if item not in self.setqueue:
                self.setqueue.add(item)
                self.queue.append(item)
    
        def _get(self):
            return self.queue.popleft()
    

    这是不正确的,
    set
    类型是一个哈希表,就像
    dict
    类型一样。确切地说,这是错误的信息。集合与dicts一样快(可能更快,因为不需要检索/存储值)。保留所有先前排队的条目的列表很重要(我使用集合,而不是列表,不确定@sam的set问题是什么)。如果您只是在队列中搜索重复项,则可以重新处理以前排队并已处理过的条目,从而将其从队列中删除。是的,我的回答假定了队列之外的第二个数据结构(因此类似于“在队列和数组中/dict”和“在数组中/dict,不在队列中”)。将项目添加到“SEED”数据结构中,然后再对其进行排队。您不搜索队列,而是搜索“seed”数组。根据定义,“seen”数组中的任何内容要么在队列中,要么已经访问过;这两种情况都不需要再次排队。主要技巧是确保检查“已看到”和队列(如果未找到)是原子的。这会导致在弹出条目后重新处理条目的风险。当然,您也可以将所有项的集合存储在“队列”中,并修改
    \u put
    以首先检查该集合。它受到队列锁定的保护,所以没有竞争条件。这是如此优雅。很不错的,即使有第一个版本的缺点。即使您确实关心订单,也可以使用从
    集合
    文档链接的配方来代替
    集合
    。关于覆盖
    放置
    方法的注意事项。如果您选择使用磁盘上的数据库如果遇到未处理的异常,您可以修复错误并继续您离开的位置。这就像说使用If条件将非常适合
    。。。。。问题的上下文。。使用SQLite会降低整个过程的速度,如果已经将其出列并进行处理,这将无济于事。“数组”?用Python?你是说“列表”、“元组”还是“字典”?如果您指的是“数组”,那么您使用的是哪种数组实现?numpy?我认为,最好像这样调用父函数
    super(SetQueue,self)。\u init()
    另外,\u init函数没有
    maxsize
    参数-
    class SetUniqueQueue(Queue):
    
        def _init(self, maxsize):
            self.queue = deque()
            self.setqueue = set()
    
        def _put(self, item):
            if item not in self.setqueue:
                self.setqueue.add(item)
                self.queue.append(item)
    
        def _get(self):
            return self.queue.popleft()