Tengo un proyecto fragmentario que contiene varias arañas. ¿Hay alguna forma de que pueda definir qué canalizaciones usar para qué araña? No todas las tuberías que he definido son aplicables para todas las arañas.
Gracias
Tengo un proyecto fragmentario que contiene varias arañas. ¿Hay alguna forma de que pueda definir qué canalizaciones usar para qué araña? No todas las tuberías que he definido son aplicables para todas las arañas.
Gracias
Respuestas:
Sobre la base de la solución de Pablo Hoffman , puede usar el siguiente decorador en el process_item
método de un objeto Pipeline para que verifique el pipeline
atributo de su araña para ver si debe ejecutarse o no. Por ejemplo:
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
Para que este decorador funcione correctamente, la araña debe tener un atributo de canalización con un contenedor de los objetos de canalización que desea utilizar para procesar el artículo, por ejemplo:
class MySpider(BaseSpider):
pipeline = set([
pipelines.Save,
pipelines.Validate,
])
def parse(self, response):
# insert scrapy goodness here
return item
Y luego en un pipelines.py
archivo:
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
Todos los objetos Pipeline aún deben estar definidos en ITEM_PIPELINES en la configuración (en el orden correcto; sería bueno cambiarlo para que el orden también se pueda especificar en Spider).
scrapy crawl <spider name>
comando. python no reconoce los nombres que establecí dentro de la clase araña para que se ejecuten las canalizaciones. Te daré enlaces a mi spider.py y pipeline.py para que eches un vistazo. Gracias
spider.py
derecha?
if not hasattr(spider, 'pipeline') or self.__class__ in spider.pipeline:
Simplemente elimine todas las tuberías de la configuración principal y use esta araña interna.
Esto definirá la canalización al usuario por araña
class testSpider(InitSpider):
name = 'test'
custom_settings = {
'ITEM_PIPELINES': {
'app.MyPipeline': 400
}
}
Las otras soluciones dadas aquí son buenas, pero creo que podrían ser lentas, porque en realidad no estamos usando la canalización por araña, sino que estamos verificando si existe una canalización cada vez que se devuelve un elemento (y en algunos casos esto podría alcanzar millones).
Una buena manera de deshabilitar (o habilitar) por completo una función por araña es usar custom_setting
y from_crawler
para todas las extensiones como esta:
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
}
Como verifica, hemos especificado custom_settings
que anulará las cosas especificadas en settings.py
, y estamos deshabilitandoSOMEPIPELINE_ENABLED
para esta araña.
Ahora, cuando ejecute esta araña, busque algo como:
[scrapy] INFO: Enabled item pipelines: []
Ahora scrapy ha inhabilitado por completo la tubería, sin preocuparse de su existencia durante toda la ejecución. Compruebe que esto también funcione para scrapy extensions
y middlewares
.
Puedo pensar en al menos cuatro enfoques:
scrapy settings
entre cada invocación de su arañadefault_settings['ITEM_PIPELINES']
clase de comando en la lista de tuberías que desea para ese comando. Vea la línea 6 de este ejemplo .process_item()
compruebe contra qué araña se está ejecutando y no haga nada si se debe ignorar para esa araña. Vea el ejemplo de uso de recursos por araña para comenzar. (Esto parece una solución desagradable porque une estrechamente arañas y tuberías de elementos. Probablemente no debería usar esta).Puede usar el name
atributo de la araña en su canalización
class CustomPipeline(object)
def process_item(self, item, spider)
if spider.name == 'spider1':
# do something
return item
return item
Definir todas las canalizaciones de esta manera puede lograr lo que desea.
Puede establecer la configuración de las canalizaciones de elementos dentro de la araña de esta manera:
class CustomSpider(Spider):
name = 'custom_spider'
custom_settings = {
'ITEM_PIPELINES': {
'__main__.PagePipeline': 400,
'__main__.ProductPipeline': 300,
},
'CONCURRENT_REQUESTS_PER_DOMAIN': 2
}
Luego puedo dividir una tubería (o incluso usar varias tuberías) agregando un valor al cargador / elemento devuelto que identifica a qué parte de la araña envió los elementos. De esta manera, no obtendré ninguna excepción de KeyError y sé qué elementos deberían estar 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
Solución simple pero útil.
Código de araña
def parse(self, response):
item = {}
... do parse stuff
item['info'] = {'spider': 'Spider2'}
código de canalización
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')
¡Espero que esto le ahorre algo de tiempo a alguien!
Estoy usando dos canalizaciones, una para descargar imágenes (MyImagesPipeline) y la segunda para guardar datos en mongodb (MongoPipeline).
supongamos que tenemos muchas arañas (spider1, spider2, ...........), en mi ejemplo spider1 y spider5 no pueden usar MyImagesPipeline
settings.py
ITEM_PIPELINES = {'scrapycrawler.pipelines.MyImagesPipeline' : 1,'scrapycrawler.pipelines.MongoPipeline' : 2}
IMAGES_STORE = '/var/www/scrapycrawler/dowload'
Y debajo del código completo de la tubería
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
La solución más simple y efectiva es establecer configuraciones personalizadas en cada araña.
custom_settings = {'ITEM_PIPELINES': {'project_name.pipelines.SecondPipeline': 300}}
Después de eso, debe configurarlos en el archivo settings.py
ITEM_PIPELINES = {
'project_name.pipelines.FistPipeline': 300,
'project_name.pipelines.SecondPipeline': 300
}
de esa manera cada araña utilizará la tubería respectiva.