Поиск ближайших любительских соревнований по бегу, плаванию, велосипедам и другим видам спорта

от автора

Последние годы увлекаюсь любительскими соревнованиями и существует проблема с поиском близлежащих соревнований — есть несколько разных сайтов которые публикуют анонсы организаторов и на этих сайтах обычно все дисциплины вперемешку и никогда нет даже общей карты, в каких городах проводятся эти соревнования.

Например, я живу в Перми и хочу поучаствовать на следующих выходных в какой нибудь активности, например, побегать, прокатиться на велосипеде или поплавать. Ввожу в поиске «Пермь», но в результатах выдачи ничего нет. Хотя есть соревнование в посёлке Юг, который находится меньше часа езды от Перми. Но раз я ищу «Пермь», то «Юг» понятное дело в списке не выводится…

Вместе с Александром Ивановым, одним из авторов телеграм -канала «Таблицы Гугл», мы решили разобраться с этой проблемой техническим способом — написать Apps Script, который обойдёт общий список соревнований на каждом из сайтов, где публикуются анонсы и соберет информацию о названии соревнования, дате и и городе, где проводится соревнование. А потом соберёт сводную таблицу предстоящих соревнований и, в идеале, отобразит эти точке на карте — так любитель сразу может понять, где проводятся ближайшие соревнования, какая у них дата и куда можно поехать. 

По итогу получилось не совсем так, как планировалось, но это может стать отправной точкой к собственным поискам. 

К тому же любительские соревнования это в первую очередь не сам спорт, а укрепление здоровья и забота о себе — по крайней мере, для меня. Я не фокусируюсь на спортивных достижениях, а участвую ради удовольствия, физической активности и возможности побывать в новых местах.

 Скриншот таблицы  amateur_sports_competitions  на 01.07.2024

Скриншот таблицы amateur_sports_competitions на 01.07.2024

Проблемы с поиском любительских соревнований

Проблема с поиском на самом деле существует — уже несколько лет в теме и последний год занимаюсь вместо живого тренера под руководством ChatGPT. Но когда я хочу найти какое-нибудь соревнование по бегу, велосипедам или плаванию, то информация об этом разбросана по нескольким сайтам, что очень неудобно. 

Например, у меня есть свободные выходные и приходится тратить много времени на изучение разных сайтов, например:

И всё потому, что нет какого-то  централизованного источника по всем дисциплинам. Мало того, что сайтов достаточно много, так ещё и функции поиска на этих сайтах неэффективны. 

Например, я могу захотеть съездить в другой город, но больше 6 часов на машине я не поеду. Или даже могу слетать куда-нибудь, чтобы побывать в Санкт-Петербурге. То есть интерес затрагивает в целом все города России.

Но особенно обидно, когда понимаешь что было время и пропустил какую-нибудь близлежащую локацию из-за требований точного совпадения города в поиске на всех сайтах. Набираю «Пермь», а соревнование было в посёлке Юг.

Маршрут из Перми до Юга

Маршрут из Перми до Юга

Предлагаемое решение

Ребята из телеграм -канала «Таблицы Гугл» создали простой Google Apps Script, который автоматизирует процесс сбора данных о событиях с определенных сайтов и обновляет Google Таблицу с этой информацией.

Решение доступно по ссылке: amateur_sports_competitions.

Как это работает?

Этот скрипт Google Apps автоматизирует процесс парсинга и сохраняет данные об соревнованиях с трех конкретных самых популярных веб-сайтов в соответствующие вкладки гугл таблицы. 

Функция run запускает функции сбора данных для iron-star.com, myrace.info и russiarunning.com и гарантирует, что документ Google Sheets будет заполнен актуальной информацией о соревнованиях из этих источников данных.

Этот код состоит из трех основных функций, каждая из которых посвящена конкретному сайту, и центральной функции для координации этих задач по автоматическому извлечению данных. 

Вот подробное описание того, что делает каждая часть кода:

1. Манифест (файл appsscript.json):

Этот файл устанавливает в проекте следующие настройки:

— Устанавливается часовой пояс Москва.

— Добавляет библиотеку Cheerio для анализа HTML.

— Включает регистрацию исключений через Stackdriver.

— Для сценария выполнения используется среда V8.

{   "timeZone": "Europe/Moscow",   "dependencies": {     "libraries": [       {         "userSymbol": "Cheerio",         "version": "14",         "libraryId": "1ReeQ6WO8kKNxoaA_O0XEQ589cIrRvEBA9qcWpNqdOP17i47u6N9M5Xh0"       }     ]   },   "exceptionLogging": "STACKDRIVER",   "runtimeVersion": "V8" }

2. Основная функция (файл 0.index.gs):

