Created
November 26, 2025 13:33
-
-
Save Anton-V-K/0d5992d5a7c31b6b420828f55d5c6f24 to your computer and use it in GitHub Desktop.
Python script to parse operations from OTP bank statement
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # This script extracts operations from the statement (saved from online.otpbank.ru), | |
| # and prints only the ones with bonuses | |
| # Setup: | |
| # a) install Python3 | |
| # b) pip install BeautifulSoup4 | |
| # Guide: | |
| # 1. Go to online.otpbank.ru | |
| # 2. Select the card | |
| # 3. Select the period | |
| # 4. Scroll to the bottom, so all operations are loaded | |
| # 5. Save the page as 'OTP.html' (you can use any name) | |
| # 6. Run this script: 'OTP.py OTP.html' | |
| import argparse | |
| import datetime | |
| import decimal | |
| from bs4 import BeautifulSoup # pip install BeautifulSoup4 | |
| from functools import reduce | |
| def parse_statement(html_content): | |
| months = \ | |
| (' января ', '.01.'), \ | |
| (' февраля ', '.02.'), \ | |
| (' марта ', '.03.'), \ | |
| (' апреля ', '.04.'), \ | |
| (' мая ', '.05.'), \ | |
| (' июня ', '.06.'), \ | |
| (' июля ', '.07.'), \ | |
| (' августа ', '.08.'), \ | |
| (' сентября ', '.09.'), \ | |
| (' октября ', '.10.'), \ | |
| (' ноября ', '.11.'), \ | |
| (' декабря ', '.12.'), | |
| history = [] | |
| soup = BeautifulSoup(html_content, 'html.parser') | |
| operations = soup.find_all('div', attrs={'data-name': 'accordion-item'}) | |
| for op in operations: | |
| if not op.find('span', string='Бонусные рубли'): | |
| continue | |
| history.append({}) | |
| sum_span = op.find('span', attrs={'data-testid': 'operation-sum'}) | |
| history[-1]['sum'] = sum_span.get_text(strip=True)[:-2].replace(' ', '') | |
| bonus_span = op.find('span', attrs={'data-testid': 'operation-bonus'}) | |
| history[-1]['bonus'] = bonus_span.get_text(strip=True) | |
| container = op.find('div', attrs={'data-name': 'statistic-container'}) | |
| items = container.find_all('div', attrs={'data-name': 'statistic-item'}, recursive=False) | |
| for item in items: | |
| type_span = item.find('span') | |
| if not type_span: | |
| continue | |
| type = type_span.get_text(strip=True) | |
| value_span = item.find('span', attrs={'data-id': lambda x: x and x.startswith('statistic-item-value-')}) | |
| value = value_span.get_text(strip=True) | |
| match type: | |
| # case 'Бонусные рубли': | |
| # history[-1]['bonus'] = value | |
| case 'Дата': | |
| # history[-1]['date'] = value | |
| # (c) https://stackoverflow.com/a/9479972/536172 | |
| timestamp = reduce(lambda a, kv: a.replace(*kv), months, value) | |
| history[-1]['date'] = datetime.datetime.strptime(timestamp, '%d.%m.%Y %H:%M:%S') | |
| return history | |
| def main(): | |
| parser = argparse.ArgumentParser(description='Prints bonus operations from the statement saved from online.otpbank.ru') | |
| parser.add_argument('source', nargs='?', default='OTP.html', help='Source HTML file with statement') | |
| args = parser.parse_args() | |
| with open(args.source, 'r', encoding='utf-8') as file: | |
| html_content = file.read() | |
| history = parse_statement(html_content) | |
| total_sum = 0 | |
| total_bonus = 0 | |
| for op in history: | |
| print(f"{op['date']} {op['sum']:>8} {op['bonus']:>8}") | |
| total_sum += decimal.Decimal(op['sum']) | |
| total_bonus += decimal.Decimal(op['bonus']) | |
| print(f"TOTAL {total_sum} {total_bonus}") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment