В этой статье будет показано, как с помощью A-Parser периодически проверять цены товаров сразу на несколько сайтах и получать уведомление в телеграм в случае их изменения.
Для примера возьмем несколько ссылок на страницы товаров:
Решение будет состоять из нескольких этапов:
Парсинг цен
На данном этапе парсер должен перейти по ссылке и получить код страницы товара.
Т.к. для примера были взяты ссылки из 2-х различных сайтов, то нужно предусмотреть корректный парсинг страниц для каждого сайта. Для этого нужно извлечь домен из ссылки и в зависимости от него парсить данные:
Как видно в коде выше, чтобы обеспечить получение именно той страницы, которая нужна, а не различных заглушек, каптч и т.п., мы будем использовать собственный обработчик попыток и проверять получаемый контент.
Для удобства парсинг цен вынесем в отдельную функцию, которая должна вернуть объект с флагом успешности запроса, ценой и названием товара.
Сравнение цен
Этот этап мы также реализуем в виде отдельной функции. Логика ее работы такова:
Обработка результата
Записываем полученные данные в results, а также, если статус цены up или down, отправляем сообщение в Telegram.
Для отправки сообщений нужно написать небольшую функцию, которая отправит сообщение и вернет успешность отправки (true/false):
В нашем парсере нужно указать токен бота и id чата с ним. Подробно процедура создания бота и добавления его в группу описана здесь в разделе Подготовка.
Заключение
В результате получается парсер, который решает поставленную вначале задачу. Благодаря универсальности кода, можно добавлять любые другие сайты, написав буквально пару строк кода в функции getData по аналогии с уже существующими.
Сохраняем задание на основе этого парсера и с заданными ссылками на товары, создаем в Планировщике задание с периодичностью, например, 1 день. В итоге парсер каждый день будет проверять цены по списку товаров и в случае их изменений, присылать уведомление в Телеграм.
Готовый пресет доступен в Каталоге: https://a-parser.com/resources/366/
Для примера возьмем несколько ссылок на страницы товаров:
Решение будет состоять из нескольких этапов:
- Парсинг названия и цены для каждого товара
- Сравнение полученной цены с ранее сохраненной:
- если данный товар парсится впервые, то просто сохраняем его цену
- иначе считаем разницу и сохраняем новую цену для последующих запусков
- Формирование результата, а также, если цена повысилась или снизилась, то отправляем уведомление в Telegram
Парсинг цен
На данном этапе парсер должен перейти по ссылке и получить код страницы товара.
Код:
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.
Код:
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;
}
Код:
*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/