/
Ged52
/
simpleestate_parser
Обзор
Документация
Войти
/
Ged52
/
simpleestate_parser
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
simpleestate_parser.py
343 строки
14 KB
Ged52
Merge branch 'master' of https://gitverse.ru/Ged52/simpleestate_parser
21 фев 2026, 20:28
21 фев 2026, 20:28
9976929
Код
Авторство
О чём код?
# === НАСТРОЙКИ === CHROMEDRIVER_PATH = r"C:\Users\Dexp\Documents\python\SimpleEstateGetSale\chromedriver\chromedriver.exe" # Укажи путь к chromedriver.exe CHROMEDRIVER_PATH = r"chromedriver\chromedriver.exe" # Укажи путь к chromedriver.exe COOKIES_FILE = "cookies.json" OUTPUT_FILE = "simpleestate_full_data.json" import os import json import time import requests import argparse from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from datetime import datetime # === АВТООПРЕДЕЛЕНИЕ ПЛАТФОРМЫ И ПУТИ К CHROMEDRIVER === import platform SYSTEM = platform.system() # 'Windows' или 'Linux' # Определяем папку с chromedriver относительно текущего скрипта SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) DRIVER_DIR = os.path.join(SCRIPT_DIR, "chromedriver") # === НАСТРОЙКИ === COOKIES_FILE = "cookies.json" OUTPUT_FILE = "simpleestate_full_data.json" def save_cookies(cookies, path=COOKIES_FILE): with open(path, "w", encoding="utf-8") as f: json.dump(cookies, f, ensure_ascii=False, indent=2) def load_cookies(path=COOKIES_FILE): if os.path.exists(path): with open(path, "r", encoding="utf-8") as f: return json.load(f) return None def api_request(session, url, params=None): try: resp = session.get(url, params=params, timeout=10) if resp.status_code == 403: print(f"❌ Доступ запрещён (403) — возможно, требуется авторизация: {url}") return None resp.raise_for_status() try: return resp.json() except json.JSONDecodeError: print(f"❌ Ошибка парсинга JSON: {url}") return None except Exception as e: print(f"❌ Ошибка запроса {url}: {e}") return None def fetch_user_portfolio(session): data = api_request(session, "https://simpleestate.ru/api/investment/user-shares/") if not data: return {} portfolio = {} for item in data.get('results', []): estate_id = item['share']['estate']['id'] name = item['share']['estate']['name'] acronym = item['share']['estate']['acronym'] url = item['share']['estate']['url'] image = item['share']['estate']['image'] qty = item['quantity'] avg_price = item.get('avg_share_purchase_price', 0) current_value = item.get('current_value', 0) dividends_summary = item['dividends_summary'] portfolio[name] = { 'estate_id': estate_id, 'acronym': acronym, 'quantity': qty, 'avg_purchase_price': avg_price, 'current_value': current_value, 'dividends_summary': dividends_summary, 'url': url, 'image': image } return portfolio def fetch_all_orders(session): all_orders = [] page = 1 while True: data = api_request(session, "https://simpleestate.ru/api/trade/order/", { 'ordering': 'direction,price', 'page': page, 'page_size': 100, 'status': 'active' }) if not data: break results = data.get('results', []) if not results: break all_orders.extend(results) page += 1 if not data.get('next'): break time.sleep(0.2) return all_orders def fetch_trade_shares(session): data = api_request(session, "https://simpleestate.ru/api/trade/shares/", {'page': 1, 'page_size': 1000}) if not data: return {} share_info = {} for share in data.get('results', []): name = share['estate']['name'] share_info[name] = { 'estate_id':share['estate']['id'], 'type':share['estate']['type'], 'price': float(share['price']), 'min_price': float(share['min_trade_price']), 'max_price': float(share['max_trade_price']), } return share_info # Теперь возвращает полную информацию def fetch_user_transactions(session): data = api_request(session, "https://simpleestate.ru/api/user/activity/", {'is_transaction': 'true'}) if not data: return [] return data.get('results', []) def print_portfolio(portfolio): print("\n" + "="*80) print("💼 МОЙ ПОРТФЕЛЬ") print("="*80) print(f"Название\tКол-во акций\tЦена покупки\tТекущая цена\tТекущая стоимость\tДивиденды\t") if portfolio: for name, info in sorted(portfolio.items()): print(f"{info['acronym']} - {name}\t{info['quantity']}\t{int(info['avg_purchase_price']):,} ₽\t{int(info['current_value']):,}\t{int(info['dividends_summary']):,} ₽") else: print(" Пусто") def print_orders(orders, direction, rec_prices=None): title = "🛒 ЗАЯВКИ НА ПОКУПКУ" if direction == 'buy' else "🏷️ ЗАЯВКИ НА ПРОДАЖУ" print("\n" + "="*80) print(title) print("="*80) if direction == 'buy': print(f"Объект Цена Кол-во Частичн. Сумма ₽ Анализ цены") else: print(f"Объект Цена Кол-во Частичн. Сумма ₽ Анализ цены") filtered_orders = [o for o in orders if o['direction'] == direction] for order in sorted(filtered_orders, key=lambda x: (x['object'], x['price'])): obj_name = order['object'] price = order['price'] rec_data = rec_prices.get(obj_name) if rec_prices else None partial_icon = "✅" if order['is_partial'] else "🔒" # Формируем строку анализа цены price_analysis = "" if rec_data: rec_price = rec_data['price'] min_price = rec_data['min_price'] max_price = rec_data['max_price'] # Процент от рекомендованной pct_of_rec = price / rec_price * 100 if price < min_price: diff_min = (min_price - price) / min_price * 100 price_analysis = f"({pct_of_rec:.0f}%) ⚠️ ниже мин. на {diff_min:.0f}%!" elif price > max_price: diff_max = (price - max_price) / max_price * 100 price_analysis = f"({pct_of_rec:.0f}%) ⚠️ выше макс. на {diff_max:.0f}%!" elif price == max_price: price_analysis = f"({pct_of_rec:.0f}% — максимум ✅)" elif price == min_price: price_analysis = f"({pct_of_rec:.0f}% — минимум ✅)" else: price_analysis = f"({pct_of_rec:.0f}%)" else: price_analysis = "(нет данных)" total = int(order['total']) name_lenght=40 print(f"{obj_name[:name_lenght]:<{name_lenght}} {int(price):>6,} {order['quantity']:>4} {partial_icon:>2} {total:>9,} ₽ {price_analysis}") def print_transactions(transactions): print("\n" + "="*80) print("📅 МОЯ ИСТОРИЯ ТРАНЗАКЦИЙ") print("="*80) print(f"Дата\tСумма\tОбъект\tОписание") total_in = 0.0 total_out = 0.0 if transactions: sorted_transactions = sorted(transactions, key=lambda x: x['created'], reverse=False) for tr in sorted_transactions: created = tr['created'][:10] estate = tr['estate'] value = float(tr['value']) desc = tr['description'] activity_type = tr['activity_type'] if activity_type == 'transfer_of_investor_funds': sign = '+' total_in += value amount_str = f"{sign}{int(value):,} ₽" elif activity_type == 'investor_leaves': sign = '-' total_out += abs(value) amount_str = f"{sign}{int(abs(value)):,} ₽" else: amount_str = f"{int(abs(value)):,} ₽" print(f"🔹 {created}\t{amount_str}\t{estate}\t{desc}") net_in = total_in - total_out print("\n" + "-"*80) print(f"📥 Всего пополнено: {int(total_in):,} ₽") print(f"📤 Всего выведено: {int(total_out):,} ₽") print(f"💰 Чистый ввод: {int(net_in):,} ₽") print("-"*80) else: print(" Нет транзакций") def interactive_mode(): print("\n🧠 Режим интерактивный:") print(" --orders — показать только заявки") print(" --portfolio — показать портфель и транзакции") print(" Без ключей — запуск полного режима (все данные)") print("Пример: python se_second_api.py --orders") input("\nНажмите Enter для запуска полного режима...") def main(): parser = argparse.ArgumentParser(description="SimpleEstate API: Данные портфеля и вторичного рынка") parser.add_argument('--orders', action='store_true', help='Показать только заявки') parser.add_argument('--portfolio', action='store_true', help='Показать портфель и транзакции') args = parser.parse_args() if not args.orders and not args.portfolio: interactive_mode() print("🚀 SimpleEstate: Загрузка данных") session = requests.Session() cookies_valid = False saved_cookies = load_cookies() if saved_cookies: for cookie in saved_cookies: session.cookies.set(cookie['name'], cookie['value']) # Проверяем доступ к приватному эндпоинту test = api_request(session, "https://simpleestate.ru/api/investment/user-shares/") if test is not None and 'results' in test: cookies_valid = True print("✅ Сессия активна (куки загружены)") else: print("⚠️ Куки найдены, но не прошли проверку авторизации") if not cookies_valid: print("\n👉 Требуется авторизация (логин + капча + SMS)") options = Options() options.add_argument("--start-maximized") #driver = webdriver.Chrome(service=Service(CHROMEDRIVER_PATH), options=options) # Указываем правильное имя драйвера в зависимости от ОС if SYSTEM == "Windows": driver = webdriver.Chrome(service=Service(CHROMEDRIVER_PATH), options=options) else: # Linux, Darwin (macOS) driver = webdriver.Chrome(options=options) try: driver.get("https://simpleestate.ru/secondary-market/") print("\n" + "="*60) print("1. Авторизуйтесь в браузере") print("2. После загрузки карточек — нажмите ENTER здесь") print("="*60) input() save_cookies(driver.get_cookies()) session = requests.Session() for cookie in driver.get_cookies(): session.cookies.set(cookie['name'], cookie['value']) print("✅ Куки сохранены") finally: driver.quit() # Загружаем данные portfolio = fetch_user_portfolio(session) all_orders = fetch_all_orders(session) rec_prices = fetch_trade_shares(session) transactions = fetch_user_transactions(session) # Преобразуем заявки для вывода orders_for_print = [] for order in all_orders: name = order['share']['estate']['name'] direction = order['direction'] entry = { 'order_id':order['id'], 'estate_id':order['share']['estate']['id'], 'object': name, 'price': float(order['price']), 'quantity': int(order['quantity']), 'is_partial': order.get('is_partial', False), 'total': float(order['price']) * int(order['quantity']), 'direction': direction } orders_for_print.append(entry) # Режимы вывода if args.orders: print_orders(orders_for_print, 'buy') print_orders(orders_for_print, 'sale', rec_prices) elif args.portfolio: print_portfolio(portfolio) print_transactions(transactions) else: # Полный режим — как раньше now = datetime.now() full_output = { 'date_report': now.strftime("%Y-%m-%d %H:%M:%S"), 'portfolio': portfolio, 'transactions': transactions, 'recommended_prices': rec_prices, 'buy_orders': [o for o in orders_for_print if o['direction'] == 'buy'], 'sell_orders': [o for o in orders_for_print if o['direction'] == 'sale'], } with open(OUTPUT_FILE, "w", encoding="utf-8") as f: json.dump(full_output, f, ensure_ascii=False, indent=2) print(f"✅ Данные сохранены в '{OUTPUT_FILE}'") print_portfolio(portfolio) print_transactions(transactions) print_orders(orders_for_print, 'buy') print_orders(orders_for_print, 'sale', rec_prices) print("\n✅ Готово!") if __name__ == '__main__': main()