python - example - Scrapy: suelta silenciosamente un objeto
scrapy vs beautifulsoup (3)
Estoy usando Scrapy para rastrear varios sitios web, que pueden compartir información redundante.
Para cada página que raspe, almaceno la url de la página, su título y su código html, en mongoDB. Quiero evitar la duplicación en la base de datos, por lo tanto, implemento una canalización para verificar si un elemento similar ya está almacenado. En tal caso, DropItem
una excepción DropItem
.
Mi problema es que cada vez que dejo caer un elemento por razón de una excepción DropItem
, Scrapy mostrará todo el contenido del elemento en el registro (stdout o archivo). Como estoy extrayendo todo el código HTML de cada página raspada, en caso de una caída, todo el código HTML se mostrará en el registro.
¿Cómo podría dejar caer silenciosamente un objeto sin que se muestre su contenido?
¡Gracias por tu tiempo!
class DatabaseStorage(object):
""" Pipeline in charge of database storage.
The ''whole'' item (with HTML and text) will be stored in mongoDB.
"""
def __init__(self):
self.mongo = MongoConnector().collection
def process_item(self, item, spider):
""" Method in charge of item valdation and processing. """
if item[''html''] and item[''title''] and item[''url'']:
# insert item in mongo if not already present
if self.mongo.find_one({''title'': item[''title'']}):
raise DropItem(''Item already in db'')
else:
self.mongo.insert(dict(item))
log.msg("Item %s scraped" % item[''title''],
level=log.INFO, spider=spider)
else:
raise DropItem(''Missing information on item %s'' % (
''scraped from '' + item.get(''url'')
or item.get(''title'')))
return item
En las versiones recientes de Scrapy, esto ha cambiado un poco. Copié el código de @jimmytheleaf y lo arreglé para trabajar con Scrapy reciente:
import logging
from scrapy import logformatter
class PoliteLogFormatter(logformatter.LogFormatter):
def dropped(self, item, exception, response, spider):
return {
''level'': logging.INFO,
''msg'': logformatter.DROPPEDMSG,
''args'': {
''exception'': exception,
''item'': item,
}
}
Ok, encontré la respuesta incluso antes de publicar la pregunta. Sigo pensando que la respuesta puede ser valiosa para cualquiera que tenga el mismo problema.
En lugar de DropItem
el objeto con una excepción DropItem
, solo tiene que devolver un valor Ninguno:
def process_item(self, item, spider):
""" Method in charge of item valdation and processing. """
if item[''html''] and item[''title''] and item[''url'']:
# insert item in mongo if not already present
if self.mongo.find_one({''url'': item[''url'']}):
return
else:
self.mongo.insert(dict(item))
log.msg("Item %s scraped" % item[''title''],
level=log.INFO, spider=spider)
else:
raise DropItem(''Missing information on item %s'' % (
''scraped from '' + item.get(''url'')
or item.get(''title'')))
return item
La forma correcta de hacer esto parece ser implementar un LogFormatter personalizado para su proyecto y cambiar el nivel de registro de los elementos descartados.
Ejemplo:
from scrapy import log
from scrapy import logformatter
class PoliteLogFormatter(logformatter.LogFormatter):
def dropped(self, item, exception, response, spider):
return {
''level'': log.DEBUG,
''format'': logformatter.DROPPEDFMT,
''exception'': exception,
''item'': item,
}
Luego, en su archivo de configuración, algo como:
LOG_FORMATTER = ''apps.crawler.spiders.PoliteLogFormatter''
Tuve mala suerte solo al devolver "Ninguno", lo que causó excepciones en futuros gasoductos.