
Привет! Наверное, каждый из нас сталкивался с желанием купить что-то, но с мыслью: «Подожду, пока цена упадет». Отслеживать цены вручную — это утомительно и неэффективно. К счастью, Python идеально подходит для создания собственного мини-сервиса мониторинга цен Python.
Этот проект является отличным примером автоматизации: вы заставляете компьютер работать за вас, пока отдыхаете. Мы не будем просто парсить HTML, мы создадим полноценную систему, которая:
- Безопасно хранит данные (URL и прошлые цены).
- Надежно извлекает данные с помощью
BeautifulSoup. - Сравнивает новую цену со старой.
- Отправляет персонализированные уведомления (Telegram и Email) только при реальном снижении цены.
Давайте погрузимся в код и создадим скрипт, который сэкономит вам деньги!
Оглавление
🛠️ Шаг 1. Настройка среды и базы данных
Для реализации проекта нам понадобятся три ключевые библиотеки: requests для получения HTML-кода, BeautifulSoup для парсинга, и sqlite3 для хранения данных (встроенная в Python).
Установка зависимостей и переменные окружения
pip install requests beautifulsoup4 python-dotenv
Вместо традиционного pip, я рекомендую использовать современный, высокоскоростной менеджер uv (от создателей Ruff). Ознакомиться с новым пакетным менеджером и его преимуществами вы можете в моей статье: uv — молниеносный менеджер пакетов и виртуальных окружений
Мы используем python-dotenv для безопасного хранения конфиденциальных данных (токены Telegram, данные Email-аккаунта).
import sqlite3
import requests
from bs4 import BeautifulSoup
import re
import os
from dotenv import load_dotenv
# Демонстрационный URL и селектор для тестов
DEMO_URL = "http://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html"
DEMO_SELECTOR = "p.price_color"
DB_NAME = 'price_monitor.db'
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
# Загрузка переменных окружения
load_dotenv()
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
TELEGRAM_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID")
EMAIL_SENDER = os.getenv("EMAIL_SENDER")
Инициализация базы данных (SQLite)
Чтобы скрипт мог сравнивать новую цену с предыдущей, нам нужно место для хранения данных. SQLite — это простой локальный файл, идеально подходящий для нашего мини-сервиса.
def setup_database():
"""Создает базу данных и таблицу items, если они не существуют."""
conn = sqlite3.connect(DB_NAME)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
url TEXT NOT NULL,
last_price REAL,
css_selector TEXT NOT NULL
)
""")
conn.commit()
conn.close()
print("База данных готова.")
🕸️ Шаг 2. Надежное извлечение цены (Парсинг)
Успех скрипта мониторинга цен Python на 90% зависит от правильного CSS-селектора. Вам нужно вручную найти уникальный класс или ID элемента, который содержит цену.
Поиск CSS-селектора на сайте
Чтобы понять, какой селектор использовать, откройте страницу товара в браузере, наведите курсор на цену, кликните правой кнопкой мыши и выберите «Просмотреть код» или «Inspect». Найдите уникальный класс или ID, который содержит цену.
Например, для демо-сайта Books To Scrape цена находится в теге <p> с классом price_color. Наш селектор: p.price_color или просто .price_color.

Функция извлечения и очистки цены
Эта функция загружает HTML, ищет элемент по селектору и с помощью регулярных выражений очищает цену от символов валюты, пробелов и запятых, чтобы получить чистое число типа float.
def get_current_price(url, css_selector):
"""Извлекает и очищает цену с веб-страницы по селектору."""
try:
# 1. Загрузка страницы
response = requests.get(url, headers=HEADERS, timeout=10)
response.raise_for_status() # Проверка HTTP-статуса
# 2. Парсинг HTML
soup = BeautifulSoup(response.text, 'html.parser')
# 3. Поиск элемента по CSS-селектору
price_element = soup.select_one(css_selector)
if not price_element:
print(f"Ошибка парсинга: Элемент по селектору '{css_selector}' не найден.")
return None
raw_price_text = price_element.get_text()
# 4. Очистка текста: извлечение числа
clean_price_match = re.search(r'[\d,.]+', raw_price_text)
if clean_price_match:
# Преобразование в float
cleaned_text = clean_price_match.group(0).replace(',', '.')
return float(cleaned_text)
return None
except requests.exceptions.RequestException as e:
print(f"Ошибка запроса страницы {url}: {e}")
return None
except ValueError:
print(f"Ошибка конвертации цены: {raw_price_text}")
return None
📚 Документация: Beautiful Soup 4
🔔 Шаг 3. Сравнение и логика уведомлений
Теперь мы объединяем базу данных и парсер, чтобы выполнить основную логику: сравнение текущей цены с сохраненной и отправку уведомления.
Функции отправки уведомлений (Заглушки)
Для начала нам понадобятся две функции-заглушки. Они будут вызваны, когда обнаружится снижение цены. Обратите внимание, что мы добавляем pass и простой print(), чтобы основной скрипт мог работать без настройки Telegram или Email.
def send_telegram_notification(message):
"""Отправляет сообщение в Telegram (заглушка)."""
# Здесь будет ваша реализация из статьи по Telegram API
print(f"Уведомление (Telegram): {message}")
pass
def send_email_notification(subject, body):
"""Отправляет письмо на Email (заглушка)."""
# Здесь будет ваша реализация из статьи по Email
print(f"Уведомление (Email) с темой: {subject}")
pass
Основной цикл проверки
Функция check_and_notify будет выполняться при каждом запуске скрипта. Она выполняет три основные задачи: считывание данных, вызов парсера и обновление базы данных.
def check_and_notify():
"""Проверяет цены всех товаров, сравнивает с предыдущей и отправляет уведомления."""
conn = sqlite3.connect(DB_NAME)
cursor = conn.cursor()
# 1. Выбираем все товары
cursor.execute("SELECT id, name, url, last_price, css_selector FROM items")
items = cursor.fetchall()
changes_detected = False
for item_id, name, url, last_price, selector in items:
current_price = get_current_price(url, selector)
if current_price is None:
continue
if last_price is None:
print(f"[{name}] Первая запись цены: {current_price} сохранена.")
elif current_price < last_price:
# 2. Обнаружено снижение цены
reduction = round(last_price - current_price, 2)
print(f"[{name}] Снижение цены! {last_price} -> {current_price}")
changes_detected = True
message = (f"💸 СНИЖЕНИЕ ЦЕНЫ! Товар: {name} ({url}). "
f"Цена упала с {last_price} до {current_price} (на {reduction}).")
# 3. Вызов функций уведомления
send_telegram_notification(message)
send_email_notification(f"Снижение цены на {name}", message)
else:
print(f"[{name}] Цена стабильна или выросла: {current_price}")
# 4. Обновление сохраненной цены
cursor.execute("UPDATE items SET last_price = ? WHERE id = ?", (current_price, item_id))
conn.commit()
conn.close()
if changes_detected:
print("\nПроверка завершена. Обнаружены изменения цены.")
else:
print("\nПроверка завершена. Цены стабильны.")
Интеграция уведомлений
В основном коде мы используем простые функции-заглушки. Для того чтобы они заработали с реальными сервисами, вам потребуется настройка.
- Telegram-уведомления: Подробности о том, как получить токен бота и использовать API для отправки сообщений, вы найдете здесь: Как отправить сообщение в Telegram с помощью Python.
- Email-уведомления: Если вы предпочитаете получить письмо, воспользуйтесь этим руководством для настройки SMTP-клиента: Как отправить письмо с Python через Gmail .
⚙️ Шаг 4. Полный код и запуск сервиса
Для удобства, весь код скрипта мониторинга цен Python собран в единый файл. Убедитесь, что у вас установлен файл .env с переменными окружения.
Полный листинг monitor.py
import sqlite3
import requests
from bs4 import BeautifulSoup
import re
import os
from dotenv import load_dotenv
# --- КОНФИГУРАЦИЯ И ПЕРЕМЕННЫЕ ---
# Демонстрационный URL и селектор для тестов (Books To Scrape)
DEMO_URL = "http://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html"
DEMO_SELECTOR = "p.price_color"
DB_NAME = 'price_monitor.db'
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
# Загрузка переменных окружения
load_dotenv()
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
TELEGRAM_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID")
EMAIL_SENDER = os.getenv("EMAIL_SENDER")
# --- ФУНКЦИИ БАЗЫ ДАННЫХ ---
def setup_database():
"""Создает базу данных и таблицу items, если они не существуют."""
conn = sqlite3.connect(DB_NAME)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
url TEXT NOT NULL,
last_price REAL,
css_selector TEXT NOT NULL
)
""")
conn.commit()
conn.close()
# --- ФУНКЦИИ ПАРСИНГА ---
def get_current_price(url, css_selector):
"""Извлекает и очищает цену с веб-страницы по селектору."""
try:
response = requests.get(url, headers=HEADERS, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
price_element = soup.select_one(css_selector)
if not price_element:
print(f"Ошибка парсинга: Элемент по селектору '{css_selector}' не найден.")
return None
raw_price_text = price_element.get_text()
clean_price_match = re.search(r'[\d,.]+', raw_price_text)
if clean_price_match:
cleaned_text = clean_price_match.group(0).replace(',', '.')
return float(cleaned_text)
return None
except requests.exceptions.RequestException as e:
print(f"Ошибка запроса страницы {url}: {e}")
return None
except ValueError:
print(f"Ошибка конвертации цены: {raw_price_text}")
return None
# --- ФУНКЦИИ УВЕДОМЛЕНИЙ (Заглушки) ---
def send_telegram_notification(message):
"""Отправляет сообщение в Telegram (заглушка)."""
# Здесь будет ваша реализация из статьи "Как отправить сообщение в Telegram..."
print(f"Уведомление (Telegram): {message}")
pass
def send_email_notification(subject, body):
"""Отправляет письмо на Email (заглушка)."""
# Здесь будет ваша реализация из статьи "Как отправить письмо с Python через Gmail..."
print(f"Уведомление (Email) с темой: {subject}")
pass
# --- ОСНОВНАЯ ЛОГИКА ---
def check_and_notify():
"""Проверяет цены всех товаров, сравнивает с предыдущей и отправляет уведомления."""
conn = sqlite3.connect(DB_NAME)
cursor = conn.cursor()
cursor.execute("SELECT id, name, url, last_price, css_selector FROM items")
items = cursor.fetchall()
changes_detected = False
for item_id, name, url, last_price, selector in items:
current_price = get_current_price(url, selector)
if current_price is None:
continue
if last_price is None:
print(f"[{name}] Первая запись цены: {current_price} сохранена.")
elif current_price < last_price:
reduction = round(last_price - current_price, 2)
print(f"[{name}] Снижение цены! {last_price} -> {current_price}")
changes_detected = True
message = (f"💸 СНИЖЕНИЕ ЦЕНЫ! Товар: {name} ({url}). "
f"Цена упала с {last_price} до {current_price} (на {reduction}).")
send_telegram_notification(message)
send_email_notification(f"Снижение цены на {name}", message)
else:
print(f"[{name}] Цена стабильна или выросла: {current_price}")
# Обновление сохраненной цены
cursor.execute("UPDATE items SET last_price = ? WHERE id = ?", (current_price, item_id))
conn.commit()
conn.close()
if changes_detected:
print("\nПроверка завершена. Обнаружены изменения цены.")
else:
print("\nПроверка завершена. Цены стабильны.")
# --- ЗАПУСК СКРИПТА ---
if __name__ == '__main__':
# 1. Настройка базы данных
setup_database()
# 2. Добавление тестового товара (Если его еще нет в базе)
conn = sqlite3.connect(DB_NAME)
cursor = conn.cursor()
# Проверка, чтобы не дублировать тестовый товар
cursor.execute("SELECT COUNT(*) FROM items WHERE name=?", ('Тестовый товар (Книга)',))
if cursor.fetchone()[0] == 0:
cursor.execute("INSERT INTO items (name, url, css_selector) VALUES (?, ?, ?)",
('Тестовый товар (Книга)', DEMO_URL, DEMO_SELECTOR))
conn.commit()
print(f"Добавлен тестовый товар: Тестовый товар (Книга).")
conn.close()
# 3. Запуск основного цикла проверки
check_and_notify()
Инструкция по запуску
Сохраните этот код в файл monitor.py. После вам достаточно запустить его из консоли:
python monitor.py
Планирование автоматического запуска
Для того чтобы скрипт работал как полноценный сервис, его нужно запускать по расписанию:
- Linux/macOS: Используйте
cron(crontab -e) для ежедневного или ежечасного запуска скрипта. - Windows: Используйте Планировщик заданий Windows (Task Scheduler).
✅ Заключение
Мы создали полноценный мини-сервис мониторинга цен Python, который включает все необходимые компоненты: от хранения данных в SQLite до сложной логики сравнения и отправки уведомлений.
Этот скрипт — отличная основа, которую вы можете масштабировать: добавить больше сайтов, настроить асинхронный парсинг для ускорения проверки (используя aiohttp) или интегрировать его в веб-интерфейс.
Самое главное: вы автоматизировали рутинную и потенциально дорогостоящую задачу, заставив Python работать на экономию ваших средств!
• Как отправить сообщение в Telegram с помощью Python — подробный гайд, необходимый для настройки Telegram-уведомлений
• Как отправить письмо с Python через Gmail — инструкция по настройке Email-уведомлений для вашего скрипта
• Как ускорить Python в 10 раз с помощью асинхронности — будет полезно, если вы решите мониторить десятки или сотни товаров одновременно
📢 Подписывайтесь на Telegram-канал PythonAuto, чтобы не пропустить новые гайды по автоматизации, парсингу и Python.
👉 Ваш интерес — лучшая мотивация для новых статей!