16 KiB
16 KiB
Parser Zakupok (Парсер Закупок)
AdonisJS 6 приложение для автоматического парсинга аукционов с сайта icetrade.by. Система автоматически собирает данные каждые 6 часов, сохраняет в PostgreSQL и отправляет Telegram уведомления по ключевым словам.
Возможности
- 🔄 Автоматический парсинг аукционов каждые 6 часов
- 🔍 Поиск по ключевым словам (заголовок, описание, организация)
- 📱 Telegram уведомления пользователям
- 📊 Логирование всех операций парсинга
- 🗄️ PostgreSQL база данных с полной историей
- ⚡ Пагинация и rate limiting для стабильности
- 🔧 REST API для управления
Технологический стек
- Framework: AdonisJS 6 (TypeScript)
- Database: PostgreSQL + Lucid ORM
- Scheduler: adonisjs-scheduler
- HTML Parser: Cheerio
- Validation: Zod
- Telegram Bot: Grammy
- Testing: Japa
Установка
Требования
- Node.js >= 20.6
- PostgreSQL >= 14
- npm >= 9
Шаги установки
# 1. Клонировать репозиторий
git clone <repository-url>
cd parser-zakupok
# 2. Установить зависимости
npm install
# 3. Скопировать .env файл
cp .env.example .env
# 4. Настроить переменные окружения
nano .env # или любой редактор
# 5. Запустить миграции
node ace migration:run
# 6. Запустить приложение
npm run dev
Настройка .env
# Сервер
PORT=3333
HOST=localhost
NODE_ENV=development
APP_KEY=<generate-with-node-ace-generate:key>
# База данных PostgreSQL
DB_HOST=127.0.0.1
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=your_password
DB_DATABASE=parser_zakupok
# Telegram Bot
TELEGRAM_BOT_TOKEN=your_bot_token_from_@BotFather
# Опционально
LOG_LEVEL=info
Команды запуска
Режим разработки
# Запуск с hot reload (рекомендуется)
npm run dev
# Альтернативный вариант
node ace serve --hmr
# С watch режимом
node ace serve --watch
Production режим
# 1. Собрать проект
npm run build
# 2. Запустить миграции
node ace migration:run --force
# 3. Запустить сервер
npm start
# или
node build/bin/server.js
Команды базы данных
Миграции
# Выполнить все pending миграции
node ace migration:run
# Откатить последний batch
node ace migration:rollback
# Откатить все миграции
node ace migration:rollback --batch=0
# Пересоздать базу (drop + migrate)
node ace migration:fresh
# Fresh + seed данные
node ace migration:fresh --seed
Создание новых сущностей
# Создать миграцию
node ace make:migration create_table_name
node ace make:migration add_column_to_table
# Создать модель
node ace make:model ModelName
# Создать модель + миграцию
node ace make:model ModelName -m
# Создать контроллер
node ace make:controller ControllerName
Database утилиты
# Открыть REPL с доступом к моделям
node ace repl
# Примеры в REPL:
# > await loadModels()
# > const Auction = await import('#models/auction')
# > await Auction.default.all()
Команды парсинга
Основная команда parse:auctions
# Парсинг с дефолтными настройками (до 10 страниц)
node ace parse:auctions
# Парсинг конкретного количества страниц
node ace parse:auctions --pages=5
# Парсинг без отправки уведомлений (только сохранение)
node ace parse:auctions --skip-notifications
# Парсинг 1 страницы без уведомлений (для тестирования)
node ace parse:auctions --pages=1 --skip-notifications
# Комбинация опций
node ace parse:auctions --pages=20 --skip-notifications
Что делает команда:
- Создает запись в
parse_logs(статус: running) - Парсит страницы с icetrade.by с rate limiting (1 сек между запросами)
- Валидирует данные через Zod схемы
- Upsert аукционов по
auction_num(обновляет существующие, создает новые) - Ищет совпадения по ключевым словам
- Создает записи уведомлений для пользователей
- Обновляет parse_log (статус: completed/failed, статистика)
Выводит:
- Количество найденных аукционов
- Количество новых аукционов
- Количество обновленных аукционов
- Количество созданных уведомлений
- Время выполнения
- Ошибки (если есть)
Команды Scheduler
Управление планировщиком
# Запустить scheduler (production)
node ace scheduler:work
# Запустить с watch режимом (development)
node ace scheduler:work --watch
# Показать список всех запланированных задач
node ace scheduler:list
# Вывод scheduler:list:
# ┌────────────────┬──────────────────┬─────────────────┐
# │ Name │ Cron Expression │ Next Run │
# ├────────────────┼──────────────────┼─────────────────┤
# │ parse:auctions │ 0 0 */6 * * * │ 2024-01-15 18:00│
# └────────────────┴──────────────────┴─────────────────┘
Настройка расписания
Расписание настроено в start/scheduler.ts:
// Текущее: каждые 6 часов (00:00, 06:00, 12:00, 18:00)
scheduler.call(async () => {
await ace.exec('parse:auctions', [])
}).cron('0 0 */6 * * *')
// Другие варианты расписания:
// .cron('0 0 * * * *') // Каждый час
// .cron('0 */30 * * * *') // Каждые 30 минут
// .everyFourHours() // Каждые 4 часа
// .dailyAt('09:00') // Ежедневно в 9:00
// .twiceDaily(9, 18) // Дважды в день (9:00, 18:00)
Production deployment scheduler
# Вариант 1: Использовать встроенный scheduler
npm start & # Запустить сервер
node ace scheduler:work # Запустить scheduler в отдельном процессе
# Вариант 2: Использовать PM2 (рекомендуется)
pm2 start ecosystem.config.js
# Вариант 3: Systemd services
# Создать два сервиса: parser-app.service и parser-scheduler.service
Команды тестирования
# Запустить все тесты
npm test
# или
node ace test
# Запустить конкретный файл теста
node ace test --files=tests/unit/models/auction.spec.ts
# Запустить тесты по паттерну (grep)
node ace test --grep="keyword matching"
node ace test --grep="scraper"
# Запустить только unit тесты
node ace test tests/unit
# Запустить только functional тесты
node ace test tests/functional
Code Quality команды
# Проверка типов TypeScript
npm run typecheck
# Линтинг (ESLint)
npm run lint
# Автофикс линтинга
npm run lint -- --fix
# Форматирование (Prettier)
npm run format
# Проверить форматирование без изменений
npm run format -- --check
Telegram Bot команды
# Запустить Telegram бота (когда будет реализовано в Phase 5)
node ace telegram:start
# С watch режимом
node ace telegram:start --watch
Команды бота для пользователей
После запуска бота, пользователи могут использовать:
/start- Регистрация в системе/addkeyword <слово>- Добавить ключевое слово/keywordsили/listkeywords- Список ваших ключевых слов/deletekeyword <id>- Удалить ключевое слово/help- Справка по командам
Структура проекта
parser-zakupok/
├── app/
│ ├── controllers/ # HTTP контроллеры
│ ├── models/ # Lucid ORM модели
│ │ ├── auction.ts
│ │ ├── keyword.ts
│ │ ├── user.ts
│ │ ├── notification.ts
│ │ └── parse_log.ts
│ ├── services/ # Бизнес-логика
│ │ ├── scraper_service.ts # Парсинг icetrade.by
│ │ └── notification_service.ts # Обработка уведомлений
│ ├── middleware/ # HTTP middleware
│ └── validators/ # VineJS валидаторы
├── commands/ # Ace CLI команды
│ └── parse_auctions.ts # Команда парсинга
├── config/ # Конфигурация приложения
│ ├── app.ts
│ ├── database.ts
│ └── logger.ts
├── database/
│ └── migrations/ # Database миграции
├── start/
│ ├── routes.ts # Маршруты
│ ├── kernel.ts # Middleware регистрация
│ ├── scheduler.ts # Настройка scheduler
│ └── env.ts # Env validation
├── tests/ # Тесты (Japa)
│ ├── unit/
│ └── functional/
├── docs/ # Документация
├── .env # Environment переменные
├── adonisrc.ts # AdonisJS конфигурация
└── package.json
Import Aliases
В проекте настроены удобные алиасы для импортов:
#controllers/* → ./app/controllers/*.js
#models/* → ./app/models/*.js
#services/* → ./app/services/*.js
#validators/* → ./app/validators/*.js
#middleware/* → ./app/middleware/*.js
#config/* → ./config/*.js
#database/* → ./database/*.js
#start/* → ./start/*.js
// Пример использования:
import Auction from '#models/auction'
import ScraperService from '#services/scraper_service'
import { DatabaseConfig } from '#config/database'
Логирование
Все операции логируются через Pino logger:
import logger from '@adonisjs/core/services/logger'
// В коде:
logger.info('Scraping started')
logger.error({ err }, 'Failed to parse page')
logger.debug({ count: auctions.length }, 'Auctions scraped')
Логи сохраняются в:
tmp/logs/app.log(production)- Console output (development)
Parse Logs в БД
Каждый запуск парсинга создает запись в таблице parse_logs:
SELECT * FROM parse_logs ORDER BY started_at DESC LIMIT 5;
-- Поля:
-- id, parse_type, status, started_at, completed_at,
-- items_found, items_new, items_updated, items_failed,
-- error_message, error_details
Полезные запросы:
-- Последние успешные парсинги
SELECT * FROM parse_logs
WHERE status = 'completed'
ORDER BY started_at DESC;
-- Статистика по парсингам
SELECT
COUNT(*) as total_runs,
AVG(items_found) as avg_found,
AVG(items_new) as avg_new,
SUM(items_failed) as total_failed
FROM parse_logs
WHERE parse_type = 'auction';
-- Неудачные парсинги с ошибками
SELECT started_at, error_message
FROM parse_logs
WHERE status = 'failed'
ORDER BY started_at DESC;
Troubleshooting
База данных не подключается
# Проверить что PostgreSQL запущен
# Linux/Mac:
sudo systemctl status postgresql
# Windows:
# Проверить через Services (services.msc)
# Проверить подключение
psql -U postgres -h localhost
# Создать базу вручную
psql -U postgres
CREATE DATABASE parser_zakupok;
\q
Ошибки при парсинге
# Проверить логи
tail -f tmp/logs/app.log
# Проверить parse_logs в БД
node ace repl
> await loadModels()
> const ParseLog = await import('#models/parse_log')
> await ParseLog.default.query().orderBy('started_at', 'desc').first()
# Запустить парсинг одной страницы для отладки
node ace parse:auctions --pages=1 --skip-notifications
Scheduler не запускается
# Проверить что scheduler провайдер добавлен в adonisrc.ts
cat adonisrc.ts | grep scheduler
# Проверить список задач
node ace scheduler:list
# Запустить вручную для проверки
node ace parse:auctions
TypeScript ошибки
# Очистить build и пересобрать
rm -rf build/
npm run build
# Проверить типы
npm run typecheck
# Обновить зависимости
npm update
API Endpoints (будут реализованы в Phase 7)
GET /api/auctions # Список аукционов (пагинация, фильтры)
GET /api/auctions/:id # Детали аукциона
GET /api/keywords # Ключевые слова пользователя
POST /api/keywords # Добавить ключевое слово
DELETE /api/keywords/:id # Удалить ключевое слово
GET /api/notifications # История уведомлений
Docker (будет реализовано в Phase 8)
# Сборка и запуск
docker-compose up -d
# Просмотр логов
docker-compose logs -f app
# Остановка
docker-compose down
Roadmap
- Phase 1: Project Setup
- Phase 2: Database Models & Migrations
- Phase 3: ScraperService Implementation
- Phase 4: Scheduler Command
- Phase 5: Telegram Bot Integration
- Phase 6: NotificationService Enhancement
- Phase 7: REST API Endpoints
- Phase 8: Docker Deployment
Contributing
- Fork репозиторий
- Создать feature branch (
git checkout -b feature/AmazingFeature) - Commit изменения (
git commit -m 'Add some AmazingFeature') - Push в branch (
git push origin feature/AmazingFeature) - Открыть Pull Request
License
Поддержка
При возникновении проблем:
- Проверьте документацию
- Создайте Issue
- Проверьте существующие Issues