| import os |
| import time |
| import base64 |
| import io |
| import threading |
| import atexit |
| import traceback |
| import asyncio |
| import concurrent.futures |
| from datetime import datetime |
| from typing import Optional, Dict, Any, Tuple |
|
|
| import gradio as gr |
| import anthropic |
| from playwright.sync_api import sync_playwright |
| from PIL import Image |
| from dotenv import load_dotenv |
|
|
| |
| load_dotenv() |
|
|
| |
| analyzed_data = {} |
| last_update_time = None |
| update_thread = None |
| is_updating = False |
|
|
| def get_claude_client(): |
| """Claude API ํด๋ผ์ด์ธํธ ์ด๊ธฐํ""" |
| api_key = os.getenv("ANTHROPIC_API_KEY") |
| if not api_key: |
| raise ValueError("ANTHROPIC_API_KEY ํ๊ฒฝ๋ณ์๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค.") |
| |
| return anthropic.Anthropic( |
| api_key=api_key, |
| max_retries=3, |
| timeout=60.0 |
| ) |
|
|
| def install_browsers(): |
| """Playwright ๋ธ๋ผ์ฐ์ ์ค์น""" |
| try: |
| print("Playwright ๋ธ๋ผ์ฐ์ ์ค์น ์ค...") |
| os.system("playwright install chromium --force") |
| print("๋ธ๋ผ์ฐ์ ์ค์น ์๋ฃ!") |
| return True |
| except Exception as e: |
| print(f"๋ธ๋ผ์ฐ์ ์ค์น ์คํจ: {e}") |
| return False |
|
|
| def create_browser_for_thread(): |
| """์ค๋ ๋๋ณ ๋
๋ฆฝ์ ์ธ ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค ์์ฑ""" |
| try: |
| print(f"[Thread {threading.current_thread().name}] ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค ์์ฑ ์ค...") |
| |
| playwright = sync_playwright().start() |
| |
| |
| browser_args = [ |
| '--no-sandbox', |
| '--disable-dev-shm-usage', |
| '--disable-gpu', |
| '--disable-extensions', |
| '--disable-web-security', |
| '--disable-blink-features=AutomationControlled', |
| '--disable-background-timer-throttling', |
| '--disable-backgrounding-occluded-windows', |
| '--disable-renderer-backgrounding', |
| '--disable-features=TranslateUI', |
| '--disable-ipc-flooding-protection' |
| ] |
| |
| browser = playwright.chromium.launch( |
| headless=True, |
| args=browser_args |
| ) |
| |
| print(f"[Thread {threading.current_thread().name}] ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค ์์ฑ ์๋ฃ") |
| return playwright, browser |
| |
| except Exception as e: |
| print(f"[Thread {threading.current_thread().name}] ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค ์์ฑ ์คํจ: {str(e)}") |
| print(f"์์ธ ์ค๋ฅ: {traceback.format_exc()}") |
| return None, None |
|
|
| def capture_google_trends() -> Optional[Image.Image]: |
| """Google Trends ์คํฌ๋ฆฐ์ท ์บก์ฒ (๋
๋ฆฝ ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค ์ฌ์ฉ)""" |
| playwright = None |
| browser = None |
| page = None |
| |
| try: |
| print("Google Trends ์คํฌ๋ฆฐ์ท ์์...") |
| |
| |
| playwright, browser = create_browser_for_thread() |
| if not browser: |
| print("๋ธ๋ผ์ฐ์ ์ด๊ธฐํ ์คํจ") |
| return None |
| |
| page = browser.new_page() |
| page.set_viewport_size({"width": 1400, "height": 900}) |
| page.set_extra_http_headers({ |
| "Accept-Language": "ko-KR,ko;q=0.9,en;q=0.8", |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", |
| "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" |
| }) |
| |
| url = "https://trends.google.co.kr/trending?geo=KR" |
| print(f"ํ์ด์ง ์ ์: {url}") |
| |
| |
| try: |
| page.goto(url, wait_until="networkidle", timeout=45000) |
| print("ํ์ด์ง ๋ก๋ฉ ์๋ฃ") |
| except: |
| |
| page.goto(url, wait_until="domcontentloaded", timeout=30000) |
| print("๊ธฐ๋ณธ ํ์ด์ง ๋ก๋ฉ ์๋ฃ") |
| |
| |
| time.sleep(5) |
| |
| |
| try: |
| accept_button = page.locator('button:has-text("๋ชจ๋ ์๋ฝ"), button:has-text("Accept all"), button:has-text("์๋ฝ")') |
| if accept_button.count() > 0: |
| accept_button.first.click(timeout=3000) |
| print("์ฟ ํค ํ์
์ฒ๋ฆฌ๋จ") |
| time.sleep(3) |
| except Exception as e: |
| print(f"์ฟ ํค ํ์
์ฒ๋ฆฌ ์คํจ (๋ฌด์): {e}") |
| |
| |
| try: |
| |
| page.wait_for_selector("main, .trending-content, .trends-wrapper, [data-topic]", timeout=15000) |
| print("ํธ๋ ๋ ์ปจํ
์ธ ๋ก๋ฉ ํ์ธ๋จ") |
| except: |
| print("ํธ๋ ๋ ์์๋ฅผ ์ฐพ์ ์ ์์, ๊ณ์ ์งํ") |
| |
| |
| time.sleep(5) |
| |
| |
| print("์คํฌ๋ฆฐ์ท ์บก์ฒ ์์...") |
| try: |
| main_content = page.locator('main, .main-content, [data-ve-type="main"], .trends-wrapper') |
| if main_content.count() > 0 and main_content.first.is_visible(): |
| screenshot_bytes = main_content.first.screenshot(timeout=15000) |
| print("๋ฉ์ธ ์ปจํ
์ธ ์คํฌ๋ฆฐ์ท ์บก์ฒ๋จ") |
| else: |
| screenshot_bytes = page.screenshot(timeout=20000) |
| print("์ ์ฒด ํ์ด์ง ์คํฌ๋ฆฐ์ท ์บก์ฒ๋จ") |
| except Exception as e: |
| print(f"์คํฌ๋ฆฐ์ท ์บก์ฒ ์ฌ์๋: {e}") |
| screenshot_bytes = page.screenshot(timeout=20000) |
| print("์ฌ์๋ ์คํฌ๋ฆฐ์ท ์บก์ฒ ์๋ฃ") |
| |
| screenshot = Image.open(io.BytesIO(screenshot_bytes)) |
| print("Google Trends ์คํฌ๋ฆฐ์ท ์๋ฃ") |
| return screenshot |
| |
| except Exception as e: |
| print(f"Google Trends ์คํฌ๋ฆฐ์ท ์คํจ: {str(e)}") |
| print(f"์์ธ ์ค๋ฅ: {traceback.format_exc()}") |
| return None |
| finally: |
| |
| if page: |
| try: |
| page.close() |
| except: |
| pass |
| if browser: |
| try: |
| browser.close() |
| except: |
| pass |
| if playwright: |
| try: |
| playwright.stop() |
| except: |
| pass |
|
|
| def capture_ezme_trends() -> Optional[Image.Image]: |
| """rank.ezme.net ์คํฌ๋ฆฐ์ท ์บก์ฒ (๋
๋ฆฝ ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค ์ฌ์ฉ)""" |
| playwright = None |
| browser = None |
| page = None |
| |
| try: |
| print("rank.ezme.net ์คํฌ๋ฆฐ์ท ์์...") |
| |
| |
| playwright, browser = create_browser_for_thread() |
| if not browser: |
| print("๋ธ๋ผ์ฐ์ ์ด๊ธฐํ ์คํจ") |
| return None |
| |
| page = browser.new_page() |
| page.set_viewport_size({"width": 1400, "height": 1200}) |
| page.set_extra_http_headers({ |
| "Accept-Language": "ko-KR,ko;q=0.9,en;q=0.8", |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", |
| "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" |
| }) |
| |
| url = "https://rank.ezme.net/diff/" |
| print(f"ํ์ด์ง ์ ์: {url}") |
| |
| |
| try: |
| page.goto(url, wait_until="networkidle", timeout=45000) |
| print("ํ์ด์ง ๋ก๋ฉ ์๋ฃ") |
| except: |
| |
| page.goto(url, wait_until="domcontentloaded", timeout=30000) |
| print("๊ธฐ๋ณธ ํ์ด์ง ๋ก๋ฉ ์๋ฃ") |
| |
| |
| print("๋์ ์ปจํ
์ธ ๋ก๋ฉ ๋๊ธฐ์ค...") |
| time.sleep(8) |
| |
| |
| try: |
| |
| page.wait_for_selector("table, .table, td", timeout=10000) |
| print("ํ
์ด๋ธ ๋ฐ์ดํฐ ๋ก๋ฉ ํ์ธ๋จ") |
| except: |
| print("ํ
์ด๋ธ ์์๋ฅผ ์ฐพ์ ์ ์์, ๊ณ์ ์งํ") |
| |
| |
| try: |
| page.evaluate("window.scrollTo(0, document.body.scrollHeight * 0.4)") |
| time.sleep(3) |
| print("์คํฌ๋กค ์๋ฃ") |
| except Exception as e: |
| print(f"์คํฌ๋กค ์คํจ: {e}") |
| |
| |
| print("์คํฌ๋ฆฐ์ท ์บก์ฒ ์์...") |
| screenshot_bytes = page.screenshot(full_page=True, timeout=20000) |
| screenshot = Image.open(io.BytesIO(screenshot_bytes)) |
| print("rank.ezme.net ์คํฌ๋ฆฐ์ท ์๋ฃ") |
| return screenshot |
| |
| except Exception as e: |
| print(f"rank.ezme.net ์คํฌ๋ฆฐ์ท ์คํจ: {str(e)}") |
| print(f"์์ธ ์ค๋ฅ: {traceback.format_exc()}") |
| return None |
| finally: |
| |
| if page: |
| try: |
| page.close() |
| except: |
| pass |
| if browser: |
| try: |
| browser.close() |
| except: |
| pass |
| if playwright: |
| try: |
| playwright.stop() |
| except: |
| pass |
|
|
| def analyze_google_trends_with_claude(image: Image.Image) -> str: |
| """Google Trends ์ด๋ฏธ์ง Claude ๋ถ์""" |
| if image is None: |
| return "Google Trends ๋ถ์ํ ์ด๋ฏธ์ง๊ฐ ์์ต๋๋ค." |
| |
| try: |
| client = get_claude_client() |
| |
| buffer = io.BytesIO() |
| image.save(buffer, format="PNG") |
| image_base64 = base64.b64encode(buffer.getvalue()).decode() |
| |
| prompt = """Google Trends ์ค์๊ฐ ์ธ๊ธฐ ๊ฒ์์ด ์คํฌ๋ฆฐ์ท์ ๋ถ์ํด์ฃผ์ธ์. |
| |
| ๋ค์ ํญ๋ชฉ๋ค์ ์ค์ฌ์ผ๋ก ํ๊ตญ์ด๋ก ๋ถ์ํด์ฃผ์ธ์: |
| |
| ### A. ์์ ๊ฒ์์ด ์์ (๊ฒ์๋ ๊ธฐ์ค) |
| ๊ฒ์๋์ด ๋์ ์์๋๋ก ์ ๋ ฌํ์ฌ ๋์ด: |
| **1์.** ๊ฒ์์ด๋ช
(๊ฒ์๋) |
| **2์.** ๊ฒ์์ด๋ช
(๊ฒ์๋) |
| **3์.** ๊ฒ์์ด๋ช
(๊ฒ์๋) |
| (์ด๋ฐ ์์ผ๋ก 10์๊น์ง) |
| |
| ### B. ์ฃผ์ ํธ๋ ๋ ํค์๋ |
| - ํนํ ์ฃผ๋ชฉํ ๋งํ ๊ฒ์์ด๋ค |
| - ๊ฒ์๋ ์ฆ๊ฐ ํจํด |
| |
| ### C. ์นดํ
๊ณ ๋ฆฌ๋ณ ํน์ง |
| - ์ํฐํ
์ธ๋จผํธ, ๋ด์ค, ์คํฌ์ธ ๋ฑ ๋ถ์ผ๋ณ ๋ํฅ |
| |
| ### D. ํน์ด์ฌํญ ๋ฐ ๋ถ์ |
| - ๊ธ์์น ํค์๋ |
| - ์๊ธฐ์ /์ฌํ์ ๋ฐฐ๊ฒฝ ์ถ์ธก |
| - ์ ์ฒด์ ์ธ ๊ด์ฌ์ฌ ํจํด |
| |
| **์ค์ ์ง์์ฌํญ:** |
| - ๋งํฌ๋ค์ด ํ์์ ์ฌ์ฉํ์ฌ **๊ตต์ ๊ธ์จ**, *๊ธฐ์ธ์*, `์ฝ๋` ๋ฑ์ผ๋ก ์ค์ํ ๋ด์ฉ์ ๊ฐ์กฐํด์ฃผ์ธ์ |
| - ๊ฒ์์ด๋ช
, ์์น, ์นดํ
๊ณ ๋ฆฌ๋ช
๋ฑ ํต์ฌ ์ ๋ณด๋ **๊ตต์ ๊ธ์จ**๋ก ํ์ |
| - ๊ธ์์น๋ฅ ์ด๋ ํน๋ณํ ์์น๋ `๋ฐฑํฑ`์ผ๋ก ๊ฐ์ธ์ ๊ฐ์กฐ |
| - ์์ ๊ฒ์์ด๋ ๋ฐ๋์ ๊ฒ์๋ ์์๋๋ก 1์๋ถํฐ 10์๊น์ง ์ ๋ ฌ""" |
|
|
| response = client.messages.create( |
| model="claude-3-5-sonnet-20241022", |
| max_tokens=1200, |
| messages=[ |
| { |
| "role": "user", |
| "content": [ |
| { |
| "type": "text", |
| "text": prompt |
| }, |
| { |
| "type": "image", |
| "source": { |
| "type": "base64", |
| "media_type": "image/png", |
| "data": image_base64 |
| } |
| } |
| ] |
| } |
| ] |
| ) |
| |
| return response.content[0].text |
| |
| except Exception as e: |
| return f"Google Trends Claude ๋ถ์ ์คํจ: {str(e)}" |
|
|
| def analyze_ezme_trends_with_claude(image: Image.Image) -> str: |
| """rank.ezme.net ์ด๋ฏธ์ง Claude ๋ถ์""" |
| if image is None: |
| return "rank.ezme.net ๋ถ์ํ ์ด๋ฏธ์ง๊ฐ ์์ต๋๋ค." |
| |
| try: |
| client = get_claude_client() |
| |
| buffer = io.BytesIO() |
| image.save(buffer, format="PNG") |
| image_base64 = base64.b64encode(buffer.getvalue()).decode() |
| |
| prompt = """ํ๊ตญ ํฌํธ์ฌ์ดํธ๋ณ ์๊ฐ๋ณ ์ค์๊ฐ ๊ฒ์์ด ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํด์ฃผ์ธ์. |
| |
| ๋ค์ ํญ๋ชฉ๋ค์ ์ค์ฌ์ผ๋ก ํ๊ตญ์ด๋ก ๋ถ์ํด์ฃผ์ธ์: |
| |
| ### A. ๋๋ฉ์ธ๋ณ ์ฃผ์ ๊ฒ์์ด |
| **Zum (์ค) ์ฃผ์ ๊ฒ์์ด:** |
| - ์์ ๊ฒ์์ด์ ์ง์
ํ์ |
| - ์ฃผ์ ๊ด์ฌ ๋ถ์ผ |
| |
| **Nate (๋ค์ดํธ) ์ฃผ์ ๊ฒ์์ด:** |
| - ์์ ๊ฒ์์ด์ ์ง์
ํ์ |
| - ์ฃผ์ ๊ด์ฌ ๋ถ์ผ |
| |
| **Google ํ๊ตญ ์ฃผ์ ๊ฒ์์ด:** |
| - ์์ ๊ฒ์์ด์ ์ง์
ํ์ |
| - ์ฃผ์ ๊ด์ฌ ๋ถ์ผ |
| |
| ### B. ์๊ฐ๋๋ณ ํธ๋ ๋ ๋ณํ |
| - ์ต๊ทผ ๋ช ์๊ฐ ๋์์ ๋ณํ ํจํด |
| - ์๊ฐ๋๋ณ ์ฃผ์ ์ด์ ๋ณํ |
| |
| ### C. ๊ฒ์์ด ์ง์
ํ์ ๋ถ์ |
| - ๊ฐ์ฅ ๋์ ์ง์
ํ์๋ฅผ ๊ธฐ๋กํ ๊ฒ์์ด๋ค |
| - ์ง์์ ์ผ๋ก ์์๊ถ์ ์ ์งํ๋ ๊ฒ์์ด๋ค |
| |
| ### D. ํฌํธ๋ณ ๊ด์ฌ์ฌ ์ฐจ์ด์ |
| - ๊ฐ ํฌํธ์ฌ์ดํธ๋ณ ํน์ฑ ๋ถ์ |
| - ์ฌ์ฉ์์ธต๋ณ ๊ด์ฌ์ฌ ์ฐจ์ด |
| |
| **์ค์ ์ง์์ฌํญ:** |
| - ๋งํฌ๋ค์ด ํ์์ ์ฌ์ฉํ์ฌ **๊ตต์ ๊ธ์จ**๋ก ๊ฒ์์ด๋ช
๊ฐ์กฐ |
| - ์ง์
ํ์๋ `๋ฐฑํฑ`์ผ๋ก ๊ฐ์ธ์ ํ์ (์: `์ง์
ํ์: 15ํ`) |
| - ์๊ฐ๋ณ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด ๊ตฌ์ฒด์ ์ผ๋ก ์ธ๊ธ |
| - ๊ฐ ํฌํธ์ ํน์ง์ ๋ช
ํํ ๊ตฌ๋ถํ์ฌ ๋ถ์""" |
|
|
| response = client.messages.create( |
| model="claude-3-5-sonnet-20241022", |
| max_tokens=1200, |
| messages=[ |
| { |
| "role": "user", |
| "content": [ |
| { |
| "type": "text", |
| "text": prompt |
| }, |
| { |
| "type": "image", |
| "source": { |
| "type": "base64", |
| "media_type": "image/png", |
| "data": image_base64 |
| } |
| } |
| ] |
| } |
| ] |
| ) |
| |
| return response.content[0].text |
| |
| except Exception as e: |
| return f"rank.ezme.net Claude ๋ถ์ ์คํจ: {str(e)}" |
|
|
| def combine_analysis_results(google_analysis: str, ezme_analysis: str) -> str: |
| """Google Trends์ ezme ๋ถ์ ๊ฒฐ๊ณผ ํตํฉ (์ค๋ฅ ์ํฉ ๋์)""" |
| timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
| |
| |
| google_failed = "๋ถ์ํ ์ด๋ฏธ์ง๊ฐ ์์ต๋๋ค" in google_analysis or "์บก์ฒ์ ์คํจ" in google_analysis |
| ezme_failed = "๋ถ์ํ ์ด๋ฏธ์ง๊ฐ ์์ต๋๋ค" in ezme_analysis or "์บก์ฒ์ ์คํจ" in ezme_analysis |
| |
| |
| if google_failed and ezme_failed: |
| status_msg = "**๋ชจ๋ ๋ฐ์ดํฐ ์์ค์ ์์ง์ ์คํจํ์ต๋๋ค.** ์ ์ ํ ๋ค์ ์๋ํด์ฃผ์ธ์." |
| elif google_failed: |
| status_msg = "**Google Trends ๋ฐ์ดํฐ ์์ง์ ์คํจํ์ต๋๋ค.** ํ๊ตญ ํฌํธ ๋ฐ์ดํฐ๋ง ์ ๊ณต๋ฉ๋๋ค." |
| elif ezme_failed: |
| status_msg = "**ํ๊ตญ ํฌํธ ๋ฐ์ดํฐ ์์ง์ ์คํจํ์ต๋๋ค.** Google Trends ๋ฐ์ดํฐ๋ง ์ ๊ณต๋ฉ๋๋ค." |
| else: |
| status_msg = "**๋ชจ๋ ๋ฐ์ดํฐ ์์ค๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ์์งํ์ต๋๋ค.**" |
| |
| combined_result = f"""# ์ค์๊ฐ ๊ฒ์์ด ํตํฉ ๋ถ์ |
| |
| **์ต์ข
์
๋ฐ์ดํธ:** {timestamp} |
| |
| {status_msg} |
| |
| --- |
| |
| ## Google Trends ๊ธ๋ก๋ฒ ๋ํฅ |
| |
| {google_analysis} |
| |
| --- |
| |
| ## ํ๊ตญ ํฌํธ ์ค์๊ฐ ๊ฒ์์ด |
| |
| {ezme_analysis} |
| """ |
| |
| return combined_result |
|
|
| def perform_parallel_analysis() -> Tuple[bool, str]: |
| """๋ณ๋ ฌ ์ฒ๋ฆฌ๋ก ํตํฉ ๋ถ์ ์ํ (๊ฐ์ ๋ ์ค๋ฅ ์ฒ๋ฆฌ)""" |
| global analyzed_data, last_update_time, is_updating |
| |
| is_updating = True |
| print(f"\n{'='*60}") |
| print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] ํตํฉ ์ค์๊ฐ ํธ๋ ๋ ๋ถ์ ์์") |
| print(f"{'='*60}") |
| |
| try: |
| |
| print("1. ๋ธ๋ผ์ฐ์ ์ค์น ํ์ธ...") |
| install_browsers() |
| |
| |
| print("2. ๋ณ๋ ฌ ์คํฌ๋ฆฐ์ท ์บก์ฒ ์์...") |
| |
| with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor: |
| print(" - Google Trends ์คํฌ๋ฆฐ์ท ์์
์์") |
| google_future = executor.submit(capture_google_trends) |
| |
| print(" - rank.ezme.net ์คํฌ๋ฆฐ์ท ์์
์์") |
| ezme_future = executor.submit(capture_ezme_trends) |
| |
| print(" - ๋ ์์
์๋ฃ ๋๊ธฐ ์ค...") |
| google_screenshot = google_future.result() |
| ezme_screenshot = ezme_future.result() |
| |
| |
| print("3. ์คํฌ๋ฆฐ์ท ๊ฒฐ๊ณผ ํ์ธ...") |
| google_success = google_screenshot is not None |
| ezme_success = ezme_screenshot is not None |
| |
| print(f" - Google Trends ์คํฌ๋ฆฐ์ท: {'์ฑ๊ณต' if google_success else '์คํจ'}") |
| print(f" - rank.ezme.net ์คํฌ๋ฆฐ์ท: {'์ฑ๊ณต' if ezme_success else '์คํจ'}") |
| |
| if not google_success and not ezme_success: |
| error_msg = "๋ชจ๋ ์คํฌ๋ฆฐ์ท ์บก์ฒ์ ์คํจํ์ต๋๋ค." |
| print(f" ERROR: {error_msg}") |
| return False, error_msg |
| |
| print("4. Claude ๋ถ์ ์์...") |
| |
| |
| if google_success: |
| print(" - Google Trends ๋ถ์ ์ค...") |
| google_analysis = analyze_google_trends_with_claude(google_screenshot) |
| print(" - Google Trends ๋ถ์ ์๋ฃ") |
| else: |
| google_analysis = "Google Trends ์คํฌ๋ฆฐ์ท ์บก์ฒ์ ์คํจํ์ฌ ๋ถ์ํ ์ ์์ต๋๋ค." |
| |
| if ezme_success: |
| print(" - rank.ezme.net ๋ถ์ ์ค...") |
| ezme_analysis = analyze_ezme_trends_with_claude(ezme_screenshot) |
| print(" - rank.ezme.net ๋ถ์ ์๋ฃ") |
| else: |
| ezme_analysis = "rank.ezme.net ์คํฌ๋ฆฐ์ท ์บก์ฒ์ ์คํจํ์ฌ ๋ถ์ํ ์ ์์ต๋๋ค." |
| |
| print("5. ํตํฉ ๋ถ์ ๊ฒฐ๊ณผ ์์ฑ...") |
| |
| |
| combined_analysis = combine_analysis_results(google_analysis, ezme_analysis) |
| |
| |
| analyzed_data["ํตํฉ์ค์๊ฐ"] = { |
| 'analysis': combined_analysis, |
| 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), |
| 'google_analysis': google_analysis, |
| 'ezme_analysis': ezme_analysis, |
| 'google_success': google_success, |
| 'ezme_success': ezme_success |
| } |
| |
| last_update_time = datetime.now() |
| |
| success_count = sum([google_success, ezme_success]) |
| print(f"ํตํฉ ๋ถ์ ์๋ฃ - {last_update_time.strftime('%Y-%m-%d %H:%M:%S')}") |
| print(f"์ฑ๊ณต๋ฅ : {success_count}/2 ์์ค") |
| |
| return True, combined_analysis |
| |
| except Exception as e: |
| error_msg = f"ํตํฉ ๋ถ์ ์ค ์์์น ๋ชปํ ์ค๋ฅ: {str(e)}" |
| print(f"ERROR: {error_msg}") |
| print(f"์์ธ ์ค๋ฅ: {traceback.format_exc()}") |
| return False, error_msg |
| finally: |
| is_updating = False |
| print(f"{'='*60}\n") |
|
|
| def auto_update_scheduler(): |
| """1์๊ฐ๋ง๋ค ์๋ ์
๋ฐ์ดํธ""" |
| while True: |
| try: |
| print(f"[์ค์ผ์ค๋ฌ] ๋ค์ ์
๋ฐ์ดํธ๊น์ง 1์๊ฐ ๋๊ธฐ ์ค...") |
| time.sleep(3600) |
| |
| if not is_updating: |
| print(f"[์ค์ผ์ค๋ฌ] ์๋ ์
๋ฐ์ดํธ ์์") |
| perform_parallel_analysis() |
| else: |
| print(f"[์ค์ผ์ค๋ฌ] ์ด๋ฏธ ์
๋ฐ์ดํธ ์ค์ด๋ฏ๋ก ๊ฑด๋๋") |
| |
| except Exception as e: |
| print(f"[์ค์ผ์ค๋ฌ] ์ค๋ฅ: {e}") |
| time.sleep(300) |
|
|
| def get_cached_analysis_result() -> tuple: |
| """์ ์ฅ๋ ํตํฉ ๋ถ์ ๊ฒฐ๊ณผ ๋ฐํ""" |
| global analyzed_data, last_update_time |
| |
| if "ํตํฉ์ค์๊ฐ" in analyzed_data: |
| data = analyzed_data["ํตํฉ์ค์๊ฐ"] |
| status = f"์ต์ ๋ฐ์ดํฐ (์
๋ฐ์ดํธ: {data['timestamp']})" |
| return data['analysis'], status |
| else: |
| if last_update_time: |
| status = f"๋ฐ์ดํฐ ์์ (๋ง์ง๋ง ์
๋ฐ์ดํธ: {last_update_time.strftime('%H:%M:%S')})" |
| else: |
| status = "์์ง ๋ถ์ ๋ฐ์ดํฐ๊ฐ ์ค๋น๋์ง ์์์ต๋๋ค." |
| |
| return "**๋ถ์ ๋ฐ์ดํฐ๊ฐ ์์ง ์ค๋น๋์ง ์์์ต๋๋ค.**\n\n์ ์ ํ ๋ค์ ์๋ํด์ฃผ์ธ์.", status |
|
|
| def create_interface(): |
| """Gradio ์ธํฐํ์ด์ค ์์ฑ""" |
| |
| def show_cached_analysis(): |
| """์ ์ฅ๋ ํตํฉ ๋ถ์ ๊ฒฐ๊ณผ ํ์""" |
| if "ํตํฉ์ค์๊ฐ" in analyzed_data: |
| data = analyzed_data["ํตํฉ์ค์๊ฐ"] |
| status = f"์บ์๋ ํตํฉ ๋ถ์ ๊ฒฐ๊ณผ (์
๋ฐ์ดํธ: {data['timestamp']})" |
| return data['analysis'], status |
| else: |
| if last_update_time: |
| status = f"์ ์ฅ๋ ๋ฐ์ดํฐ ์์ (๋ง์ง๋ง ์
๋ฐ์ดํธ: {last_update_time.strftime('%H:%M:%S')})" |
| else: |
| status = "์์ง ๋ถ์ ๋ฐ์ดํฐ๊ฐ ์ค๋น๋์ง ์์์ต๋๋ค. ์ ์ ํ ๋ค์ ์๋ํด์ฃผ์ธ์." |
| |
| return "**์ ์ฅ๋ ํตํฉ ๋ถ์ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.**\n\n'์ค์๊ฐ ์
๋ฐ์ดํธ' ๋ฒํผ์ ๋๋ฌ์ฃผ์ธ์.", status |
| |
| def perform_live_update(): |
| """์ค์๊ฐ ์
๋ฐ์ดํธ ์ํ""" |
| if is_updating: |
| return "**ํ์ฌ ์
๋ฐ์ดํธ๊ฐ ์งํ ์ค์
๋๋ค.**\n\n์ ์ ํ ๋ค์ ์๋ํด์ฃผ์ธ์.", "์
๋ฐ์ดํธ ์งํ ์ค..." |
| |
| print("์ค์๊ฐ ํตํฉ ์
๋ฐ์ดํธ ์์ฒญ๋จ") |
| success, result = perform_parallel_analysis() |
| |
| if success and "ํตํฉ์ค์๊ฐ" in analyzed_data: |
| data = analyzed_data["ํตํฉ์ค์๊ฐ"] |
| return data['analysis'], f"์ค์๊ฐ ์
๋ฐ์ดํธ ์๋ฃ - {datetime.now().strftime('%H:%M:%S')}" |
| else: |
| return "**์
๋ฐ์ดํธ์ ์คํจํ์ต๋๋ค.**\n\n์ ์ ํ ๋ค์ ์๋ํด์ฃผ์ธ์.", f"์
๋ฐ์ดํธ ์คํจ - {datetime.now().strftime('%H:%M:%S')}" |
| |
| |
| with gr.Blocks( |
| title="ํตํฉ ์ค์๊ฐ ๊ฒ์์ด ๋ถ์", |
| theme=gr.themes.Soft(), |
| css=""" |
| @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap'); |
| |
| * { |
| font-family: 'Noto Sans KR', sans-serif !important; |
| } |
| |
| .markdown-content { |
| font-size: 14px; |
| line-height: 1.6; |
| background: #f8f9fa; |
| padding: 20px; |
| border-radius: 8px; |
| border: 1px solid #e9ecef; |
| } |
| |
| .markdown-content h1 { |
| color: #1d4ed8; |
| margin-bottom: 15px; |
| } |
| |
| .markdown-content h2 { |
| color: #2563eb; |
| margin-top: 25px; |
| margin-bottom: 12px; |
| } |
| |
| .markdown-content h3 { |
| color: #3b82f6; |
| margin-top: 20px; |
| margin-bottom: 10px; |
| } |
| |
| .markdown-content strong { |
| color: #1d4ed8; |
| font-weight: 600; |
| } |
| """ |
| ) as interface: |
| |
| gr.Markdown("# ํตํฉ ์ค์๊ฐ ๊ฒ์์ด ๋ถ์") |
| gr.Markdown("Google Trends์ ํ๊ตญ ํฌํธ์ฌ์ดํธ ๊ฒ์์ด๋ฅผ ํตํฉ ๋ถ์ํฉ๋๋ค.") |
| |
| with gr.Row(): |
| with gr.Column(scale=1): |
| gr.Markdown("### ๋ถ์ ์ต์
") |
| |
| with gr.Column(): |
| cached_btn = gr.Button("ํตํฉ ๊ฒ์์ด ๋ถ์", variant="primary", size="lg") |
| gr.Markdown("์ ์ฅ๋ ํตํฉ ๋ถ์ ๊ฒฐ๊ณผ๋ฅผ ์ฆ์ ํ์ํฉ๋๋ค.") |
| |
| gr.Markdown("---") |
| |
| with gr.Column(): |
| live_btn = gr.Button("์ค์๊ฐ ์
๋ฐ์ดํธ", variant="secondary", size="lg") |
| gr.Markdown("Google Trends + ํ๊ตญ ํฌํธ ์ต์ ๋ฐ์ดํฐ๋ก ์
๋ฐ์ดํธํฉ๋๋ค. (์ฝ 1๋ถ ์์)") |
| |
| gr.Markdown("---") |
| |
| status_output = gr.Textbox( |
| label="์ํ", |
| value="๋ถ์ํ ์ต์
์ ์ ํํด์ฃผ์ธ์.", |
| interactive=False |
| ) |
| |
| |
| try: |
| gr.Image( |
| value="apeach.png", |
| label=None, |
| show_label=False, |
| interactive=False, |
| height=200, |
| width=200 |
| ) |
| except: |
| pass |
| |
| with gr.Column(scale=2): |
| analysis_output = gr.Markdown( |
| value="**๋ถ์ ๋ฒํผ์ ๋๋ฌ์ฃผ์ธ์.**\n\n- **ํตํฉ ๊ฒ์์ด ๋ถ์**: ์ ์ฅ๋ ๊ฒฐ๊ณผ ์ฆ์ ํ์ธ\n- **์ค์๊ฐ ์
๋ฐ์ดํธ**: ์ต์ ๋ฐ์ดํฐ๋ก ์๋ก ๋ถ์", |
| elem_classes=["markdown-content"], |
| height=700 |
| ) |
| |
| |
| cached_btn.click( |
| fn=show_cached_analysis, |
| outputs=[analysis_output, status_output] |
| ) |
| |
| live_btn.click( |
| fn=perform_live_update, |
| outputs=[analysis_output, status_output] |
| ) |
| |
| return interface |
|
|
| def main(): |
| """๋ฉ์ธ ํจ์""" |
| print("=" * 60) |
| print("ํตํฉ ์ค์๊ฐ ๊ฒ์์ด ๋ถ์ ์๋น์ค ์์!") |
| print("=" * 60) |
| |
| |
| api_key = os.getenv("ANTHROPIC_API_KEY") |
| if not api_key: |
| print("ANTHROPIC_API_KEY ํ๊ฒฝ๋ณ์๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค.") |
| print(" .env ํ์ผ์ ANTHROPIC_API_KEY=your_key_here ๋ฅผ ์ถ๊ฐํ์ธ์.") |
| return |
| else: |
| print("Claude API ํค ํ์ธ๋จ") |
| |
| |
| print("์ด๊ธฐ ํตํฉ ๋ถ์ ์์...") |
| initial_success, _ = perform_parallel_analysis() |
| |
| if initial_success: |
| print("์ด๊ธฐ ํตํฉ ๋ถ์ ์๋ฃ") |
| else: |
| print("์ด๊ธฐ ๋ถ์ ์คํจ, ์๋น์ค๋ ๊ณ์ ์งํ๋ฉ๋๋ค.") |
| |
| |
| print("์๋ ์
๋ฐ์ดํธ ์ค์ผ์ค๋ฌ ์์ (1์๊ฐ ๊ฐ๊ฒฉ)") |
| global update_thread |
| update_thread = threading.Thread(target=auto_update_scheduler, daemon=True) |
| update_thread.start() |
| |
| print("๋ชจ๋ ์ค๋น ์๋ฃ!") |
| print("=" * 60) |
| |
| |
| try: |
| interface = create_interface() |
| interface.launch( |
| server_name="0.0.0.0", |
| server_port=7860, |
| share=False, |
| show_error=True, |
| quiet=False |
| ) |
| except Exception as e: |
| print(f"์ธํฐํ์ด์ค ์์ ์คํจ: {e}") |
| print(f"์์ธ ์ค๋ฅ: {traceback.format_exc()}") |
|
|
| if __name__ == "__main__": |
| main() |