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


84

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

감사


2
좋은 질문에 감사드립니다. 향후 모든 Google 직원에 대한 답변을 선택하세요. 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

이 데코레이터가 제대로 작동하려면 스파이더에 항목을 처리하는 데 사용할 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에서도 지정 될 수 있도록 변경하는 것이 좋습니다).


파이프 라인간에 전환하는 방법을 구현하려고합니다.하지만 NameError가 발생합니다! 파이프 라인이 정의되지 않았습니다. 이 코드를 직접 테스트 했습니까? 도와 줄래?
mehdix_ apr

. @ mehdix_ 예, 저에게 효과적입니다. 어디에서 NameError가 발생합니까?
mstringer 2015-04-06

오류는 scrapy crawl <spider name>명령 직후에 발생 합니다. 파이썬은 파이프 라인을 실행하기 위해 스파이더 클래스 내에서 설정 한 이름을 인식하지 못합니다. 살펴볼 수 있도록 spider.pypipeline.py에 대한 링크를 제공합니다 . 감사합니다
mehdix_ 2015-04-07

1
설명해 주셔서 감사합니다. 첫 번째 코드 조각은 어디로 갑니까? spider.py오른쪽 끝에 어딘가 ?
mehdix_ apr

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'이 무엇인지 궁금해하는 분을 위해? 나처럼-FROM THE DOC- "이 설정에서 클래스에 할당하는 정수 값은 실행 순서를 결정합니다. 항목은 낮은 값에서 높은 값의 클래스로 이동합니다. 이러한 숫자를 0-1000 범위로 정의하는 것이 일반적입니다." - docs.scrapy.org/en/latest/topics/item-pipeline.html
brainLoop

2
왜 이것이 받아 들여지는 대답이 아닌지 확실하지 않으며, 받아 들인 대답보다 완벽하고 훨씬 깨끗하고 간단합니다. 이것이 바로 제가 찾던 것입니다. 아직 scrapy 1.8에서 작업
에릭 F

1
방금 스크래피 1.6에 체크인했습니다. settings.py에서 파이프 라인 설정을 제거 할 필요는 없습니다. spider의 custom_settings는 settings.py의 파이프 라인 설정을 재정의합니다.
Graham Monkman

내 시나리오에 완벽하게 작동합니다!
Mark Kamyszek

'app.MyPipeline'의 경우 파이프 라인 클래스의 전체 이름을 바꿉니다. 예 : project.pipelines.MyPipeline 여기서 project는 프로젝트 이름, pipelines는 pipelines.py 파일, MyPipeline은 Pipeline 클래스
Nava Bogatee

13

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

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


11

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

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

귀하의 답변에 감사드립니다. 방법 1을 사용했지만 하나의 프로젝트가 더 깨끗하고 코드를 재사용 할 수 있다고 생각합니다. 방법 3에 대해 자세히 설명해 주시겠습니까? 스파이더를 도구 명령으로 분리하려면 어떻게해야합니까?
CodeMonkeyB 2011

다른 답변에 게시 된 링크에 따르면 파이프 라인을 재정의 할 수 없으므로 3 번이 작동하지 않을 것 같습니다.
Daniel Bang

여기에서 나를 도와 주시겠습니까? 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
}

그런 식으로 각 스파이더는 각각의 파이프 라인을 사용합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.