Як я можу використовувати різні трубопроводи для різних павуків в одному проекті Скрапі


84

У мене є проект зі скребу, який містить декількох павуків. Чи я можу визначити, які трубопроводи використовувати для якого павука? Не всі визначені мною трубопроводи застосовні до кожного павука.

Дякую


2
Дякую за ваше дуже гарне запитання. Виберіть відповідь для всіх майбутніх googlers. Відповідь, надана mstringer, дуже добре для мене спрацювала.
symbiotech

Відповіді:


35

Спираючись на рішення від Пабло Гофмана , ви можете скористатися наведеним нижче декоратором щодо process_itemметоду об’єкта трубопроводу, щоб він перевіряв 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'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

Щоб цей декоратор працював коректно, павук повинен мати атрибут трубопроводу з контейнером об’єктів трубопроводу, який ви хочете використовувати для обробки елемента, наприклад:

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

Усі об'єкти трубопроводу все ще повинні бути визначені в ITEM_PIPELINES у налаштуваннях (у правильному порядку - було б непогано змінити, щоб порядок міг бути вказаний і на Spider).


Я намагаюся реалізувати ваш спосіб перемикання між конвеєрами, однак я отримую NameError! Я отримую трубопроводи не визначено. ви самі тестували цей код? чи не могли б ви мені допомогти?
mehdix_

. @ mehdix_ так, це працює для мене. Де ви отримуєте помилку NameError?
mstringer

Помилка виникає відразу після scrapy crawl <spider name>команди. python не розпізнає імена, які я встановив у класі spider для запуску конвеєрів. Я дам вам посилання на мої spider.py та pipeline.py, щоб ви їх подивились. Дякую
mehdix_

1
Дякую за роз'яснення. куди потрапляє перший фрагмент коду? десь у кінці spider.pyправоруч?
mehdix_

1
Я відредагував умову про невдачу на вже визначених павуках, у яких не встановлено конвеєр, це також змусить його виконувати всі конвеєри за замовчуванням, якщо не сказано інше. if not hasattr(spider, 'pipeline') or self.__class__ in spider.pipeline:
Nour Wolf

138

Просто видаліть усі конвеєри з основних налаштувань і використовуйте це всередині павука.

Це визначить конвеєр для користувача на павука

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

3
для того, хто цікавиться, що таке "400"? як я - ІЗ ДОКУ - "Цілі значення, які ви присвоюєте класам у цьому налаштуванні, визначають порядок їх запуску: елементи переходять від нижчих до вищих класів. Ці цифри прийнято визначати в діапазоні 0-1000" - docs.scrapy.org/en/latest/topics/item-pipeline.html
brainLoop

2
Не впевнений, чому це не прийнята відповідь, працює ідеально, набагато чистіше та простіше, ніж прийнята відповідь. Це саме те, що я шукав. Все ще працює в скрапі 1.8
Ерік Ф,

1
Щойно зареєстровано в скрапі 1.6. Не потрібно видаляти налаштування конвеєра в settings.py. custom_settings у павуку замінює налаштування конвеєра в settings.py.
Грехем Монкман,

Чудово працює під мій сценарій!
Марк Камишек

для 'app.MyPipeline' замініть повне ім'я класу конвеєра. Наприклад, project.pipelines.MyPipeline, де project - це назва проекту, pipelines - файл pipelines.py, а MyPipeline - клас Pipeline
Nava Bogatee

13

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

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

трубопроводи.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: []

Зараз скрапія повністю знеструмила трубопровід, не турбуючи його існування протягом усього циклу. Переконайтеся, що це також працює для скрапінгу extensionsта middlewares.


11

Я можу придумати щонайменше чотири підходи:

  1. Використовуйте інший проект скрапінгу для кожного набору павуків + трубопроводів (може бути доречним, якщо ваші павуки досить різні, що гарантує наявність у різних проектах)
  2. У командному рядку інструмента для скрапірування змініть налаштування конвеєра з проміжком scrapy settingsміж кожним викликом вашого павука
  3. Ізолюйте своїх павуків у їх власні команди інструмента для скрапінгу та визначте у default_settings['ITEM_PIPELINES']своєму класі команд до списку конвеєрів, який ви хочете для цієї команди. Див. Рядок 6 цього прикладу .
  4. У самих класах конвеєра process_item()перевірте, проти якого павука він працює, і нічого не робіть, якщо його слід ігнорувати для цього павука. Див. Приклад використання ресурсів на павука для початку. (Це здається потворним рішенням, оскільки воно щільно поєднує павуків та трубопроводи предметів. Вам, мабуть, не слід використовувати цей.)

Дякую за Вашу відповідь. Я використовував метод 1, але я відчуваю, що один проект є чистішим і дозволяє мені повторно використовувати код. Ви можете, будь ласка, детальніше розглянути метод 3. Як би я виділив павуків у власні команди інструменту?
CodeMonkeyB

Відповідно до посилання, розміщеного в іншій відповіді, ви не можете замінити трубопроводи, тому, мабуть, номер 3 не буде працювати.
Даніель Банг

чи не могли б ви мені допомогти? stackoverflow.com/questions/25353650 / ...
Marco Dinatsoli

11

Ви можете використовувати nameатрибут павука у своєму трубопроводі

class CustomPipeline(object)

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

Визначивши всі конвеєри таким чином, можна досягти того, що ви хочете.


4

Ви можете просто встановити налаштування конвеєрів елементів всередині павука так:

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

1
Це має бути прийнятою відповіддю. Більш гнучкий і менш громіздкий
Бен Вілсон,

1

Просте, але все ж корисне рішення.

Код павука

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

Сподіваюся, це заощадить трохи часу для когось!


0

Я використовую два конвеєри, один для завантаження зображень (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

0

ми можемо використовувати деякі умови в конвеєрі, як це

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

Найпростішим та ефективним рішенням є встановлення власних налаштувань для кожного павука.

custom_settings = {'ITEM_PIPELINES': {'project_name.pipelines.SecondPipeline': 300}}

Після цього вам потрібно встановити їх у файлі settings.py

ITEM_PIPELINES = {
   'project_name.pipelines.FistPipeline': 300,
   'project_name.pipelines.SecondPipeline': 300
}

таким чином кожен павук буде використовувати відповідний трубопровід.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.