Ho un progetto scrapy che contiene più ragni. C'è un modo per definire quale pipeline utilizzare per quale spider? Non tutte le condutture che ho definito sono applicabili per ogni spider.
Grazie
Ho un progetto scrapy che contiene più ragni. C'è un modo per definire quale pipeline utilizzare per quale spider? Non tutte le condutture che ho definito sono applicabili per ogni spider.
Grazie
Risposte:
Basandosi sulla soluzione di Pablo Hoffman , è possibile utilizzare il seguente decoratore sul process_item
metodo di un oggetto Pipeline in modo che controlli l' pipeline
attributo del tuo spider per verificare se deve essere eseguito o meno. Per esempio:
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
Affinché questo decoratore funzioni correttamente, lo spider deve avere un attributo pipeline con un contenitore degli oggetti Pipeline che si desidera utilizzare per elaborare l'articolo, ad esempio:
class MySpider(BaseSpider):
pipeline = set([
pipelines.Save,
pipelines.Validate,
])
def parse(self, response):
# insert scrapy goodness here
return item
E poi in un pipelines.py
file:
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
Tutti gli oggetti Pipeline dovrebbero ancora essere definiti in ITEM_PIPELINES nelle impostazioni (nell'ordine corretto - sarebbe bello cambiare in modo che l'ordine possa essere specificato anche sullo Spider).
scrapy crawl <spider name>
comando. python non riconosce i nomi che ho impostato all'interno della classe spider per l'esecuzione delle pipeline. Ti fornirò i link ai miei spider.py e pipeline.py per farti dare un'occhiata. Grazie
spider.py
destra?
if not hasattr(spider, 'pipeline') or self.__class__ in spider.pipeline:
Basta rimuovere tutte le pipeline dalle impostazioni principali e utilizzare questo spider interno.
Questo definirà la pipeline per l'utente per spider
class testSpider(InitSpider):
name = 'test'
custom_settings = {
'ITEM_PIPELINES': {
'app.MyPipeline': 400
}
}
Le altre soluzioni qui fornite sono buone, ma penso che potrebbero essere lente, perché in realtà non stiamo usando la pipeline per spider, invece stiamo controllando se esiste una pipeline ogni volta che un articolo viene restituito (e in alcuni casi questo potrebbe raggiungere milioni).
Un buon modo per disabilitare completamente (o abilitare) una funzione per spider sta usando custom_setting
e from_crawler
per tutte le estensioni come questa:
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
}
Mentre controlli, abbiamo specificato custom_settings
che sovrascriverà le cose specificate in settings.py
e stiamo disabilitando SOMEPIPELINE_ENABLED
per questo spider.
Ora quando esegui questo ragno, controlla qualcosa come:
[scrapy] INFO: Enabled item pipelines: []
Ora scrapy ha completamente disabilitato l'oleodotto, senza preoccuparsi della sua esistenza per l'intera corsa. Verifica che funzioni anche per scrapy extensions
e middlewares
.
Mi vengono in mente almeno quattro approcci:
scrapy settings
tra ogni invocazione del tuo spiderdefault_settings['ITEM_PIPELINES']
classe di comando sulla tua lista di pipeline che desideri per quel comando. Vedere la riga 6 di questo esempio .process_item()
controlla su quale spider sta funzionando e non fare nulla se dovrebbe essere ignorato per quel ragno. Guarda l' esempio utilizzando le risorse per spider per iniziare. (Questa sembra una brutta soluzione perché accoppia strettamente ragni e pipeline di oggetti. Probabilmente non dovresti usare questo.)Puoi semplicemente impostare le impostazioni delle pipeline degli elementi all'interno dello spider in questo modo:
class CustomSpider(Spider):
name = 'custom_spider'
custom_settings = {
'ITEM_PIPELINES': {
'__main__.PagePipeline': 400,
'__main__.ProductPipeline': 300,
},
'CONCURRENT_REQUESTS_PER_DOMAIN': 2
}
Posso quindi suddividere una pipeline (o persino utilizzare più pipeline) aggiungendo un valore al caricatore / articolo restituito che identifica quale parte dello spider ha inviato gli articoli. In questo modo non otterrò eccezioni KeyError e so quali elementi dovrebbero essere disponibili.
...
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
La soluzione più semplice ed efficace è impostare le impostazioni personalizzate in ogni spider stesso.
custom_settings = {'ITEM_PIPELINES': {'project_name.pipelines.SecondPipeline': 300}}
Dopodiché è necessario impostarli nel file settings.py
ITEM_PIPELINES = {
'project_name.pipelines.FistPipeline': 300,
'project_name.pipelines.SecondPipeline': 400
}
in questo modo ogni spider utilizzerà la rispettiva pipeline.
Soluzione semplice ma comunque utile.
Codice Spider
def parse(self, response):
item = {}
... do parse stuff
item['info'] = {'spider': 'Spider2'}
codice 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')
Spero che questo faccia risparmiare tempo a qualcuno!
Sto usando due pipeline, una per il download delle immagini (MyImagesPipeline) e la seconda per il salvataggio dei dati in mongodb (MongoPipeline).
supponiamo di avere molti ragni (spider1, spider2, ...........), nel mio esempio spider1 e spider5 non possono usare MyImagesPipeline
settings.py
ITEM_PIPELINES = {'scrapycrawler.pipelines.MyImagesPipeline' : 1,'scrapycrawler.pipelines.MongoPipeline' : 2}
IMAGES_STORE = '/var/www/scrapycrawler/dowload'
E qui sotto il codice completo della 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