web-dev-qa-db-fra.com

Comment puis-je utiliser différents pipelines pour différentes araignées dans un seul projet Scrapy

J'ai un projet qui contient plusieurs araignées .. Est-ce que je peux définir quels pipelines utiliser pour quelle araignée? Tous les pipelines que j'ai définis ne sont pas applicables à chaque araignée.

Merci

64
CodeMonkeyB

En vous basant sur la solution de Pablo Hoffman , vous pouvez utiliser le décorateur suivant sur la méthode process_item d'un objet Pipeline pour qu'il vérifie si l'attribut pipeline de votre araignée doit ou non être exécuté. Par exemple:

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

Pour que ce décorateur fonctionne correctement, l'araignée doit avoir un attribut pipeline avec un conteneur des objets Pipeline que vous souhaitez utiliser pour traiter l'élément, par exemple:

class MySpider(BaseSpider):

    pipeline = set([
        pipelines.Save,
        pipelines.Validate,
    ])

    def parse(self, response):
        # insert scrapy goodness here
        return item

Et puis dans un fichier 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

Tous les objets Pipeline doivent toujours être définis dans ITEM_PIPELINES dans les paramètres (dans le bon ordre - il serait agréable de le modifier pour que l'ordre puisse également être spécifié sur l'araignée).

29
mstringer

Supprimez simplement tous les pipelines des paramètres principaux et utilisez cette araignée à l'intérieur.

Cela définira le pipeline utilisateur par spider

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

Je peux penser à au moins quatre approches:

  1. Utilisez un projet différent avec des araignées et des pipelines (cela pourrait être approprié si vos araignées sont suffisamment différentes pour pouvoir participer à différents projets)
  2. Sur la ligne de commande de Scrapy Tool, modifiez le paramètre de pipeline avec scrapy settings entre chaque invocation de votre araignée.
  3. Isolez vos araignées dans leurs propres commandes scrapy tool , et définissez le default_settings['ITEM_PIPELINES'] de votre classe de commandes sur la liste de pipeline que vous souhaitez pour cette commande. Voir ligne 6 de cet exemple .
  4. Dans les classes de pipeline elles-mêmes, demandez à process_item() de vérifier l'araignée sur laquelle elle s'exécute et ne faites rien s'il doit être ignoré pour cette araignée. Voir l'exemple en utilisant ressources par spider pour vous aider à démarrer. (Cela semble être une solution laide, car cela couple étroitement les araignées et les pipelines d’articles. Vous ne devriez probablement pas utiliser celui-ci.)
10
Francis Avila

Vous pouvez utiliser l'attribut name de l'araignée dans votre pipeline.

class CustomPipeline(object)

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

Définir tous les pipelines de cette façon peut accomplir ce que vous voulez.

8
pad

Vous pouvez simplement définir les paramètres de pipelines d'éléments à l'intérieur de l'araignée comme suit:

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

Je peux ensuite scinder un pipeline (ou même utiliser plusieurs pipelines) en ajoutant une valeur au chargeur/à l'élément renvoyé qui identifie la partie de l'araignée qui a envoyé les éléments. De cette façon, je n’aurai aucune exception KeyError et je saurai quels éléments devraient être 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
1
Ryan Stefan

J'utilise deux pipelines, l'un pour le téléchargement d'images (MyImagesPipeline) et l'autre pour l'enregistrement de données dans mongodb (MongoPipeline).

supposons que nous ayons beaucoup d'araignées (spider1, spider2, ...........), dans mon exemple, spider1 et spider5 ne peuvent pas utiliser MyImagesPipeline

settings.py

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

Et ci-dessous le code complet du pipeline

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
0
Nanhe Kumar

Solution simple mais toujours utile.

Code araignée

    def parse(self, response):
        item = {}
        ... do parse stuff
        item['info'] = {'spider': 'Spider2'}

code de pipeline

    def process_item(self, item, spider):
        if item['info']['spider'] == 'Spider1':
            logging.error('Spider1 pipeline works')
        Elif item['info']['spider'] == 'Spider2':
            logging.error('Spider2 pipeline works')
        Elif item['info']['spider'] == 'Spider3':
            logging.error('Spider3 pipeline works')

J'espère que cela fera gagner du temps à quelqu'un!

0
NashGC

nous pouvons utiliser certaines conditions dans le pipeline comme cela

    # -*- 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
0
Wade