[python] 단일 Scrapy 프로젝트에서 서로 다른 스파이더에 대해 서로 다른 파이프 라인을 사용하려면 어떻게해야합니까?

여러 거미가 포함 된 스크래피 프로젝트가 있습니다. 어떤 스파이더에 사용할 파이프 라인을 정의 할 수있는 방법이 있습니까? 내가 정의한 모든 파이프 라인이 모든 스파이더에 적용되는 것은 아닙니다.

감사



답변

바탕 파블로 호프만의 솔루션 , 당신은에 다음과 같은 장식을 사용할 수 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

이 데코레이터가 제대로 작동하려면 스파이더에 항목을 처리하는 데 사용할 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

모든 파이프 라인 객체는 여전히 설정의 ITEM_PIPELINES에 정의되어야합니다 (올바른 순서로-순서가 Spider에서도 지정 될 수 있도록 변경하는 것이 좋습니다).


답변

기본 설정에서 모든 파이프 라인을 제거하고 스파이더 내부에서 사용하십시오.

이것은 스파이더별로 사용자에 대한 파이프 라인을 정의합니다.

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


답변

여기에 지정된 다른 솔루션이 좋은,하지만 난 우리가 정말하지 않기 때문에 그들은 속도가 느릴 수 있다고 생각 하지 않고 우리가 파이프 라인이 항목이 반환 될 때마다 존재하는 경우 확인되어, 거미 당 파이프 라인을 사용하여 (어떤 경우에는이 도달 할 수 수백만).

스파이더별로 기능을 완전히 비활성화 (또는 활성화)하는 좋은 방법은 다음 custom_settingfrom_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: []

이제 스크래피는 파이프 라인을 완전히 비활성화하고 전체 실행 동안 그 존재를 방해하지 않습니다. 스크래피 extensionsmiddlewares.


답변

적어도 네 가지 접근 방식을 생각할 수 있습니다.

  1. 스파이더 + 파이프 라인 세트마다 다른 스크래피 프로젝트를 사용하십시오 (스파이더가 다른 프로젝트에 있다는 것을 충분히 보증하는 경우 적절할 수 있음)
  2. 스크래피 도구 명령 줄에서 scrapy settings스파이더를 호출 할 때마다 파이프 라인 설정을 변경합니다.
  3. 스파이더를 자체 스크래피 도구 명령 으로 분리 하고 default_settings['ITEM_PIPELINES']명령 클래스에서 해당 명령에 대해 원하는 파이프 라인 목록에를 정의합니다 . 이 예의 6 행을 참조하십시오 .
  4. 파이프 라인 클래스 자체 process_item()에서 실행중인 스파이더를 확인하고 해당 스파이더에 대해 무시해야하는 경우 아무 작업도 수행하지 않습니다. 시작하려면 스파이더 당 리소스를 사용 하는 예를 참조하세요 . (이것은 스파이더와 아이템 파이프 라인을 밀접하게 결합하기 때문에 추악한 솔루션처럼 보입니다. 아마 이것을 사용해서는 안됩니다.)


답변

name파이프 라인에서 스파이더 의 속성을 사용할 수 있습니다.

class CustomPipeline(object)

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

이러한 방식으로 모든 파이프 라인을 정의하면 원하는 것을 달성 할 수 있습니다.


답변

다음과 같이 스파이더 내부에 항목 파이프 라인 설정을 지정할 수 있습니다.

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


답변

간단하지만 여전히 유용한 솔루션입니다.

스파이더 코드

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

누군가를 위해 시간을 절약하기를 바랍니다!