Escribo un rastreador simple en Python usando los módulos de subprocesamiento y cola. Busco una página, reviso los enlaces y los pongo en una cola, cuando un determinado hilo ha terminado de procesar la página, toma el siguiente de la cola. Estoy usando una matriz para las páginas que ya he visitado para filtrar los enlaces que agrego a la cola, pero si hay más de un subproceso y obtienen los mismos enlaces en páginas diferentes, ponen enlaces duplicados a la cola. Entonces, ¿cómo puedo averiguar si alguna url ya está en la cola para evitar ponerlo de nuevo allí?¿Cómo verificar si una tarea ya está en cola de Python?
Respuesta
Si no importa el orden en que se procesan los artículos, me gustaría probar una subclase de Queue
que utiliza internamente 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()
Como señaló Paul McGuire, esto permitiría agregar un elemento duplicado una vez que se ha eliminado del conjunto "por procesar" y aún no se ha agregado al conjunto "procesado". Para solucionar esto, puede almacenar ambos conjuntos en la instancia Queue
, pero como está utilizando el conjunto más grande para comprobar si el elemento se ha procesado, puede volver al queue
, que ordenará las solicitudes correctamente.
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)
La ventaja de esto, en lugar de utilizar un conjunto separado, es que los métodos de la Queue
's son seguros para subprocesos, por lo que no necesita de bloqueo adicional para el control de la otra serie.
Esto corre el riesgo de reprocesar una entrada después de que se ha reventado. – PaulMcG
Claro, podría almacenar también el conjunto de todos los elementos en la "cola" y modificar '_put' para verificar primero ese conjunto. Está protegido por el bloqueo de Queue, por lo que no hay condiciones de carrera. –
Esto es tan elegante. Muy agradable, incluso con el inconveniente de la primera versión. –
La forma en que resolví esto (en realidad lo hice en Scala, no en Python) fue utilizar un conjunto y una cola, solo agregando enlaces a la cola (y establecer) si no existían en el conjunto.
Tanto el conjunto como la cola se encapsularon en un único subproceso, lo que expone solo una interfaz tipo cola a los subprocesos del consumidor.
Editar: alguien más sugirió SQLite y eso también es algo que estoy considerando, si el conjunto de URL visitadas necesita crecer. (Actualmente, cada rastreo tiene solo unos cientos de páginas, por lo que cabe fácilmente en la memoria). Pero la base de datos es algo que también se puede encapsular dentro del propio conjunto, por lo que los hilos del consumidor no tienen que ser conscientes de ello.
SQLite es tan simple de usar y cabría perfectamente ... solo una sugerencia.
Con la ventaja adicional de darle persistencia si elige usar una base de datos en disco. Si se golpea una excepción no controlada puede corregir el error y continuar donde lo dejó –
Esto es tan bueno como diciendo 'Usando una condición si encajaría perfectly' ..... contexto N a la pregunta .. El uso de SQLite ralentizaría todo el proceso overalll – Mayhem
Además, en lugar de un conjunto puede intentar usar un diccionario. Las operaciones en conjuntos tienden a ser bastante lentas cuando son grandes, mientras que una búsqueda en el diccionario es agradable y rápida.
Mi 2c.
Esto es incorrecto, el tipo 'set' es una tabla hash al igual que el tipo' dict'. –
exactamente, eso es información errónea. conjuntos son exactamente tan rápido como predice (tal vez incluso más rápido, ya que no hay valores tienen que ser recuperado/almacenado) –
uso:
url in q.queue
que devuelve Verdadero si y sólo si es url
en la cola
que no ayuda si se trata de ser quitadas de la cola y ya procesada. –
¿Por qué sólo usar la matriz (idealmente, un diccionario sería aún mejor) para filtrar las cosas que ya has visitado? Agregue elementos a su matriz/diccionario tan pronto como los ponga en cola y solo agréguelos a la cola si aún no están en la matriz/dict. Entonces usted tiene 3 simples cosas separadas:
- enlaces aún no se ve (ni en la cola ni matriz/dict)
- Enlaces programados para ser visitado (tanto en la cola y la matriz/dict)
- enlaces ya visitados (en orden de batalla/dict, no en la cola)
Es importante mantener la lista de todas las entradas en cola previamente (que haría uso de un conjunto, no una lista, no está seguro de lo que @ problema de Sam es con juego). Si solo busca duplicados en la cola, puede volver a procesar una entrada que ya estaba en cola y * ya * procesada, por lo tanto, se eliminó de la cola. – PaulMcG
Sí, mi respuesta supone una segunda estructura de datos, además de la cola (por lo tanto, cosas como 'tanto en la cola y la matriz/dict' y 'en serie/dict, no en la cola'). Agregue elementos a la estructura de datos 'vista' antes de ponerlos en cola. No busca en la cola, busca en su matriz 'vista'. Por definición, cualquier elemento de la matriz 'visto' está en la cola o ya ha sido visitado; ninguno de esos casos necesita ser puesto nuevamente en cola. El truco principal es asegurarse de que el check-'seen'-y-queue-if-not-found sea atómico. – Amber
en lugar de "conjunto de páginas que ya visitó" crea un "conjunto de páginas ya se agrega a la cola"
El método put también necesita ser sobrescrito, si no una unión llamada se bloqueará siempre https://github.com/python/cpython/blob/master/Lib/queue.py#L147
class UniqueQueue(Queue):
def put(self, item, block=True, timeout=None):
if item not in self.queue: # fix join bug
Queue.put(self, item, block, timeout)
def _init(self, maxsize):
self.queue = set()
def _put(self, item):
self.queue.add(item)
def _get(self):
return self.queue.pop()
Lamentablemente, no tengo ninguna calificación de enouch para comentar la mejor respuesta de Lukáš Lalinský.
para añadir soporte para SetQueue.task_done()
y SetQueue.join()
para la segunda variante de SetQueue de Lukáš Lalinský añadir otra brahch al caso:
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;
probado y funciona con Python 3.4.
Esta es la versión completa de 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
creo, mejor llame a las funciones principales como esta 'super (SetQueue, self) ._ init()' también, la función _init no tiene el parámetro 'maxsize' - '__init__' recibe esto – lucidyan
Lo que sigue es una mejora sobre Lukáš de este último solution Lalinský. La diferencia importante es que se put
anulados a fin de garantizar unfinished_tasks
es precisa y join
funciona como se espera.
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)
estoy de acuerdo con @Ben James.Try utilizar tanto deque y ajuste.
aquí código:
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()
- 1. ¿Cómo puedo verificar si una cola está vacía?
- 2. ¿Cómo verificar si una variable ya está declarada (T-SQL)?
- 3. ¿Cómo saber si una tarea ya se ha puesto en cola en django-apio?
- 4. iOS - ¿Cómo verificar si una NSOperation está en un NSOperationQueue?
- 5. ¿Cómo verificar si una variable está vacía en python?
- 6. ¿Cancelar una tarea que ya se está ejecutando con Apio?
- 7. ¿Cómo coloco una tarea nuevamente en la cola si la tarea falla?
- 8. Cómo verificar si una NSWindow está visible
- 9. ¿Cómo puedo verificar si una lista está ordenada?
- 10. Python PySerial.¿Cómo saber si un puerto ya está abierto?
- 11. ¿Cómo puedo verificar si PHP ya está conectado a una base de datos?
- 12. Comprueba si una constante ya está definida
- 13. Prueba si todavía se está procesando una tarea de apio
- 14. Cómo verificar la longitud de la cola en Python
- 15. ¿Cómo puedo verificar si una letra de una cadena está en mayúscula con Python?
- 16. ¿Cómo verificar si un producto de Magento ya está agregado en el carro o no?
- 17. ¿Cómo verificar si sql_safe_updates está activado?
- 18. comisión si ya se está ejecutando
- 19. Cola azul: busque si el artículo está en la cola
- 20. Cómo verificar si un DataGridViewCheckBoxCell está marcado
- 21. Compruebe si el archivo ya está abierto
- 22. ¿Cómo verificar si ya existe un valor para evitar duplicados?
- 23. ¿Cómo verificar si una fecha está en un rango determinado?
- 24. Conexión ADO VB6 - ¿Cómo verificar si está en una transacción?
- 25. ¿Cómo verificar si una variable está cargada en JavaScript?
- 26. ¿Cómo verificar si una variable está definida en Octave?
- 27. ¿Cómo puedo verificar si una IP está viva en Java?
- 28. Cómo rastrear si se está ejecutando una tarea async/awaitable
- 29. ¿Cómo verificar si un archivo ya está abierto por otro proceso en C?
- 30. ¿Cómo comprobar si una conexión MySQL está cerrada en Python?
"matriz"? En Python? ¿Quiere decir "lista" o "tupla" o "diccionario"? Si te refieres a "matriz", ¿qué implementación de matriz estás usando? numpy? –
me refería a la lista ... – Fluffy
@roddik: Por favor, no hagan comentarios en su propia pregunta. Por favor actualice su pregunta con hechos adicionales. –