複数のスパイダーを含むスクレイププロジェクトがあります。どのパイプラインをどのスパイダーに使用するかを定義する方法はありますか?私が定義したすべてのパイプラインがすべてのスパイダーに適用できるわけではありません。
ありがとう
複数のスパイダーを含むスクレイププロジェクトがあります。どのパイプラインをどのスパイダーに使用するかを定義する方法はありますか?私が定義したすべてのパイプラインがすべてのスパイダーに適用できるわけではありません。
ありがとう
回答:
上の建物パブロ・ホフマンからソリューション、あなたは上で次のデコレータを使用することができ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で定義する必要があります(正しい順序で-スパイダーでも順序を指定できるように変更すると便利です)。
scrapy crawl <spider name>
コマンドの直後にエラーが発生します。pythonは、パイプラインを実行するためにスパイダークラス内で設定した名前を認識しません。私の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
、このようなすべての拡張のために:
pipes.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: []
現在、scrapyはパイプラインを完全に無効にし、実行全体でその存在を気にすることはありません。これがscrapyextensions
とmiddlewares
。でも機能することを確認してください。
私は少なくとも4つのアプローチを考えることができます:
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')
これが誰かのために時間を節約することを願っています!
2つのパイプラインを使用しています。1つは画像のダウンロード(MyImagesPipeline)用で、もう1つは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
}
このようにして、各スパイダーはそれぞれのパイプラインを使用します。