Основная функция run служит точкой входа, которая последовательно вызывает три другие функции для извлечение данных с сайтов: 

  1. userActionScrapeMyraceInfo

  2. userActionScrapeIronStarCom

  3. userActionScrapeRussiarunningCom.

function run() {   userActionScrapeMyraceInfo();   userActionScrapeIronStarCom();   userActionScrapeRussiarunningCom(); }

3. Парсинг сайта iron-star.com (файл iron-star.com.gs):

Сайт IRONSTAR TRIATHLON, это крупнейшая в России серия соревнований по триатлону.

Автор на соревнованиях Ironstar Sprint Ekaterinburg

Автор на соревнованиях Ironstar Sprint Ekaterinburg

Эта функция парсит информацию о соревнованиях с сайта iron-star.com. Она извлекает содержимое веб-страницы, анализирует его с помощью Cheerio, извлекает URL-адреса событий, даты, имена и города, а затем записывает данные в документ Google Sheets на вкладку с именем iron-star.com.

function userActionScrapeIronStarCom() {   console.log('userActionScrapeIronStarCom', 'start');   // Получаем новые ссылки   const url = 'https://iron-star.com/event/';   const contentList = UrlFetchApp.fetch(url).getContentText();   const $ = Cheerio.load(contentList);   const itemList = $('.event-item-wrap a.event-item');    const values = [];    itemList.map((_, item) => {     const $item = Cheerio.load(item);     const date = $item('.date').text().trim();     const name = $item('.title').text().trim();     const city = $item('.place').text().trim();     const url = `https://iron-star.com${$(item).attr('href')}`;     values.push([url, date, name, city]);   });    const book = SpreadsheetApp.getActiveSpreadsheet();   const sheet = book.getSheetByName('iron-star.com');    sheet.getRange(3, 1, sheet.getMaxRows() - 1, sheet.getMaxColumns()).clearContent();    if (values.length) {     sheet.getRange(2, 1, values.length, values[0].length).setValues(values);   } else {     sheet.getRange('2:2').clearContent();   }   console.log('userActionScrapeIronStarCom', 'finish'); } 

4. Парсинг сайта myrace.info (файл myrace.info.gs):

Сайт MyRace это площадка для регистрации на спортивные события в России. 

Автор на соревнованиях X-Waters Saint Petersburg. Заплыв вокруг Елагина

Автор на соревнованиях X-Waters Saint Petersburg. Заплыв вокруг Елагина

Аналогично предыдущей функции, эта функция собирает информацию о соревнованиях с myrace.info. Она извлекает содержимое веб-страницы, анализирует его с помощью Cheerio, извлекает URL-адреса событий, даты, имена и города, а затем записывает данные в документ Google Sheets на вкладку с именем myrace.info.

function userActionScrapeMyraceInfo() {   console.log('userActionScrapeMyraceInfo', 'start');   // Получаем новые ссылки   const url = 'https://myrace.info/take/';   const contentList = UrlFetchApp.fetch(url).getContentText();   const $ = Cheerio.load(contentList);   const itemList = $('.container-content.centered > a.events-list__item.row');    const values = [];    itemList.map((_, item) => {     const $item = Cheerio.load(item);      const date = $item('.date')       .text()       .replace(/(\d+)-(\d)+/, '$1');     const name = $item('h2').clone().children().remove().end().text().trim();     const city = $item('.flag').text().trim();     const url = `https://myrace.info${$(item).attr('href')}`;     values.push([url, date, name, city]);   });    const book = SpreadsheetApp.getActive();   const sheet = book.getSheetByName('myrace.info');    sheet.getRange(3, 1, sheet.getMaxRows() - 1, sheet.getMaxColumns()).clearContent();    if (values.length) {     sheet.getRange(2, 1, values.length, values[0].length).setValues(values);   } else {     sheet.getRange('2:2').clearContent();   }   console.log('userActionScrapeMyraceInfo', 'finish'); } 

5. Парсинг russiarunning.com (файл russiarunning.com.gs):

Сайт RussiaRunning — это крупнейшая платформа для регистрации на спортивные события в России. Она объединяет организаторов спортивных событий и участников соревнований. На сайте можно выбрать событие, зарегистрироваться на него, оплатить участие и получить подтверждение.

Автор на Пермском марафоне

Автор на Пермском марафоне

Эта функция собирает информацию о соревнованиях с russiarunning.com и работает по другому, отправляя POST-запрос к API сайта. Она получает информацию о событии в формате JSON, извлекает соответствующие детали (URL-адреса, даты, имена и места) и записывает данные в документ Google Sheets на вкладку с именем russiarunning.com.

function userActionScrapeRussiarunningCom() {   console.log('userActionScrapeRussiarunningCom', 'start');   const url = 'https://russiarunning.com/api/events/list/ru';   const payload = {     Take: 500,     DateFrom: new Date().toISOString().split('T')[0],   };   const options = {     method: 'post',     contentType: 'application/json',     payload: JSON.stringify(payload),     muteHttpExceptions: true,   };   const data = UrlFetchApp.fetch(url, options);    const items = JSON.parse(data.getContentText()).Items;    const values = items.map((item) => {     const { c, d, t, p } = item;     const link = `https://russiarunning.com/event/${c}/`;     const date = d.split('T')[0];     const row = [link, date, t, p];     return row;   });    const book = SpreadsheetApp.getActive();   const sheet = book.getSheetByName('russiarunning.com');    sheet.getRange(3, 1, sheet.getMaxRows() - 1, sheet.getMaxColumns()).clearContent();    if (values.length) {     sheet.getRange(2, 1, values.length, values[0].length).setValues(values);   } else {     sheet.getRange('2:2').clearContent();   }   console.log('userActionScrapeRussiarunningCom', 'finish'); } 

Что в итоге?

После парсинга трёх сайтов через QUERY запрос делается выборка всех предстоящих соревнований от сегодняшнего дня вперёд в будущее. QUERY запрос нужен чтобы отсечь предыдущие даты — ведь при парсинге некоторые сайты отдают и предыдущие соревнования.

 Скриншот таблицы  amateur_sports_competitions  на 29.06.2024

Скриншот таблицы amateur_sports_competitions на 29.06.2024

Эта формула в гугл таблицах объединяет данные о соревнованиях из трех разных листов, фильтрует их, чтобы включить только будущие события (или события, происходящие сегодня), и сортирует эти события по дате в порядке возрастания:

=QUERY(         {         myrace.info!A:D;         'iron-star.com'!A:D;         russiarunning.com!A:D         };  "SELECT *  WHERE Col2 >= date '"&text(today();"yyyy-MM-dd")&"' Order by Col2 asc ")

Что не получилось сделать

Идея сделать подобную таблицу пришла ещё в середине мае, но работа была закончена только сейчас и перед вами уже вторая версия кода — первый был гораздо более сложный и по другому осуществлял процесс парсинга.

Важную часть идею — не только собрать, но и отобразить места на карте — решили перенести во вторую часть этой статьи (работа над которой сейчас идёт), потому что столкнулись с тем, что не всегда указаны места проведения и не всегда они указаны четко: например указан какой-то посёлок, но в какой он находится области — это непонятно.

⚠️ Как пользоваться для самых далёких от программирования

По ссылке ниже откроется ваша копия таблицы. Если в браузере выполнен вход в разные гугл-аккаунты, то нужно оставить только один, иначе при переходе по ссылке может появиться ошибка. По ссылке Вам предложат создать свою копию таблицы — создайте копию в свой аккаунт:

➡️amateur_sports_competitions⬅️

Дальше откройте редактор сценариев Google Apps:

 – В Google Таблице выберите «Расширения» > «Apps Script».

 – Редактор скриптов Google Apps откроется на новой вкладке.

Теперь, когда у вас открыт редактор сценариев приложений запустите функцию под названием run(). Чтобы её запустить нажмите кнопку воспроизведения (значок треугольника). 

При первом запуске скрипту необходимо авторизоваться для работы. Для этого потребуется авторизация в вашем аккаунте. Полный процесс запуска можно изучить по статье открываем редактор скриптов в Гугл таблице. Не бойтесь, что скрипт как-то повредит вам — в любой момент вы можете посмотреть список выданных вами разрешений на специальной странице и в один клик их отозвать. В целом, вам не стоит беспокоиться, если это ваша Таблица и ваш код, так как все права администрирования остаются только за вами.

Гугл предупредит, что приложение не проверено. Нужно выбрать «Дополнительные настройки», а потом перейти по нижней ссылке. После этого останется нажать на кнопку «Разрешить», и на этом первоначальная настройка будет готова.

Выполнив эти шаги, вы сможете успешно запустить скрипт поиска соревнований по всей России — он обновит уже имеющиеся данные в вашей копии таблицы.

Заключение

Проведена большая работа по созданию сводной таблицы по самым популярным площадкам, которые публикуют информацию о любительских соревнованиях.

Вы всегда можете сделать собственную копию данной таблицы и пользоваться ей — она автоматически скачает для вас все соревнования и представит их в удобном табличном виде.

Репозиторий программы: googlesheets-ru/amateur_sports_competitions

Код создан командой телеграм-канала «Таблицы Гугл»

Автор текста и идеи: Михаил Шардин

1 июля 2024 г.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Какой физической активностью занимаетесь Вы?

52.94% Бег9
52.94% Велосипед9
23.53% Плавание4
5.88% Никакой активностью не занимаюсь1
5.88% Напишу свой вариант в комментариях1

Проголосовали 17 пользователей. Воздержавшихся нет.

ссылка на оригинал статьи https://habr.com/ru/articles/825508/