Что такое пользовательские параметры Spider и зачем они нужны?
Пользовательские параметры Scrapy Spider — это способ динамически конфигурировать поведение вашего паука (spider) во время запуска. Они позволяют изменять настройки, такие как API-ключи, лимиты запросов, прокси и другие переменные, без необходимости изменения исходного кода паука. Это делает ваш код более гибким, переиспользуемым и упрощает управление конфигурацией в различных окружениях (разработка, тестирование, продакшн).
Зачем они нужны?
- Гибкость: Возможность адаптировать паука под разные задачи, просто меняя параметры запуска.
- Конфигурация окружения: Использование различных API-ключей или прокси для разных окружений.
- Тестирование: Легкое изменение параметров для тестирования различных сценариев.
- Избежание изменений кода: Нет необходимости переписывать код при изменении конфигурационных параметров.
Стандартные способы настройки Scrapy Spider (краткий обзор)
В Scrapy есть несколько стандартных способов настройки Spider:
settings.py: Основной файл конфигурации проекта, где можно задать общие настройки (USERAGENT, CONCURRENTREQUESTS и т.д.).- Аргументы командной строки: Передача настроек при запуске паука.
Spider.custom_settings: Переопределение настроек для конкретного паука.
Пользовательские параметры Spider расширяют возможности настройки, позволяя передавать произвольные значения, которые можно использовать внутри паука для управления его логикой.
Передача пользовательских параметров при запуске Spider
Использование -a аргумента для передачи параметров через командную строку
Самый простой и распространенный способ передачи пользовательских параметров — использование аргумента -a при запуске паука через командную строку.
Пример:
scrapy crawl my_spider -a api_key=your_api_key -a max_items=100
В этом примере мы передаем два параметра: api_key со значением your_api_key и max_items со значением 100.
Примеры передачи различных типов данных (строки, числа, списки)
Аргумент -a всегда передает значения как строки. Если вам нужно передать числа или списки, необходимо преобразовать их внутри паука.
- Строка:
scrapy crawl my_spider -a query="обувь nike" - Число:
scrapy crawl my_spider -a max_price=200 - Список (как строка JSON):
scrapy crawl my_spider -a categories='["мужская", "женская", "детская"]'
import scrapy
import json
class MySpider(scrapy.Spider):
name = "my_spider"
start_urls = ["http://example.com"]
def __init__(self, api_key: str = None, max_items: int = 10, categories: str = '[]', **kwargs):
super().__init__(**kwargs)
self.api_key = api_key
self.max_items = int(max_items) # Преобразование в число
self.categories = json.loads(categories) # Преобразование в список
def parse(self, response):
# Используем параметры
print(f"API Key: {self.api_key}")
print(f"Max Items: {self.max_items}")
print(f"Categories: {self.categories}")
yield {"title": response.css("title::text").get()}
Особенности работы с settings.py и приоритет параметров
Важно понимать, что параметры, переданные через командную строку, имеют более высокий приоритет, чем значения, заданные в settings.py. Это означает, что если вы определите параметр в settings.py и передадите его через -a, значение из командной строки будет использовано.
# settings.py
API_KEY = "default_api_key"
scrapy crawl my_spider -a api_key=your_api_key
# Будет использован api_key=your_api_key
Доступ к пользовательским параметрам внутри Spider
Через атрибут self.settings
Хотя параметры передаются через -a, внутри паука они доступны через атрибут self.settings. Однако, это не рекомендуется для пользовательских параметров, т.к. они могут смешаться с общими настройками Scrapy.
Через метод __init__ и передача параметров как аргументов
Наиболее чистый и рекомендуемый способ доступа к параметрам — передавать их как аргументы в метод __init__ паука. Это делает код более читаемым и явным.
import scrapy
class MySpider(scrapy.Spider):
name = "my_spider"
start_urls = ["http://example.com"]
def __init__(self, api_key: str = None, max_items: int = 10, **kwargs):
super().__init__(**kwargs)
self.api_key = api_key
self.max_items = int(max_items)
def parse(self, response):
# Используем параметры
print(f"API Key: {self.api_key}")
print(f"Max Items: {self.max_items}")
yield {"title": response.css("title::text").get()}
Примеры использования параметров для конфигурации логики Spider
Параметры можно использовать для управления различными аспектами логики паука:
- Лимит количества обрабатываемых страниц:
max_items. - Использование API ключа:
api_key. - Выбор категорий товаров для парсинга:
categories. - Настройка задержки между запросами:
delay.
import scrapy
import time
class MySpider(scrapy.Spider):
name = "my_spider"
start_urls = ["http://example.com"]
def __init__(self, api_key: str = None, delay: float = 0.5, **kwargs):
super().__init__(**kwargs)
self.api_key = api_key
self.delay = float(delay)
def parse(self, response):
# Используем параметры
print(f"API Key: {self.api_key}")
time.sleep(self.delay) # Задержка между запросами
yield {"title": response.css("title::text").get()}
Обработка параметров по умолчанию и валидация
Установка значений по умолчанию для параметров
При передаче параметров через __init__, можно задать значения по умолчанию, если параметр не был передан через командную строку. Это делает паука более устойчивым к ошибкам и упрощает его использование.
def __init__(self, api_key: str = "", max_items: int = 10, **kwargs):
super().__init__(**kwargs)
self.api_key = api_key
self.max_items = int(max_items)
Проверка типов данных и допустимых значений параметров
Важно проверять типы данных и допустимые значения параметров, чтобы избежать ошибок во время работы паука. Можно использовать try-except блоки или библиотеки валидации, такие как cerberus или voluptuous.
def __init__(self, api_key: str = None, max_items: int = 10, **kwargs):
super().__init__(**kwargs)
self.api_key = api_key
try:
self.max_items = int(max_items)
if self.max_items <= 0:
raise ValueError("max_items должен быть больше 0")
except ValueError as e:
raise ValueError(f"Некорректное значение max_items: {e}")
Использование argparse для более сложной обработки (опционально)
Для более сложной обработки параметров, можно использовать библиотеку argparse. Она позволяет определять аргументы, их типы, значения по умолчанию, описания и т.д. В контексте Scrapy, это требует немного больше усилий для интеграции.
Продвинутые техники работы с параметрами
Использование параметров для динамической настройки Pipeline
Параметры можно использовать для динамической настройки pipeline обработки данных. Например, можно включить/выключить определенные pipeline компоненты в зависимости от значения параметра.
# settings.py
ITEM_PIPELINES = {
'my_project.pipelines.FilterWordsPipeline': 300,
'my_project.pipelines.SaveToDatabasePipeline': 400,
}
class MySpider(scrapy.Spider):
name = "my_spider"
def __init__(self, enable_db_pipeline: bool = True, **kwargs):
super().__init__(**kwargs)
self.enable_db_pipeline = enable_db_pipeline
if not self.enable_db_pipeline:
self.settings['ITEM_PIPELINES'].pop('my_project.pipelines.SaveToDatabasePipeline', None)
Передача параметров через Scheduler Middleware (если применимо)
В редких случаях, когда требуется передавать параметры на уровень планировщика запросов (Scheduler Middleware), можно использовать request.meta. Однако, это более сложная и менее распространенная практика.
Практические примеры: настройка лимитов, API ключей и прокси
- Лимиты запросов: Передача параметра
max_requestsи ограничение количества запросов в методеparse. - API ключи: Передача параметра
api_keyи использование его в заголовках запросов или URL. - Прокси: Передача параметра
proxyи настройка прокси для каждого запроса черезrequest.meta['proxy'].
import scrapy
class MySpider(scrapy.Spider):
name = "my_spider"
start_urls = ["http://example.com"]
def __init__(self, api_key: str = None, proxy: str = None, **kwargs):
super().__init__(**kwargs)
self.api_key = api_key
self.proxy = proxy
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(url, callback=self.parse, meta={'proxy': self.proxy})
def parse(self, response):
# Используем параметры
print(f"API Key: {self.api_key}")
print(f"Proxy: {self.proxy}")
yield {"title": response.css("title::text").get()}