Привет! Сегодня я расскажу о том, как я разработал Telegram-бота для мониторинга цен на Авито. Бот умеет отслеживать изменения цен в объявлениях и уведомлять пользователей об изменениях. В статье я поделюсь всеми этапами разработки, от проектирования до финальной реализации.
Идея создания бота появилась, когда я хотел сделать агента под свои нужды, не буду говорить какие. И дело дошло до автоматизации процесса пользованием авито.
Что умеет бот?
-
Поиск объявлений по различным параметрам (название, категория, город, ценовой диапазон)
-
Отслеживание цен в реальном времени
-
Уведомления при изменении цены (настраиваемый порог изменения)
-
Управление списком отслеживаемых объявлений
-
Поддержка нескольких объявлений для каждого пользователя
Технический стек
-
Python 3.9+
-
python-telegram-bot 20.7
-
aiohttp 3.9.1
-
pydantic 2.5.2
-
python-dotenv 1.0.0
Структура проекта
avito-monitor/
├── bot.py # Основной файл бота
├── avito_api.py # Класс для работы с API Авито
├── config.py # Конфигурация
├── requirements.txt # Зависимости
└── README.md # Документация
Шаг 1: Настройка окружения
python -m venv venv
source venv/bin/activate # для Linux/macOS
pip install python-telegram-bot requests python-dotenv aiohttp beautifulsoup4 pydantic aiofiles
Шаг 2: Конфигурация
Создаем файл config.py для хранения настроек:
import os
from dotenv import load_dotenv
load_dotenv()
BOT_TOKEN = os.getenv('BOT_TOKEN')
AVITO_CLIENT_ID = os.getenv('AVITO_CLIENT_ID')
AVITO_CLIENT_SECRET = os.getenv('AVITO_CLIENT_SECRET')
AVITO_ACCESS_TOKEN = os.getenv('AVITO_ACCESS_TOKEN')
# Настройки мониторинга
CHECK_INTERVAL = 300 # 5 минут
MAX_ITEMS_PER_USER = 10
PRICE_CHANGE_THRESHOLD = 5 # процент изменения цены
Шаг 3: Реализация API клиента
class AvitoAPI:
def __init__(self):
self.base_url = AVITO_API_BASE_URL
self.access_token = AVITO_ACCESS_TOKEN
async def search_items(self, **params):
"""Поиск объявлений"""
return await self._make_request('GET', '/items', params=params)
async def get_item_details(self, item_id: str):
"""Получение деталей объявления"""
return await self._make_request('GET', f'/items/{item_id}')
Важные особенности реализации:
-
Асинхронные запросы через aiohttp
-
Система повторных попыток при ошибках
-
Автоматическое обновление токена
-
Обработка различных HTTP-статусов
Шаг 4: Разработка Telegram бота
class AvitoBot:
def __init__(self):
self.api = AvitoAPI()
async def start(self, update, context):
"""Обработчик команды /start"""
await update.message.reply_text(
"Привет! Я бот для мониторинга объявлений на Авито..."
)
async def check_prices(self, context):
"""Периодическая проверка цен"""
for user_id, items in user_items.items():
for item_id, last_price in items.copy().items():
try:
item_details = await self.api.get_item_details(item_id)
current_price = float(item_details.get('price', 0))
if self._price_changed_significantly(last_price, current_price):
await self._notify_user(user_id, item_id, last_price, current_price)
except Exception as e:
logger.error(f"Error checking price: {e}")
Шаг 5: Реализация поиска
Поиск реализован в двух форматах:
-
Простой: пользователь отправляет только поисковый запрос
-
Расширенный: запрос в формате "Запрос | Категория | Город | Цена от | Цена до"
async def search_items(self, update, query, category=None, location=None,
price_from=None, price_to=None):
try:
# Получаем ID категории и локации если указаны
category_id = await self._get_category_id(category)
location_id = await self._get_location_id(location)
# Выполняем поиск
results = await self.api.search_items(
category_id=category_id,
location_id=location_id,
search_query=query,
price_from=price_from,
price_to=price_to
)
await self._send_search_results(update, results)
except Exception as e:
logger.error(f"Search error: {e}")
Шаг 6: Система мониторинга цен
Мониторинг реализован через job_queue библиотеки python-telegram-bot:
def main():
application = Application.builder().token(BOT_TOKEN).build()
job_queue = application.job_queue
job_queue.run_repeating(avito_bot.check_prices,
interval=CHECK_INTERVAL, first=10)
Для простоты я использовал словари для хранения данных (In-memory хранилище):
user_items: Dict[int, Dict[str, float]] = {} # user_id -> {item_id: last_price}
В реальном проекте лучше использовать базу данных (например, PostgreSQL или Redis).
Обработка ошибок
Важный момент - правильная обработка ошибок API:
async def _make_request(self, method, endpoint, params=None, data=None,
retry_count=0):
if retry_count >= MAX_RETRIES:
raise Exception(f"Превышено количество попыток")
try:
async with session.request(method, url, params=params,
json=data) as response:
if response.status == 401:
await self.refresh_token()
return await self._make_request(method, endpoint,
params, data, retry_count + 1)
# ... остальная обработка
except aiohttp.ClientError:
await asyncio.sleep(RETRY_DELAY)
return await self._make_request(method, endpoint,
params, data, retry_count + 1)
Возможные улучшения
-
Добавить поддержку базы данных
-
Реализовать систему фильтров для уведомлений
-
Добавить статистику изменения цен
-
Реализовать поддержку нескольких площадок
-
Добавить систему подписок на поисковые запросы
Заключение
Разработка бота была интересным опытом. Основные сложности были связаны с:
-
Реализацией асинхронных запросов
-
Правильной организацией системы уведомлений
Исходный код проекта будет доступен на GitHub
P.S. Если у вас есть вопросы или предложения по улучшению бота - пишите в комментариях!
Автор: kbones
