По прошлым публикациям видно, что у меня есть некий телеграм-бот. Этот бот имеет такую фичу: поздравляет с каким-нибудь праздником на этот день. Всякий раз, когда нужно было вызвать эту фичу, он идёт на сайт со списком праздников, берёт заданную дату и выводит название праздника. Настало время, когда такая зависимость от сайта меня перестала устраивать. Решено забрать от сайта все праздники, записать их куда-нибудь по-ближе.
Итак алгоритм этой работы будет таким:
- В цикле от 1 января по 31 декабря (включая 29 февраля)
- Сформировать список ссылок для первых COUNT_REQUEST дней
- Запросить праздники для первых COUNT_REQUEST дней
- Запомнить праздники
- Подождать DELAY_REQUEST секунд (ведь у меня цель не задосить сайт, а парсить)
- Счётчик цикла увеличить на COUNT_REQUEST
- После выхода из цикла записать считанные праздники в JSON.
Перейдём к реализации на JS
Первый и шестой пункт цикла:
const COUNT_REQUEST = 2;
const DELAY_REQUEST = 20000;
/**
* Прибавить в дате countDays дней
* @param {Date} date
* @param {number} countDays
* @returns {Date}
*/
export function addDay(date, countDays = 1) {
const newDate = new Date(date);
return new Date(newDate.setDate(newDate.getDate() + countDays));
}
const startDate = new Date("2020-01-01");
const endDate = new Date("2020-12-31");
let loop = new Date(startDate);
while (loop <= endDate) {
loop = addDay(loop, COUNT_REQUEST);
await delay(DELAY_REQUEST);
}
2. Сформировать список ссылок для первых COUNT_REQUEST дней
/**
* @typedef {Object} UrlData
* @property {Date} date - Запрашиваемый день
* @property {string} url - Ссылка
*/
/**
* Получить массив ссылок для countDays дней
* @param {Date} startDate С какой даты начинать делать ссылки
* @param {number} countDays Сколько ссылок спрашивать
* @param {Date} endDate За какую дату не заходить
* @returns {UrlData}
*/
export function getUrls(startDate, countDays, endDate) {
//--- Текст функции
return urls;
}
3. Запросить праздники для первых COUNT_REQUEST дней
Для этого понадобятся два пакета node-fetch и node-html-parser.
Для реализации использовал фичу из 16 версии nodejs AbortController . Хотя и не полностью как в статье сделал,- setTimeout у меня по старинке запускается.
import fetch from "node-fetch";
import { parse } from "node-html-parser";
/**
* Запрос списка праздников
* @param {string} url
* @param {Date} date
* @returns {String[]}
*/
export async function getHolydays(url, date) {
// Для отмены фетча
const cancelFetch = new AbortController();
// Промис запроса к сайту
const promise = fetch(url, {
timeout: REQUEST_TIMEOUT,
signal: cancelFetch.signal,
});
// Время ожидания
const timeout = setTimeout(() => {
cancelFetch.abort();
}, WAIT_REQUEST_TIMEOUT);
try {
const response = await promise;
// Получить текст HTML
const htmlContent = await response.text();
// Получить структуру DOM
const root = parse(htmlContent);
// Массив праздников: DOM-элементы
const source = root.querySelectorAll(".holydays >span");
// Массив праздников: текст
const holidays = source.map((element) => element.textContent);
return { holidays, day: date.getDate(), month: 1 + date.getMonth() };
} catch (e) {
console.log("FetchError :>> ", date);
return null;
} finally {
clearTimeout(timeout);
}
}
// Получить список праздников из массива ссылок
const promisesOfHolidays = await Promise.all(
urlsData.map(async (ud) => await getHolydays(ud.url, ud.date))
);
4. Запомнить праздники
Результат собирается в массив
let holidayData = [];
///
const promisesOfHolidays = await Promise.all(
urlsData.map(async (ud) => await getHolydays(ud.url, ud.date))
);
holidayData = [
...holidayData,
...promisesOfHolidays.filter((r) => r !== null),
];
5. Подождать DELAY_REQUEST секунд
Использую @stanislavkarol/delay
7. После выхода из цикла записать считанные праздники в JSON.
import fs from "fs";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
if (!fs.existsSync(`${__dirname}/../json`)) {
fs.mkdirSync(`${__dirname}/../json`);
}
fs.writeFile(
`${__dirname}/../json/holidays.json`,
JSON.stringify(holidayData),
(err) => {
if (err) throw err;
console.log("Data written to file");
}
);
Всё вместе, в рабочем виде, на гитхаб.
Top comments (0)