¿Cómo puedo usar diferentes tuberías para diferentes arañas en un solo proyecto Scrapy?

Tengo un proyecto desechable que contiene múltiples arañas. ¿Hay alguna manera en que pueda definir qué tuberías usar para cada araña? No todas las tuberías que he definido son aplicables para todas las arañas.

Gracias

Sobre la base de la solución de Pablo Hoffman , puede usar el siguiente decorador en el método process_item de un objeto Pipeline para que verifique el atributo de pipeline de su araña para ver si se debe ejecutar o no. Por ejemplo:

 def check_spider_pipeline(process_item_method): @functools.wraps(process_item_method) def wrapper(self, item, spider): # message template for debugging msg = '%%s %s pipeline step' % (self.__class__.__name__,) # if class is in the spider's pipeline, then use the # process_item method normally. if self.__class__ in spider.pipeline: spider.log(msg % 'executing', level=log.DEBUG) return process_item_method(self, item, spider) # otherwise, just return the untouched item (skip this step in # the pipeline) else: spider.log(msg % 'skipping', level=log.DEBUG) return item return wrapper 

Para que este decorador funcione correctamente, la araña debe tener un atributo de canalización con un contenedor de los objetos de Canalización que desea utilizar para procesar el elemento, por ejemplo:

 class MySpider(BaseSpider): pipeline = set([ pipelines.Save, pipelines.Validate, ]) def parse(self, response): # insert scrapy goodness here return item 

Y luego en un archivo pipelines.py :

 class Save(object): @check_spider_pipeline def process_item(self, item, spider): # do saving here return item class Validate(object): @check_spider_pipeline def process_item(self, item, spider): # do validating here return item 

Todos los objetos de Pipeline aún deben definirse en ITEM_PIPELINES en la configuración (en el orden correcto: sería bueno cambiarlos para que el orden también se especifique en la Araña).

Simplemente elimine todas las tuberías de la configuración principal y use esta araña interna.

Esto definirá la tubería al usuario por araña.

 class testSpider(InitSpider): name = 'test' custom_settings = { 'ITEM_PIPELINES': { 'app.MyPipeline': 400 } } 

Las otras soluciones que se ofrecen aquí son buenas, pero creo que podrían ser lentas, porque en realidad no estamos usando el gasoducto por araña, en lugar de eso, estamos verificando si existe un gasoducto cada vez que se devuelve un artículo (y en algunos casos esto podría llegar millones).

Una buena manera de deshabilitar (o habilitar) completamente una característica por spider es usar custom_setting y from_crawler para todas las extensiones como esta:

pipelines.py

 from scrapy.exceptions import NotConfigured class SomePipeline(object): def __init__(self): pass @classmethod def from_crawler(cls, crawler): if not crawler.settings.getbool('SOMEPIPELINE_ENABLED'): # if this isn't specified in settings, the pipeline will be completely disabled raise NotConfigured return cls() def process_item(self, item, spider): # change my item return item 

settings.py

 ITEM_PIPELINES = { 'myproject.pipelines.SomePipeline': 300, } SOMEPIPELINE_ENABLED = True # you could have the pipeline enabled by default 

spider1.py

 class Spider1(Spider): name = 'spider1' start_urls = ["http://example.com"] custom_settings = { 'SOMEPIPELINE_ENABLED': False } 

A medida que verifica, hemos especificado custom_settings que anularán las cosas especificadas en settings.py , y estamos deshabilitando SOMEPIPELINE_ENABLED para esta araña.

Ahora, cuando corras esta araña, busca algo como:

 [scrapy] INFO: Enabled item pipelines: [] 

Ahora, scrapy ha deshabilitado completamente la tubería, sin preocuparse por su existencia durante toda la carrera. Compruebe que esto también funciona para las extensions chatarra y middlewares .

