
Однажды после очередного код-ревью (Кириллу привет) у меня жутко заболела голова.
Аптечка! У меня дома должна быть аптечка?!

После 10 минут поисков аптечки я обнаружил, что в ней лежит 2 пакетика травы, 75 таблеток мескалина, 5 листов марок мощнейшей кислоты, полсолонки кокаина и гора возбудителей початая ампула изопропилового спирта, засохший листочек подорожника и пачка разнокалиберных таблеток с непонятными названиями.

Асептолин, Риталин, Екокс…что из этого избавит от головной боли, а что является жаропонижающим?! Боль усиливалась…
После еще 5 минут гугления я понял, что мне нужен Баралгин и он, о чудо, есть в моей аптечке…правда его срок годности истёк.
Сквозь завесу боли в голову пришла запоздалая идея: "А вот бы у меня было приложение для учета лекарств в домашней аптечке (cпойлер - теперь такой сервис есть). И чтобы оно напоминало об истечении срока годности…и чтобы можно было оперативно получить информацию о лекарстве…и чтобы просроченное лекарство можно было бы сразу же заказать в аптеке."
Закинувшись просроченными таблетками, я попытался сформулировать основные требования к будущему сервису…
Требования
Сервис должен отображать список лекарств с разбиением на просроченные и непросроченные.
Для каждого лекарства должна отображаться ключевая информация - назначение, противопоказания, побочные эффекты и т.п.
Добавление лекарства должно осуществляеться за минимальное количество шагов: вводим название лекарства (после начала ввода система предлагает варианты препаратов из БД) и месяц/год истечения срока годности - остальная информация (рекомендации к применению, противопоказания, побочки) подгружается из БД;
Пользователь должен иметь возможность в один клик/тап перейти в интернет аптеку для заказа просроченного лекарства в онлайн-аптеке;
Сервис должен периодически проверять, истёк ли срок годности лекарств и, если таки истёк, уведомлять об этом пользователя.
Предполагается, что сервис будет реализован в виде связки telegram бота и web-приложения. Телеграм есть/будет у каждого, т.о. не надо будет заморачиваться ни с доставкой приложения пользователям, ни с публикацией приложения под разные платформы в разных сторах (Telegram постепенно превращается в SuperApp?).
Техническая реализация подобного сервиса довольно тривиальна...

...но где нам взять данные о лекарственных препаратах?
Получаем данные о лекарственных препаратах
В ряде стран эти данные можно получить через бесплатные сервисы. Например OpenFDA в США или NHS APIs в Британии.
В России же с этим несколько сложнее…
Есть такой замечательный справочник лекарственных препаратов - VIDAL. В нём представлена информация о более чем 25 тысячах лекарств.
Есть все необходимые данные - показания к применению, противопоказания, побочные эффекты и т.п.
Более того, информация проверена врачом-экспертом, а для ряда лекарств есть ссылки оформление заказа в онлайн-аптеках.
Одна беда - доступ к API\БД стоит денег, да и компания работает только с юр лицами.
Как же нам получить эти данные?
Вариант со взломом сервера сайта отпал сразу после того, как я бегло пролистал УК РФ - 272 статья - это не шутка. Остался только один вариант - соSCRAPYть необходимую информацию.

Изучив вопрос с технической и правовой стороны, а также получив одобрение на использование материалов сайта от компании владельца VIDAL (социальная инженерия вежливость творит чудеса), я приступил к работе.
Извлекать данные с сайта мы будем в три этапа при помощи библиотеки Scrapy.
Три этапа == три CrawlSpider'a == три набора правил парсинга и извлечения информации с web-страницы.

Паук №1 - на главной странице сайта получаем ссылки на списки лекарственных препаратов, сгруппированных по первой букве названия лекарства;

Павук №2 - для каждой ссылки, полученной на предыдущем шаге, мы скрейпим ссылки на лекарственные препараты;

Паук №3 - извлекаем ключевую информацию со страницы лекарственного препарата.

Как итог мы получим ключевую информацию о более чем 25 тысячах лекарственных препаратах:
Фармакологическая группа;
Показания к применению;
Противопоказания к применению;
Побочные эффекты;
Особые указания.
Для некоторых лекарств есть возможность оформления заказа в электронных аптеках, список которых запрашивается каждый раз при загрузке страницы с препаратом. Это обычный rest запрос, для которого необходим только идентификатор лекарства (даже токен не нужен, что как-то странно...), который мы также извлекаем при помощи web скрейпинга.

Разработка сервиса

Код проекта: MedicineBoxBot
Нет, ну серьёзно, не хочется вдаваться в технические подробности - на Хабре уйма статей а-ля Пишем Телеграм бота на aiogram (1..n), в которых подробно описаны все аспекты разработки...давайте лучше посмотрим, что получилось и обсудим сервис в комментариях.
А теперь слайды!







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

Тратить деньги на домен, статический ip и ssl сертификат тоже жалко (нет, я не жадный, я домовитый), поэтому доменное имя мы получим в сервисе no-ip (инструкция), за обновление привязки динамического ip адреса к домену будет отвечать Dynamic Update Client (DUC) от того-же no-ip. SSL сертификат cгенерируем при помощи let’s encrypt.
Для удобства управления вся наша инфраструктура будет работать внутри docker контейнеров.
docker-compose.yml
---
services:
rabbitmq:
image: rabbitmq:3.10.7-management
container_name: rabbitmq
hostname: rabbitmq
restart: always
env_file:
- ../secrets/rabbitmq_vault.env
ports:
- '15672:15672'
- '5672:5672'
volumes:
- ./rabbitmq:/var/lib/rabbitmq
noip_duc:
image: 'ghcr.io/noipcom/noip-duc:latest'
container_name: noip
restart: unless-stopped
env_file:
- ../secrets/no_ip_vault.env
nginx_proxy:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
container_name: nginx
depends_on:
- webapp
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
webapp:
container_name: webapp
build: ../webapp
depends_on:
- rabbitmq
volumes:
- ./../db/:/app/db
ports:
- '8000:8000'
environment:
WEBAPP_PORT: 8000
env_file:
- ../secrets/webapp_vault.env
bot:
container_name: bot
build: ../bot
depends_on:
- rabbitmq
volumes:
- ./../db/:/app/db
env_file:
- ../secrets/bot_vault.env
rabbitmq — брокер сообщений для обмена данными между web‑приложением и telegram ботом (сообщения об истечении срока годности лекарственного препарата);
noip_duc — dynamic update client. Периодически подключается к no-ip и обновляет информацию об ip адресе, за которым закреплено доменное имя;
nginx_proxy (Nginx proxy manager) - nginx сервер с удобной web админкой и возможностью генерировать (и автоматически обновлять) SSL сертификаты;
webapp и bot - контейнеры с web-приложением и ботом соответственно.
После подготовки raspberry, установки docker, настройки Nginx Proxy Manager и запуска контейнеров наш сервис готов к работе.

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