|
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() |