it-swarm.dev

Come posso utilizzare diverse pipeline per diversi spider in un singolo progetto Scrapy

Ho un progetto scrapy che contiene più spider . C'è un modo per definire quali pipeline usare per quale spider? Non tutte le condutture che ho definito sono applicabili per ogni spider.

Grazie

64
CodeMonkeyB

Basandosi su la soluzione di Pablo Hoffman , è possibile utilizzare il seguente decoratore sul metodo process_item di un oggetto Pipeline in modo che controlli l'attributo pipeline del proprio spider per stabilire se debba essere eseguito o meno. Per esempio:

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

Affinché questo decoratore funzioni correttamente, lo spider deve avere un attributo della pipeline con un contenitore degli oggetti Pipeline che si desidera utilizzare per elaborare l'elemento, ad esempio:

class MySpider(BaseSpider):

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

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

E poi in un file 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

Tutti gli oggetti Pipeline dovrebbero essere ancora definiti in ITEM_PIPELINES nelle impostazioni (nell'ordine corretto - sarebbe bello cambiare in modo che l'ordine possa essere specificato anche sullo Spider).

29
mstringer

Basta rimuovere tutte le tubazioni dalle impostazioni principali e utilizzare questo spider interno.

Questo definirà la pipeline all'utente per spider

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

Le altre soluzioni fornite qui sono buone, ma penso che potrebbero essere lente, perché non stiamo realmente non usando la pipeline per spider, invece stiamo controllando se una pipeline esiste ogni volta che un elemento viene restituito (e in alcuni casi questo potrebbe raggiungere milioni).

Un buon metodo per disabilitare completamente (o abilitare) una funzionalità per spider sta usando custom_setting e from_crawler per tutte le estensioni come questa:

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
    }

Mentre controlli, abbiamo specificato custom_settings che sovrascriverà le cose specificate in settings.py, e disabilitiamo SOMEPIPELINE_ENABLED per questo spider.

Ora quando esegui questo spider, controlla qualcosa come:

[scrapy] INFO: Enabled item pipelines: []

Ora scrapy ha completamente disattivato la pipeline, senza preoccuparsi della sua esistenza per l'intera corsa. Verifica che questo funzioni anche per extensions e middlewares.

11
eLRuLL

Posso pensare ad almeno quattro approcci:

  1. Usa un progetto scrapy diverso per set di spider + pipeline (potrebbe essere appropriato se i tuoi spider sono abbastanza diversi da essere giustificato in diversi progetti)
  2. Nella riga di comando dello strumento scrapy, modifica l'impostazione della pipeline con scrapy settings tra ogni chiamata del tuo spider
  3. Isolare gli spider nei propri comandi scrapy tool e definire default_settings['ITEM_PIPELINES'] sulla classe di comando sull'elenco di pipeline che si desidera per quel comando. Vedi la riga 6 di questo esempio .
  4. Nelle stesse classi di pipeline, avere process_item() controlla su quale spider è in esecuzione, e non fare nulla se dovrebbe essere ignorato per quel ragno. Vedi l'esempio usando le risorse per spider per iniziare. (Sembra una soluzione brutta perché accoppia strettamente i ragni e le condutture degli articoli. Probabilmente non dovresti usare questo.)
10
Francis Avila

Puoi utilizzare l'attributo name dello spider nella tua pipeline

class CustomPipeline(object)

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

Definire tutte le condotte in questo modo può portare a termine ciò che desideri.

8
pad

Puoi semplicemente impostare le impostazioni delle pipeline dell'articolo all'interno dello spider in questo modo:

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

Posso quindi suddividere una pipeline (o anche utilizzare più pipeline) aggiungendo un valore al caricatore/elemento restituito che identifica la parte del ragno che ha inviato gli articoli. In questo modo non otterrò alcuna eccezione di KeyError e so quali elementi dovrebbero essere disponibili. 

    ...
    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

Sto usando due pipeline, una per il download di immagini (MyImagesPipeline) e la seconda per salvare i dati in mongodb (MongoPipeline).

supponiamo di avere molti spider (spider1, spider2, ...........), nel mio esempio spider1 e spider5 non possono usare MyImagesPipeline

settings.py

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

E sotto il codice completo della 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

possiamo usare alcune condizioni nella pipeline come questa

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