Puedo pensar en al menos cuatro enfoques:

  1. Use un proyecto diferente para cada conjunto de arañas + tuberías (podría ser apropiado si sus arañas son lo suficientemente diferentes como para estar en diferentes proyectos)
  2. En la línea de comando de la herramienta scrapy, cambia la configuración de la tubería con la scrapy settings entre cada invocación de tu araña
  3. Aísle sus arañas en sus propios comandos de la herramienta de desecho , y defina las default_settings['ITEM_PIPELINES'] en su clase de comando a la lista de tuberías que desea para ese comando. Vea la línea 6 de este ejemplo .
  4. En las clases de canalización, tienen que process_item() verifique con qué araña se está ejecutando, y no haga nada si debería ignorarse para esa araña. Vea el ejemplo usando recursos por spider para comenzar. (Esto parece una solución fea porque acopla estrechamente a las arañas y las tuberías de elementos. Probablemente no deberías usar esta).

Puede usar el atributo de name de la araña en su canalización

 class CustomPipeline(object) def process_item(self, item, spider) if spider.name == 'spider1': # do something return item return item 

Definir todas las tuberías de esta manera puede lograr lo que quiere.

Solo puede establecer la configuración de las tuberías de elementos dentro de la araña de esta manera:

 class CustomSpider(Spider): name = 'custom_spider' custom_settings = { 'ITEM_PIPELINES': { '__main__.PagePipeline': 400, '__main__.ProductPipeline': 300, }, 'CONCURRENT_REQUESTS_PER_DOMAIN': 2 } 

Luego, puedo dividir una tubería (o incluso usar varias tuberías) agregando un valor al cargador / elemento devuelto que identifica a qué parte de la araña envió los elementos. De esta manera no obtendré ninguna excepción de KeyError y sé qué artículos deberían estar disponibles.

  ... def scrape_stuff(self, response): pageloader = PageLoader( PageItem(), response=response) pageloader.add_xpath('entire_page', '/html//text()') pageloader.add_value('item_type', 'page') yield pageloader.load_item() productloader = ProductLoader( ProductItem(), response=response) productloader.add_xpath('product_name', '//span[contains(text(), "Example")]') productloader.add_value('item_type', 'product') yield productloader.load_item() class PagePipeline: def process_item(self, item, spider): if item['item_type'] == 'product': # do product stuff if item['item_type'] == 'page': # do page stuff 

Estoy usando dos tuberías, una para la descarga de imágenes (MyImagesPipeline) y la segunda para guardar datos en mongodb (MongoPipeline).

Supongamos que tenemos muchas arañas (spider1, spider2, ………..), en mi ejemplo, spider1 y spider5 no pueden usar MyImagesPipeline

settings.py

 ITEM_PIPELINES = {'scrapycrawler.pipelines.MyImagesPipeline' : 1,'scrapycrawler.pipelines.MongoPipeline' : 2} IMAGES_STORE = '/var/www/scrapycrawler/dowload' 

Y debajo del código completo de la tubería.

 import scrapy import string import pymongo from scrapy.pipelines.images import ImagesPipeline class MyImagesPipeline(ImagesPipeline): def process_item(self, item, spider): if spider.name not in ['spider1', 'spider5']: return super(ImagesPipeline, self).process_item(item, spider) else: return item def file_path(self, request, response=None, info=None): image_name = string.split(request.url, '/')[-1] dir1 = image_name[0] dir2 = image_name[1] return dir1 + '/' + dir2 + '/' +image_name class MongoPipeline(object): collection_name = 'scrapy_items' collection_url='snapdeal_urls' def __init__(self, mongo_uri, mongo_db): self.mongo_uri = mongo_uri self.mongo_db = mongo_db @classmethod def from_crawler(cls, crawler): return cls( mongo_uri=crawler.settings.get('MONGO_URI'), mongo_db=crawler.settings.get('MONGO_DATABASE', 'scraping') ) def open_spider(self, spider): self.client = pymongo.MongoClient(self.mongo_uri) self.db = self.client[self.mongo_db] def close_spider(self, spider): self.client.close() def process_item(self, item, spider): #self.db[self.collection_name].insert(dict(item)) collection_name=item.get( 'collection_name', self.collection_name ) self.db[collection_name].insert(dict(item)) data = {} data['base_id'] = item['base_id'] self.db[self.collection_url].update({ 'base_id': item['base_id'] }, { '$set': { 'image_download': 1 } }, upsert=False, multi=True) return item 

Podemos usar algunas condiciones en la tubería como esta

  # -*- coding: utf-8 -*- from scrapy_app.items import x class SaveItemPipeline(object): def process_item(self, item, spider): if isinstance(item, x,): item.save() return item