여러 거미가 포함 된 스크래피 프로젝트가 있습니다. 어떤 스파이더에 사용할 파이프 라인을 정의 할 수있는 방법이 있습니까? 내가 정의한 모든 파이프 라인이 모든 스파이더에 적용되는 것은 아닙니다.
감사
답변:
바탕 파블로 호프만의 솔루션 , 당신은에 다음과 같은 장식을 사용할 수 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에서도 지정 될 수 있도록 변경하는 것이 좋습니다).
scrapy crawl <spider name>
명령 직후에 발생 합니다. 파이썬은 파이프 라인을 실행하기 위해 스파이더 클래스 내에서 설정 한 이름을 인식하지 못합니다. 살펴볼 수 있도록 spider.py 및 pipeline.py에 대한 링크를 제공합니다 . 감사합니다
spider.py
오른쪽 끝에 어딘가 ?
if not hasattr(spider, 'pipeline') or self.__class__ in spider.pipeline:
기본 설정에서 모든 파이프 라인을 제거하고 스파이더 내부에서 사용하십시오.
이것은 스파이더별로 사용자에 대한 파이프 라인을 정의합니다.
class testSpider(InitSpider):
name = 'test'
custom_settings = {
'ITEM_PIPELINES': {
'app.MyPipeline': 400
}
}
여기에 지정된 다른 솔루션이 좋은,하지만 난 우리가 정말하지 않기 때문에 그들은 속도가 느릴 수 있다고 생각 하지 않고 우리가 파이프 라인이 항목이 반환 될 때마다 존재하는 경우 확인되어, 거미 당 파이프 라인을 사용하여 (어떤 경우에는이 도달 할 수 수백만).
스파이더별로 기능을 완전히 비활성화 (또는 활성화)하는 좋은 방법은 다음 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
.
적어도 네 가지 접근 방식을 생각할 수 있습니다.
scrapy settings
스파이더를 호출 할 때마다 파이프 라인 설정을 변경합니다.default_settings['ITEM_PIPELINES']
명령 클래스에서 해당 명령에 대해 원하는 파이프 라인 목록에를 정의합니다 . 이 예의 6 행을 참조하십시오 .process_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')
누군가를 위해 시간을 절약하기를 바랍니다!
두 개의 파이프 라인을 사용하고 있는데 하나는 이미지 다운로드 용 (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
가장 간단하고 효과적인 솔루션은 각 스파이더 자체에서 사용자 지정 설정을 설정하는 것입니다.
custom_settings = {'ITEM_PIPELINES': {'project_name.pipelines.SecondPipeline': 300}}
그런 다음 settings.py 파일에서 설정해야합니다.
ITEM_PIPELINES = {
'project_name.pipelines.FistPipeline': 300,
'project_name.pipelines.SecondPipeline': 300
}
그런 식으로 각 스파이더는 각각의 파이프 라인을 사용합니다.