Подтвердить что ты не робот

Как я могу использовать разные конвейеры для разных пауков в одном проекте Scrapy

У меня есть проект scrapy, который содержит несколько пауков. Можно ли каким-либо образом определить, какие конвейеры использовать для какого-то паука? Не все конвейеры, которые я определил, применимы для каждого паука.

Спасибо

4b9b3361

Ответ 1

Основываясь на решении от Pablo Hoffman, вы можете использовать следующий декоратор в методе process_item объекта Pipeline, чтобы он проверял атрибут pipeline вашего паука для того, нужно ли его выполнять. Например:

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

Чтобы этот декоратор работал правильно, паук должен иметь атрибут конвейера с контейнером объектов Pipeline, который вы хотите использовать для обработки элемента, например:

class MySpider(BaseSpider):

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

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

И затем в файле 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

Все объекты Pipeline все еще должны быть определены в ITEM_PIPELINES в настройках (в правильном порядке - было бы хорошо изменить, чтобы порядок мог быть указан и на Spider).

Ответ 2

Просто удалите все конвейеры из основных настроек и используйте этот внутренний паук.

Это определит конвейер для пользователя на паука

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

Ответ 3

Другие приведенные здесь решения хороши, но я думаю, что они могут быть медленными, потому что мы не действительно не, используя конвейер на паук, вместо этого мы проверяем, существует ли конвейер каждый раз, когда элемент (и в некоторых случаях это может достигать миллионов).

Хороший способ полностью отключить (или включить) функцию на паука с помощью custom_setting и from_crawler для всех расширений, подобных этому:

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
    }

Как вы проверяете, мы указали custom_settings, который будет переопределять вещи, указанные в settings.py, и мы отключим SOMEPIPELINE_ENABLED для этого паука.

Теперь, когда вы запустите этот паук, проверьте что-то вроде:

[scrapy] INFO: Enabled item pipelines: []

Теперь scrapy полностью отключила конвейер, не беспокоя его существование для всего прогона. Убедитесь, что это также работает для scrapy extensions и middlewares.

Ответ 4

Я могу думать о по крайней мере четырех подходах:

  • Используйте другой проект scrapy для набора пауков + конвейеров (может быть, подходит, если ваши пауки достаточно различны в разных проектах).
  • В командной строке scrapy tool измените настройку конвейера с помощью scrapy settings между каждым вызовом вашего паука
  • Изолируйте своих пауков в свои собственные команды scrapy tool и определите default_settings['ITEM_PIPELINES'] в своем классе команд в список конвейера, который вы хотите для этой команды. См. строка 6 этого примера.
  • В самих классах конвейера process_item() проверьте, с какими пауками он работает, и ничего не делайте, если его следует игнорировать для этого паука. См. Пример используя ресурсы для каждого паука, чтобы начать работу. (Это кажется уродливым решением, потому что оно плотно соединяет пауков и конвейеров предметов. Вероятно, вы не должны использовать этот.)

Ответ 5

Вы можете использовать атрибут name паука в вашем конвейере

class CustomPipeline(object)

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

Определение всех конвейеров таким образом может выполнить то, что вы хотите.

Ответ 6

Вы можете просто установить параметры конвейера элементов внутри паука следующим образом:

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

Затем я могу разделить конвейер (или даже использовать несколько конвейеров), добавив значение в загрузчик/возвращаемый элемент, которое определяет, какая часть паука отправила элементы. Таким образом, я не получу никаких исключений KeyError и знаю, какие элементы должны быть доступны.

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

Ответ 7

Я использую два конвейера, один для загрузки изображений (MyImagesPipeline) и второй для сохранения данных в mongodb (MongoPipeline).

предположим, что у нас много пауков (spider1, spider2,...........), в моем примере spider1 и spider5 не могут использовать MyImagesPipeline

settings.py

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

И полный полный код конвейера

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

Ответ 8

мы можем использовать некоторые условия в конвейере, как это

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

Ответ 9

Простое, но все же полезное решение.

Код паука

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

код конвейера

    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')

Надеюсь, это сэкономит время для кого-то!