У меня есть проект scrapy, который содержит несколько пауков. Есть ли способ определить, какие конвейеры использовать для какого паука? Не все определенные мной конвейеры применимы для каждого паука.
благодаря
У меня есть проект scrapy, который содержит несколько пауков. Есть ли способ определить, какие конвейеры использовать для какого паука? Не все определенные мной конвейеры применимы для каждого паука.
благодаря
Ответы:
Основываясь на решении от Пабло Хоффмана , вы можете использовать следующий декоратор для process_item
метода объекта Pipeline, чтобы он проверял 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 в настройках (в правильном порядке - было бы неплохо изменить, чтобы порядок можно было указать и на Spider).
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
для всех подобных расширений:
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: []
Теперь scrapy полностью отключил конвейер, не беспокоясь о его существовании на протяжении всего прогона. Убедитесь, что это также работает для scrapy 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
Самое простое и эффективное решение - установить индивидуальные настройки в каждом пауке.
custom_settings = {'ITEM_PIPELINES': {'project_name.pipelines.SecondPipeline': 300}}
После этого вам необходимо установить их в файле settings.py
ITEM_PIPELINES = {
'project_name.pipelines.FistPipeline': 300,
'project_name.pipelines.SecondPipeline': 400
}
таким образом каждый паук будет использовать соответствующий трубопровод.
Простое, но все же полезное решение.
Код паука
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