У мене є проект зі скребу, який містить декількох павуків. Чи я можу визначити, які трубопроводи використовувати для якого павука? Не всі визначені мною трубопроводи застосовні до кожного павука.
Дякую
У мене є проект зі скребу, який містить декількох павуків. Чи я можу визначити, які трубопроводи використовувати для якого павука? Не всі визначені мною трубопроводи застосовні до кожного павука.
Дякую
Відповіді:
Спираючись на рішення від Пабло Гофмана , ви можете скористатися наведеним нижче декоратором щодо process_item
методу об’єкта трубопроводу, щоб він перевіряв 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 для запуску конвеєрів. Я дам вам посилання на мої 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
для всіх таких розширень:
трубопроводи.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: []
Зараз скрапія повністю знеструмила трубопровід, не турбуючи його існування протягом усього циклу. Переконайтеся, що це також працює для скрапінгу 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
Просте, але все ж корисне рішення.
Код павука
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
Найпростішим та ефективним рішенням є встановлення власних налаштувань для кожного павука.
custom_settings = {'ITEM_PIPELINES': {'project_name.pipelines.SecondPipeline': 300}}
Після цього вам потрібно встановити їх у файлі settings.py
ITEM_PIPELINES = {
'project_name.pipelines.FistPipeline': 300,
'project_name.pipelines.SecondPipeline': 300
}
таким чином кожен павук буде використовувати відповідний трубопровід.