ti-enxame.com

Como posso usar pipelines diferentes para diferentes aranhas em um único projeto Scrapy

Eu tenho um projeto escasso que contém várias aranhas. Existe alguma maneira que eu possa definir quais pipelines usar para qual spider? Nem todos os pipelines que defini são aplicáveis ​​a todas as aranhas.

Obrigado

64
CodeMonkeyB

Baseando-se em a solução de Pablo Hoffman , você pode usar o seguinte decorador no método process_item de um objeto Pipeline para que ele verifique o atributo pipeline de sua aranha para saber se deve ou não ser executado. Por exemplo:

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 esse decorador funcione corretamente, o spider deve ter um atributo de pipeline com um contêiner dos objetos Pipeline que você deseja usar para processar o item, por exemplo:

class MySpider(BaseSpider):

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

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

E então em um arquivo 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 os objetos do Pipeline ainda devem ser definidos em ITEM_PIPELINES nas configurações (na ordem correta - seria bom alterar para que o pedido também pudesse ser especificado no Spider).

29
mstringer

Basta remover todos os pipelines das configurações principais e usar essa aranha interna.

Isso definirá o pipeline para o usuário por spider

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

As outras soluções dadas aqui são boas, mas eu acho que elas poderiam ser lentas, porque nós não estamos realmente não usando o pipeline por spider, em vez disso estamos checando se existe um pipeline toda vez que um item é retornado (e em alguns casos, isso pode atingir milhões).

Uma boa maneira de desativar completamente (ou ativar) um recurso por spider é usar custom_setting e from_crawler para todas as extensões 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
    }

Conforme você verifica, especificamos custom_settings que substituirá as coisas especificadas em settings.py, e estamos desabilitando SOMEPIPELINE_ENABLED para esta aranha.

Agora, quando você executar esta aranha, procure por algo como:

[scrapy] INFO: Enabled item pipelines: []

Agora, o scrapy desativou completamente o pipeline, sem incomodar sua existência durante toda a execução. Verifique se isso também funciona para extensions e middlewares com escassez.

11
eLRuLL

Eu posso pensar em pelo menos quatro abordagens:

  1. Use um projeto scrapy diferente por conjunto de spiders + pipelines (pode ser apropriado se suas aranhas forem diferentes o suficiente para garantir que estejam em projetos diferentes)
  2. Na linha de comando da ferramenta scrapy, altere a configuração do pipeline com scrapy settings entre cada invocação da sua aranha
  3. Isole suas aranhas em seus próprios comandos scrapy tool , e defina o default_settings['ITEM_PIPELINES'] em sua classe de comando para a lista de pipeline que você deseja para aquele comando. Veja linha 6 deste exemplo .
  4. Nas próprias classes de pipeline, verifique process_item() em qual aranha ele está sendo executado e não faça nada se ele for ignorado por essa aranha. Veja o exemplo usando recursos por spider para você começar. (Isso parece ser uma solução feia porque combina aranhas e pipelines de itens. Você provavelmente não deveria usar este.)
10
Francis Avila

Você pode usar o atributo name da aranha em seu pipeline

class CustomPipeline(object)

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

Definir todos os pipelines dessa maneira pode realizar o que você deseja.

8
pad

Você pode apenas definir as configurações de pipelines de itens dentro da aranha assim:

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

Eu posso então dividir um pipeline (ou até mesmo usar múltiplos pipelines) adicionando um valor ao carregador/item retornado que identifica qual parte da aranha enviou itens. Dessa forma, não receberei exceções de KeyError e sei quais itens devem estar disponíveis. 

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

Eu estou usando dois pipelines, um para download de imagem (MyImagesPipeline) e segundo para salvar dados no mongodb (MongoPipeline).

suponha que temos muitas aranhas (spider1, spider2, ...........), no meu exemplo spider1 e spider5 não podem usar MyImagesPipeline

settings.py

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

E abaixo do código completo do gasoduto

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

podemos usar algumas condições em pipeline como este

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