В этой статье будет показано, как с помощью A-Parser периодически проверять цены товаров сразу на несколько сайтах и получать уведомление в телеграм в случае их изменения.
Для примера возьмем несколько ссылок на страницы товаров:
Решение будет состоять из нескольких этапов:
Хранить цены будем в базе данных SQLite, т.к. для работы с такими БД в А-Парсере уже есть встроенные инструменты.
- Парсинг названия и цены для каждого товара
- Сравнение полученной цены с ранее сохраненной:
- если данный товар парсится впервые, то просто сохраняем его цену
- иначе считаем разницу и сохраняем новую цену для последующих запусков
- Формирование результата, а также, если цена повысилась или снизилась, то отправляем уведомление в Telegram
Парсинг цен
На данном этапе парсер должен перейти по ссылке и получить код страницы товара.
Т.к. для примера были взяты ссылки из 2-х различных сайтов, то нужно предусмотреть корректный парсинг страниц для каждого сайта. Для этого нужно извлечь домен из ссылки и в зависимости от него парсить данные:Код:resp = yield this.request('GET', url, {}, { attempt: attempt, decode: 'auto-html', browser: 1, headers: { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36' } });
Как видно в коде выше, чтобы обеспечить получение именно той страницы, которая нужна, а не различных заглушек, каптч и т.п., мы будем использовать собственный обработчик попыток и проверять получаемый контент.Код:switch(this.utils.url.extractTopDomainByZone(url)) { case 'avito.ru': if (!/itemProp="price".*?>(.*?)</.test(resp.data)) throw {message: 'Content mismatch'} result.price = parseFloat(this.parseSingle(resp.data, /itemProp="price".*?>(.*?)</)?.replace(/\s/g, '')); result.title = this.parseSingle(resp.data, /<title.*?>(.*?)<\/title>/); break; case 'hotline.ua': if (!/class="price__value".*?>(.*?)<\/span/.test(resp.data)) throw {message: 'Content mismatch'} result.price = parseFloat(this.parseSingle(resp.data, /class="price__value".*?>(.*?)<\/span/)?.replace(/\s/g, '')); result.title = this.parseSingle(resp.data, /<title.*?>(.*?)<\/title>/); break; default: this.logger.put('Unknown query'); resp.success = 0; }
Для удобства парсинг цен вынесем в отдельную функцию, которая должна вернуть объект с флагом успешности запроса, ценой и названием товара.
Сравнение цен
Этот этап мы также реализуем в виде отдельной функции. Логика ее работы такова:
- если новая цена не была спаршена (например, товар закончился и его цена больше не отображается на странице), то возвращаем статус not found
- иначе ищем в БД товар, в качестве идентификатора используем ссылку
- если товар не найден, то записываем в БД данные о товаре и возвращаем статус new
- если товар найден, то сравниваем цены и возвращаем статус up, down или equal, обозначающие направление изменения цены
Обработка результатаКод:comparePrice(url, price, title) { if(!price) return {status: 'not found', oldPrice: ''}; let row = tools.sqlite.get(db, "SELECT * FROM prices WHERE url = ?", url); if(!row) { tools.sqlite.run(db, "INSERT INTO prices(url, title, price) VALUES(?, ?, ?)", url, title, price); return {status: 'new', oldPrice: ''}; } else { tools.sqlite.run(db, "UPDATE prices SET title = ?, price = ? WHERE url = ?", title, price, url); let diff = price - (!row.price ? 0 : row.price); return { status: diff > 0 ? 'up' : diff < 0 ? 'down' : 'equal', oldPrice: row.price }; } }
Записываем полученные данные в results, а также, если статус цены up или down, отправляем сообщение в Telegram.
Для отправки сообщений нужно написать небольшую функцию, которая отправит сообщение и вернет успешность отправки (true/false):Код:results.title = data.title; results.price = data.price; results.oldPrice = status.oldPrice; results.status = status.status; switch(status.status) { case 'up': results.success = yield* this.send(`Цена на <a href="${set.query}">${data.title}</a> <b>повысилась</b>`); break; case 'down': results.success = yield* this.send(`Цена на <a href="${set.query}">${data.title}</a> <b>снизилась</b>`); break; }
В нашем парсере нужно указать токен бота и id чата с ним. Подробно процедура создания бота и добавления его в группу описана здесь в разделе Подготовка.Код:*send(text) { if(!this.conf.token || !this.conf.id) { this.logger.put('Need set Telegram parameters!'); return false; } let resp = yield this.request('POST', 'https://api.telegram.org/bot' + this.conf.token + '/sendMessage', { chat_id: this.conf.id, text: text, parse_mode: 'HTML', disable_web_page_preview: true }, { use_proxy: 0, parsecodes: { 200: 1, 400: 1, 401: 1 } }); if(!resp.success) { this.logger.put('Sending failed'); return false; } try { let json = JSON.parse(resp.data); if(json.ok) { return true; } else { if(json.description) this.logger.put(json.description); return false; } } catch(e) { return false; } }
Заключение
В результате получается парсер, который решает поставленную вначале задачу. Благодаря универсальности кода, можно добавлять любые другие сайты, написав буквально пару строк кода в функции getData по аналогии с уже существующими.
Сохраняем задание на основе этого парсера и с заданными ссылками на товары, создаем в Планировщике задание с периодичностью, например, 1 день. В итоге парсер каждый день будет проверять цены по списку товаров и в случае их изменений, присылать уведомление в Телеграм.
Готовый пресет доступен в Каталоге: https://a-parser.com/resources/366/
-
Вступайте в наш Telegram чат: https://t.me/a_parser Нас уже 2600+ и мы растем!Скрыть объявление
Периодический мониторинг цен с уведомлением в Telegram
Мониторинг цен на товары на mvideo и rozetka с уведомлениями в Телеграм