Skip to content
UKENRU
Kyiv, Ukrainemail@dvornichenko.com
Михаил Дворниченко Архитектор решений · SK.AI
Home Blog Все заметки

Как автоматически получать сумму сбора с МоноБанки без API

Приветствую! Поделюсь простым решением: как получить сумму сбора с МоноБанки и подтянуть её на свой сайт, в чат-бот или другой сервис.

Задача появилась, когда я не нашёл адекватного решения в API МоноБанка. Там есть механизм Webhook: он должен присылать сумму после пополнения банки, но у меня он так и не заработал. Может, я что-то не так понял, может, торопился или были проблемы на сервере, но зарегистрировать listener так и не вышло. Написал в поддержку Моно — ответили только через сутки, и то в стиле «Читай документацию, там всё есть». Пока ждал, успел написать другое решение — не такое красивое, работает «тупо в лоб», но работает и задачу мою решает 🙂

Идея:

Идея простая — парсить страницу напрямую. Опасался, что МоноБанк генерирует CSS-классы динамически, чтобы защититься от парсинга, но, к счастью, все классы статические и всё очень просто.

План действий:

  1. Написать на Python веб-сервис: он принимает запрос с идентификатором страницы МоноБанки, парсит её и отдаёт JSON с результатами.
  2. На хостинге написать PHP-скрипт, который по CRON каждые 30 минут дёргает этот веб-сервис. Ответ складывает в базу или в файл, чтобы потом им пользоваться. Но тут уже как вам удобнее

Зачем cron и промежуточное хранение?

Почему я делаю это через CRON и кладу значение в промежуточную базу, а не дёргаю сервис каждый раз, когда пользователь заходит на сайт? Чтобы случайно не начать «штурмовать» страницу МоноБанка запросами всякий раз, когда кто-то открывает сайт. Лишняя нагрузка тут ни к чему. Обновлять данные раз в полчаса или даже раз в час — для такой копии вполне нормально: цифра остаётся актуальной, а систему это не грузит.

Что нам понадобится:

  • Простой VPS, чтобы разместить сервис. У меня дешёвый: 1 Гб оперативки, 1 CPU и 20 Гб диска;
  • Flask для веб-сервиса;
  • Selenium и Chrome WebDriver для парсинга страницы.

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

Flask-сервис принимает GET-запрос с идентификатором МоноБанки и токеном. Токен нужен, чтобы закрыть сервис от случайных ботов и «незваных гостей». Чисто для примера я прописал токен прямо в коде.
На выходе получим JSON с названием организации, названием сбора и собранной суммой. При желании можно парсить и другие поля.

Вот здесь идентификатор банки:

Решение, как автоматизировать получение суммы сбора с МоноБанки без API — иллюстрация 1

Код Flask-сервиса:

from flask import Flask, request, jsonify
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import re

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False
ACCESS_TOKEN = "YOUR_TOKEN_HERE"

def get_jar_data(account_code):
    chrome_options = Options()
    chrome_options.add_argument("--headless")
    chrome_options.add_argument("--no-sandbox")
    chrome_options.add_argument("--disable-dev-shm-usage")
    service = Service("/usr/local/bin/chromedriver")
    driver = webdriver.Chrome(service=service, options=chrome_options)

    try:
        driver.get(f'https://send.monobank.ua/jar/{account_code}')
        wait = WebDriverWait(driver, 20)

        amount = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, '.stats-data-value'))).text
        name = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, 'div.field.name h1'))).text
        owner_info = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, 'div.field.jarOwnerInfo span'))).text
        clean_amount = re.sub(r'[^d.,]', '', amount)

        return {"name": name, "owner_info": owner_info, "balance": clean_amount}

    except Exception as e:
        return {"error": f"Error: {e}"}

    finally:
        driver.quit()

@app.route('/get_balance', methods=['GET'])
def get_jar_data_route():
    account_code = request.args.get('account_code')
    access_token = request.args.get('access_token')

    if not account_code or not access_token:
        return jsonify({"error": "Error account_code и access_token"}), 400
    if access_token != ACCESS_TOKEN:
        return jsonify({"error": "Wrong Token"}), 403

    data = get_jar_data(account_code)
    return jsonify(data), (500 if "error" in data else 200)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)


Code language: Python (python)

В результате получим такой ответ:

{
“balance”: “20214”,
“name”: “РЕБ для саперів 68 батальйону ТРО”,
“owner_info”: “БФ ”СЕЙВ ЮКРЕЙН ЛАЙФ” created a fundraiser”
}Code language: JSON / JSON with Comments (json)
Решение, как автоматизировать получение суммы сбора с МоноБанки без API — иллюстрация 2

И это совпадает со значением на странице:

Решение, как автоматизировать получение суммы сбора с МоноБанки без API — иллюстрация 3

PHP-скрипт для  CRON

Скрипт ставлю в CRON на выполнение каждые 30 минут. Для примера складываю результат в json-файл:

<?php
$url = ‘http://YOUR_SERVER_IP:PORT/get_balance?account_code=YOUR_ACCOUNT_CODE&access_token=YOUR_ACCESS_TOKEN’;

$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 60,
CURLOPT_TIMEOUT => 120,
CURLOPT_FAILONERROR => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false
]);

$response = curl_exec($ch);

if (curl_errno($ch)) {
file_put_contents(‘error_log.txt’, ‘Request error: ‘ . curl_error($ch) . “n”, FILE_APPEND);
} elseif (!empty($response)) {
$decoded_response = json_decode($response, true);
if (json_last_error() === JSON_ERROR_NONE) {
file_put_contents(‘curcollect.json’, json_encode($decoded_response, JSON_PRETTY_PRINT));
} else {
file_put_contents(‘error_log.txt’, “JSON decoding error: ” . json_last_error_msg() . “n”, FILE_APPEND);
}
} else {
file_put_contents(‘error_log.txt’, “Empty response from servern”, FILE_APPEND);
}

curl_close($ch);
?>
Code language: HTML, XML (xml)

Надеюсь, было полезно! Посмотреть, как это работает, можно по ссылкам ниже. 

Напоминаю, что сейчас идёт сбор на РЭБ для военных, и Ваша поддержка очень важна:

Страница сбора на сайте: https://save-ukraine.life/ua_fundraising/reb-dlia-saperiv-68-batalyonu-tro/
МоноБанка сбора: https://send.monobank.ua/jar/8uw2Qx1C21

Michael Dvornichenko Архитектор решений · SK.AI

Leave a comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *