J'ai un projet qui contient plusieurs araignées .. Est-ce que je peux définir quels pipelines utiliser pour quelle araignée? Tous les pipelines que j'ai définis ne sont pas applicables à chaque araignée.
Merci
En vous basant sur la solution de Pablo Hoffman , vous pouvez utiliser le décorateur suivant sur la méthode process_item
d'un objet Pipeline pour qu'il vérifie si l'attribut pipeline
de votre araignée doit ou non être exécuté. Par exemple:
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
Pour que ce décorateur fonctionne correctement, l'araignée doit avoir un attribut pipeline avec un conteneur des objets Pipeline que vous souhaitez utiliser pour traiter l'élément, par exemple:
class MySpider(BaseSpider):
pipeline = set([
pipelines.Save,
pipelines.Validate,
])
def parse(self, response):
# insert scrapy goodness here
return item
Et puis dans un fichier 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
Tous les objets Pipeline doivent toujours être définis dans ITEM_PIPELINES dans les paramètres (dans le bon ordre - il serait agréable de le modifier pour que l'ordre puisse également être spécifié sur l'araignée).
Supprimez simplement tous les pipelines des paramètres principaux et utilisez cette araignée à l'intérieur.
Cela définira le pipeline utilisateur par spider
class testSpider(InitSpider):
name = 'test'
custom_settings = {
'ITEM_PIPELINES': {
'app.MyPipeline': 400
}
}
Je peux penser à au moins quatre approches:
scrapy settings
entre chaque invocation de votre araignée.default_settings['ITEM_PIPELINES']
de votre classe de commandes sur la liste de pipeline que vous souhaitez pour cette commande. Voir ligne 6 de cet exemple .process_item()
de vérifier l'araignée sur laquelle elle s'exécute et ne faites rien s'il doit être ignoré pour cette araignée. Voir l'exemple en utilisant ressources par spider pour vous aider à démarrer. (Cela semble être une solution laide, car cela couple étroitement les araignées et les pipelines d’articles. Vous ne devriez probablement pas utiliser celui-ci.)Vous pouvez utiliser l'attribut name
de l'araignée dans votre pipeline.
class CustomPipeline(object)
def process_item(self, item, spider)
if spider.name == 'spider1':
# do something
return item
return item
Définir tous les pipelines de cette façon peut accomplir ce que vous voulez.
Vous pouvez simplement définir les paramètres de pipelines d'éléments à l'intérieur de l'araignée comme suit:
class CustomSpider(Spider):
name = 'custom_spider'
custom_settings = {
'ITEM_PIPELINES': {
'__main__.PagePipeline': 400,
'__main__.ProductPipeline': 300,
},
'CONCURRENT_REQUESTS_PER_DOMAIN': 2
}
Je peux ensuite scinder un pipeline (ou même utiliser plusieurs pipelines) en ajoutant une valeur au chargeur/à l'élément renvoyé qui identifie la partie de l'araignée qui a envoyé les éléments. De cette façon, je n’aurai aucune exception KeyError et je saurai quels éléments devraient être disponibles.
...
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
J'utilise deux pipelines, l'un pour le téléchargement d'images (MyImagesPipeline) et l'autre pour l'enregistrement de données dans mongodb (MongoPipeline).
supposons que nous ayons beaucoup d'araignées (spider1, spider2, ...........), dans mon exemple, spider1 et spider5 ne peuvent pas utiliser MyImagesPipeline
settings.py
ITEM_PIPELINES = {'scrapycrawler.pipelines.MyImagesPipeline' : 1,'scrapycrawler.pipelines.MongoPipeline' : 2}
IMAGES_STORE = '/var/www/scrapycrawler/dowload'
Et ci-dessous le code complet du pipeline
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
Solution simple mais toujours utile.
Code araignée
def parse(self, response):
item = {}
... do parse stuff
item['info'] = {'spider': 'Spider2'}
code de pipeline
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')
J'espère que cela fera gagner du temps à quelqu'un!
nous pouvons utiliser certaines conditions dans le pipeline comme cela
# -*- 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