У меня есть проект scrapy, который содержит несколько пауков. Есть ли способ определить, какие конвейеры использовать для какого паука? Не все определенные мной конвейеры применимы для каждого паука.
благодаря
python
scrapy
web-crawler
CodeMonkeyB
источник
источник
Ответы:
Основываясь на решении от Пабло Хоффмана , вы можете использовать следующий декоратор для
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 полностью отключил конвейер, не беспокоясь о его существовании на протяжении всего прогона. Убедитесь, что это также работает для scrapy
extensions
иmiddlewares
.источник
Вы можете использовать
name
атрибут паука в своем конвейереclass CustomPipeline(object) def process_item(self, item, spider) if spider.name == 'spider1': # do something return item return item
Такое определение всех конвейеров может достичь желаемого.
источник
Я могу придумать как минимум четыре подхода:
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
источник
мы можем использовать некоторые условия в конвейере, так как это
# -*- 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
источник