मैं किसी एकल स्क्रेपी परियोजना में विभिन्न मकड़ियों के लिए विभिन्न पाइपलाइनों का उपयोग कैसे कर सकता हूं


84

मेरे पास एक स्क्रैपी प्रोजेक्ट है जिसमें कई स्पाइडर हैं। क्या कोई तरीका है जो मैं परिभाषित कर सकता हूं कि किस मकड़ी के लिए कौन सी पाइपलाइनों का उपयोग करना है? मेरे द्वारा परिभाषित सभी पाइपलाइन हर मकड़ी के लिए लागू नहीं हैं।

धन्यवाद


2
आपके बहुत अच्छे प्रश्न के लिए धन्यवाद। कृपया भविष्य के सभी googler के लिए एक उत्तर चुनें। Mstringer द्वारा प्रदान किए गए उत्तर ने मेरे लिए बहुत अच्छा काम किया।
सहजीवन

जवाबों:


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 में परिभाषित किया जाना चाहिए (सही क्रम में - बदलने के लिए अच्छा होगा ताकि ऑर्डर को स्पाइडर पर भी निर्दिष्ट किया जा सके)।


मैं पाइपलाइनों के बीच स्विच करने के आपके तरीके को लागू करने की कोशिश कर रहा हूं, हालांकि मुझे NameError मिल रहा है! मुझे लगता है कि पाइपलाइनों को परिभाषित नहीं किया गया है। क्या आपने स्वयं इस कोड का परीक्षण किया है? क्या आप मेरी मदद करेंगे?
mehdix_

। @ mehdix_ हाँ, यह मेरे लिए काम करता है। आपको NameError कहाँ से मिलती है?
mstringer

scrapy crawl <spider name>कमांड के ठीक बाद त्रुटि आती है । अजगर पाइपलाइनों को चलाने के लिए मकड़ी वर्ग के भीतर मेरे द्वारा निर्धारित नामों को नहीं पहचानता है। मैं तुम्हें मेरे से जोड़ता है दे देंगे spider.py और pipeline.py आप एक बार देख लेने के लिए। धन्यवाद
mehdix_

1
स्पष्टीकरण के लिए धन्यवाद। पहला कोड स्निपेट कहां जाता है? spider.pyसही के अंत में कहीं ?
mehdix_

1
मैंने इस शर्त को संपादित किया कि पहले से ही परिभाषित मकड़ियों पर विफल न हो, जिनके पास कोई पाइपलाइन सेट नहीं है, यह डिफ़ॉल्ट रूप से सभी पाइपलाइनों को तब तक निष्पादित करेगा जब तक कि अन्यथा नहीं। if not hasattr(spider, 'pipeline') or self.__class__ in spider.pipeline:
नूर वुल्फ

138

बस मुख्य सेटिंग्स से सभी पाइपलाइनों को हटा दें और इसका उपयोग मकड़ी के अंदर करें।

यह प्रति स्पाइडर उपयोगकर्ता को पाइपलाइन को परिभाषित करेगा

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

3
जो '400' है, उसके लिए क्या सोच रहा है? मेरी तरह - DOC से - "इस सेटिंग में आपके द्वारा वर्गों के लिए असाइन किए गए पूर्णांक मान उस क्रम को निर्धारित करते हैं जिसमें वे चलते हैं: आइटम कम मूल्यवान से उच्च मूल्यवान कक्षाओं में जाते हैं। यह 0-1000 रेंज में इन संख्याओं को परिभाषित करने के लिए प्रथागत है" - docs.scrapy.org/en/latest/topics/item-pipeline.html
brainLoop

2
निश्चित नहीं है कि यह स्वीकृत उत्तर क्यों नहीं है, स्वीकार किए गए उत्तर की तुलना में पूरी तरह से, बहुत क्लीनर और सरल काम करता है। यही वह है जिसकी तलाश में मैं हूं। अभी भी स्क्रैपी 1.8 में काम कर रहे हैं
एरिक एफ

1
बस स्क्रैपी 1.6 में जाँच की गई। यह आवश्यक नहीं है कि सेटिंग्स में पाइपलाइन सेटिंग्स को हटाया जाए। custom_settings स्पाइडर ओवरराइड पाइपलाइन सेटिंग्स सेटिंग्स में।
ग्राहम मॉन्कमैन

