Node.js 관련 문서

16296 단어 htmlnode
По прошлым публикациям видно, что у меня есть некий телеграм-бот. Этот бот имеет такую ​​사진: поздравляет с каким-нибудь праздником на этот день. Всякий раз, когда нужно было вызвать эту фичу, он идёт на сайт со стиском праздников, нэдёт Настало время, когда такая зависимость от сайта меня перестала устраивать. Решено забрать от сайта все праздники, записать их куда-нибудь по-ближе.
Итак алгоритм этой работы будет таким:
  • В цикле от 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");
      }
    );
    


    Всё вместе, в рабочем виде, на гитхаб .

    좋은 웹페이지 즐겨찾기