मेरे परिदृश्य के लिए पूरी तरह से काम करता है!
मार्क Kamyszek

'app.MyPipeline' के लिए पाइपलाइन वर्ग का पूरा नाम बदलें। उदाहरण के लिए, project.pipelines.MyPipeline जहां प्रोजेक्ट प्रोजेक्ट का नाम है, पाइपलाइनों पाइपलाइन पाइपलाइन फ़ाइल है और MyPipeline पाइपलाइन क्लास है
Nava Bogatee

13

यहां दिए गए अन्य समाधान अच्छे हैं, लेकिन मुझे लगता है कि वे धीमे हो सकते हैं, क्योंकि हम वास्तव में प्रति स्पाइडर पाइप लाइन का उपयोग नहीं कर रहे हैं, इसके बजाय हम जाँच कर रहे हैं कि क्या कोई पाइपलाइन हर बार एक आइटम के वापस आने पर मौजूद है (और कुछ मामलों में यह पहुंच सकता है) लाखों)।

स्पाइडर प्रति एक सुविधा को पूरी तरह से अक्षम (या सक्षम) करने का एक अच्छा तरीका है 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: []

अब स्क्रैप ने पूरी तरह से पाइपलाइन को निष्क्रिय कर दिया है, पूरे रन के लिए इसके अस्तित्व को परेशान नहीं करता है। जाँच करें कि यह भी स्क्रैप के लिए काम करता है extensionsऔर middlewares


11

मैं कम से कम चार दृष्टिकोणों के बारे में सोच सकता हूं:

  1. मकड़ियों + पाइपलाइनों के सेट के अनुसार एक अलग स्क्रैपी प्रोजेक्ट का उपयोग करें (उपयुक्त हो सकता है यदि आपके मकड़ियों अलग-अलग परियोजनाओं में पर्याप्त वारंट हैं)
  2. स्क्रैपी टूल कमांड लाइन पर, scrapy settingsअपने मकड़ी के प्रत्येक आह्वान के बीच पाइप लाइन सेटिंग को बदलें
  3. अपने मकड़ियों को अपने स्वयं के स्क्रैप किए गए टूल कमांड में अलग करें , और उस default_settings['ITEM_PIPELINES']कमांड के लिए अपने कमांड क्लास पर उस पाइपलाइन सूची में परिभाषित करें जिसे आप चाहते हैं। इस उदाहरण की लाइन 6 देखें ।
  4. स्वयं पाइपलाइन कक्षाओं में, process_item()जाँच लें कि यह मकड़ी किसके खिलाफ चल रही है, और कुछ भी नहीं है अगर इसे उस मकड़ी के लिए अनदेखा किया जाना चाहिए। आरंभ करने के लिए प्रति मकड़ी प्रति संसाधनों का उपयोग करके उदाहरण देखें । (यह एक बदसूरत समाधान की तरह लगता है क्योंकि यह कपल मकड़ियों और आइटम पाइपलाइनों को कस कर देता है। आप शायद इसे इस्तेमाल न करें।)

आपके प्रतिक्रिया के लिए धन्येवाद। मैं 1 विधि का उपयोग कर रहा था, लेकिन मुझे लगता है कि एक परियोजना क्लीनर है और मुझे कोड का पुन: उपयोग करने की अनुमति देता है। क्या आप कृपया विधि 3 पर अधिक जानकारी दे सकते हैं। मैं मकड़ियों को अपने उपकरण कमांड में कैसे अलग करूंगा?
कोडमोनकीबी

एक अन्य उत्तर पर पोस्ट किए गए लिंक के अनुसार, आप पाइपलाइनों को ओवरराइड नहीं कर सकते, इसलिए मुझे लगता है कि नंबर 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) के लिए और दूसरा मैंडोडब (MongoPipeline) में डेटा बचाने के लिए।

मान लें कि हमारे पास कई मकड़ियाँ हैं (spider1, spider2, ...........), मेरे उदाहरण में spider1 और spider5 MyImagesPipeline का उपयोग नहीं कर सकते हैं

settings.py

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

और पाइप लाइन का पूरा कोड bellow

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

उसके बाद आपको उन्हें सेटिंग्स में सेट करना होगा फ़ाइल

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

उस तरह से प्रत्येक मकड़ी संबंधित पाइपलाइन का उपयोग करेगी।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.