hanjunjung commited on
Commit
a4bb82f
ยท
1 Parent(s): c23d25c

[fix] chrome browser

Browse files
Files changed (2) hide show
  1. app.py +1064 -165
  2. packages.txt +5 -3
app.py CHANGED
@@ -3,6 +3,8 @@ import time
3
  import base64
4
  import json
5
  import traceback
 
 
6
  from typing import Tuple, Optional
7
  import gradio as gr
8
  import anthropic
@@ -19,6 +21,50 @@ import threading
19
  IS_HUGGINGFACE = os.getenv("SPACE_ID") is not None
20
  print(f'IS_HUGGINGFACE = {IS_HUGGINGFACE}')
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  # Claude API ์„ค์ • (Hugging Face Secrets์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ)
23
  ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY")
24
 
@@ -48,34 +94,44 @@ class GoogleTrendsAutomator:
48
 
49
  # Hugging Face Space ํ™˜๊ฒฝ ์„ค์ •
50
  if IS_HUGGINGFACE:
51
- print("Hugging Face Space ํ™˜๊ฒฝ, ํ—ค๋“œ๋ฆฌ์Šค ๋ชจ๋“œ๋กœ ์‹คํ–‰")
52
- options.add_argument("--headless=new") # ์ƒˆ๋กœ์šด ํ—ค๋“œ๋ฆฌ์Šค ๋ชจ๋“œ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  options.add_argument("--no-sandbox")
54
  options.add_argument("--disable-dev-shm-usage")
55
  options.add_argument("--disable-gpu")
56
  options.add_argument("--disable-software-rasterizer")
57
  options.add_argument("--remote-debugging-port=9222")
 
 
 
 
58
  else:
59
  options.add_argument("--headless")
60
 
61
- # # ํ™˜๊ฒฝ๋ณ„ ์„ค์ •
62
- # if os.getenv("SPACE_ID"): # Hugging Face Space
63
- # options.add_argument("--headless")
64
- # options.add_argument("--no-sandbox")
65
- # options.add_argument("--disable-dev-shm-usage")
66
- # options.add_argument("--disable-gpu")
67
- # else: # ๋กœ์ปฌ ํ™˜๊ฒฝ
68
- # options.add_argument("--headless")
69
-
70
  # ์†๋„ ์ตœ์ ํ™” ์„ค์ •
71
- options.add_argument("--window-size=1280,720") # ํ•ด์ƒ๋„ ์ค„์ž„
72
  options.add_argument("--disable-blink-features=AutomationControlled")
73
  options.add_argument("--disable-extensions")
74
  options.add_argument("--disable-web-security")
75
  options.add_argument("--allow-running-insecure-content")
76
- options.add_argument("--disable-background-timer-throttling")
77
- options.add_argument("--disable-backgrounding-occluded-windows")
78
- options.add_argument("--disable-renderer-backgrounding")
79
  options.add_argument("--disable-features=TranslateUI")
80
  options.add_argument("--disable-ipc-flooding-protection")
81
 
@@ -85,18 +141,17 @@ class GoogleTrendsAutomator:
85
 
86
  # ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”
87
  options.add_argument("--memory-pressure-off")
88
- # options.add_argument("--max_old_space_size=4096")
89
- options.add_argument("--max_old_space_size=2048")# ๋ฉ”๋ชจ๋ฆฌ ์ œํ•œ
90
 
91
  # ์ด๋ฏธ์ง€/CSS ๋น„ํ™œ์„ฑํ™”๋กœ ๋กœ๋”ฉ ์†๋„ ํ–ฅ์ƒ
92
  prefs = {
93
- "profile.managed_default_content_settings.images": 2, # ์ด๋ฏธ์ง€ ์ฐจ๋‹จ
94
- "profile.default_content_setting_values.notifications": 2, # ์•Œ๋ฆผ ์ฐจ๋‹จ
95
  }
96
  options.add_experimental_option("prefs", prefs)
97
 
98
  # ๋ด‡ ๊ฐ์ง€ ํšŒํ”ผ
99
- options.add_argument("--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")
100
  options.add_experimental_option("excludeSwitches", ["enable-automation"])
101
  options.add_experimental_option('useAutomationExtension', False)
102
 
@@ -105,9 +160,10 @@ class GoogleTrendsAutomator:
105
  options.add_argument("--accept-lang=ko-KR,ko;q=0.9,en;q=0.8")
106
 
107
  try:
108
- # Hugging Face ํ™˜๊ฒฝ์—์„œ๋Š” webdriver-manager ์šฐ์„  ์‚ฌ์šฉ
109
  if IS_HUGGINGFACE:
110
  try:
 
111
  from webdriver_manager.chrome import ChromeDriverManager
112
  from selenium.webdriver.chrome.service import Service
113
 
@@ -117,7 +173,7 @@ class GoogleTrendsAutomator:
117
  print("ChromeDriver ์„ค์น˜ ๋ฐ ์ดˆ๊ธฐํ™” ์„ฑ๊ณต")
118
 
119
  except Exception as e:
120
- print(f"webdriver-manager ์„ค์น˜ ์‹คํŒจ: {e}")
121
  # ์‹œ์Šคํ…œ ChromeDriver ์‹œ๋„
122
  try:
123
  self.driver = webdriver.Chrome(options=options)
@@ -137,38 +193,10 @@ class GoogleTrendsAutomator:
137
  except:
138
  self.driver = webdriver.Chrome(options=options)
139
  print("๋กœ์ปฌ ์‹œ์Šคํ…œ ChromeDriver ์‚ฌ์šฉ")
140
- # # ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•์œผ๋กœ ChromeDriver ์‹œ๋„
141
- # driver_created = False
142
 
143
- # # ๋ฐฉ๋ฒ• 1: webdriver-manager ์‚ฌ์šฉ
144
- # try:
145
- # from webdriver_manager.chrome import ChromeDriverManager
146
- # from selenium.webdriver.chrome.service import Service
147
-
148
- # service = Service(ChromeDriverManager().install())
149
- # self.driver = webdriver.Chrome(service=service, options=options)
150
- # print("webdriver-manager๋กœ ChromeDriver ์ž๋™ ์„ค์น˜ ์„ฑ๊ณต")
151
- # driver_created = True
152
-
153
- # except Exception as wm_error:
154
- # print(f"webdriver-manager ์‹คํŒจ: {wm_error}")
155
-
156
- # # ๋ฐฉ๋ฒ• 2: ์‹œ์Šคํ…œ ChromeDriver ์‚ฌ์šฉ
157
- # if not driver_created:
158
- # try:
159
- # self.driver = webdriver.Chrome(options=options)
160
- # print("์‹œ์Šคํ…œ ChromeDriver ์‚ฌ์šฉ ์„ฑ๊ณต")
161
- # driver_created = True
162
- # except Exception as sys_error:
163
- # print(f"์‹œ์Šคํ…œ ChromeDriver ์‹คํŒจ: {sys_error}")
164
-
165
- # if not driver_created:
166
- # print("๋ชจ๋“  ChromeDriver ์ดˆ๊ธฐํ™” ๋ฐฉ๋ฒ• ์‹คํŒจ")
167
- # return False
168
-
169
- # ํƒ€์ž„์•„์›ƒ ์„ค์ • (์†๋„ ์ตœ์ ํ™”)
170
- self.driver.set_page_load_timeout(15) # ํŽ˜์ด์ง€ ๋กœ๋”ฉ ํƒ€์ž„์•„์›ƒ 15์ดˆ
171
- self.driver.implicitly_wait(2) # ์š”์†Œ ๋Œ€๊ธฐ ์‹œ๊ฐ„ 2์ดˆ๋กœ ๋‹จ์ถ•
172
 
173
  # ๋ด‡ ๊ฐ์ง€ ๋ฐฉ์ง€ ์Šคํฌ๋ฆฝํŠธ
174
  try:
@@ -240,7 +268,7 @@ class GoogleTrendsAutomator:
240
  if progress_callback:
241
  progress_callback("์ง€์—ญ ์„ค์ • ๋ณ€๊ฒฝ ์ค‘...")
242
  results['region'] = self.change_region(region)
243
- time.sleep(0.5) # ์ตœ์†Œ ๋Œ€๊ธฐ
244
  else:
245
  results['region'] = True
246
 
@@ -248,7 +276,7 @@ class GoogleTrendsAutomator:
248
  if progress_callback:
249
  progress_callback("๊ธฐ๊ฐ„ ์„ค์ • ๋ณ€๊ฒฝ ์ค‘...")
250
  results['period'] = self.change_time_period(period)
251
- time.sleep(0.5) # ์ตœ์†Œ ๋Œ€๊ธฐ
252
  else:
253
  results['period'] = True
254
 
@@ -256,7 +284,7 @@ class GoogleTrendsAutomator:
256
  if progress_callback:
257
  progress_callback("์นดํ…Œ๊ณ ๋ฆฌ ์„ค์ • ๋ณ€๊ฒฝ ์ค‘...")
258
  results['category'] = self.change_category(category)
259
- time.sleep(0.5) # ์ตœ์†Œ ๋Œ€๊ธฐ
260
  else:
261
  results['category'] = True
262
 
@@ -277,7 +305,7 @@ class GoogleTrendsAutomator:
277
  if not self.safe_click(region_selectors):
278
  return False
279
 
280
- time.sleep(1) # ๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋‹จ์ถ•
281
 
282
  region_mapping = {
283
  "์ „์„ธ๊ณ„": ["์ „ ์„ธ๊ณ„", "Worldwide"],
@@ -313,7 +341,7 @@ class GoogleTrendsAutomator:
313
  if not self.safe_click(period_selectors):
314
  return False
315
 
316
- time.sleep(1) # ๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋‹จ์ถ•
317
 
318
  period_mapping = {
319
  "์ง€๋‚œ 1์‹œ๊ฐ„": ["์ง€๋‚œ 1์‹œ๊ฐ„"],
@@ -349,7 +377,7 @@ class GoogleTrendsAutomator:
349
  if not self.safe_click(category_selectors):
350
  return False
351
 
352
- time.sleep(1) # ๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋‹จ์ถ•
353
 
354
  category_mapping = {
355
  "๊ฒŒ์ž„": ["๊ฒŒ์ž„"],
@@ -395,19 +423,20 @@ class GoogleTrendsAutomator:
395
  # Google Trends ์ ‘์† (ํ•œ๊ตญ์–ด ์ง์ ‘ URL)
396
  self.driver.get("https://trends.google.com/trending?geo=KR&hl=ko")
397
 
398
- # ์ตœ์†Œํ•œ์˜ ๋กœ๋”ฉ ๋Œ€๊ธฐ (1์ดˆ๋กœ ๋‹จ์ถ•)
399
- time.sleep(1)
 
400
 
401
  if progress_callback:
402
  progress_callback("ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์™„๋ฃŒ, ์„ค์ • ๋ณ€๊ฒฝ ์‹œ์ž‘...")
403
 
404
  # ํŽ˜์ด์ง€ ์ค€๋น„ ํ™•์ธ (๋น ๋ฅธ ์ฒดํฌ)
405
  try:
406
- WebDriverWait(self.driver, 5).until(
407
  EC.presence_of_element_located((By.TAG_NAME, "button"))
408
  )
409
  except TimeoutException:
410
- pass # ๊ณ„์† ์ง„ํ–‰
411
 
412
  # ์„ค์ • ๋ณ€๊ฒฝ (๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ)
413
  settings_result = self.parallel_change_settings(region, period, category, progress_callback)
@@ -427,10 +456,10 @@ class GoogleTrendsAutomator:
427
  if progress_callback:
428
  progress_callback("์„ค์ • ๋ณ€๊ฒฝ ์™„๋ฃŒ, ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๋Š”์ค‘...")
429
 
430
- # ์ตœ์ข… ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ๋Œ€๊ธฐ (1์ดˆ๋กœ ๋‹จ์ถ•)
431
- time.sleep(1)
432
 
433
- # ์Šคํฌ๋ฆฐ์ƒท ์ดฌ์˜ (ํ•„์š”ํ•œ ์˜์—ญ๋งŒ)
434
  screenshot = self.driver.get_screenshot_as_png()
435
  screenshot_b64 = base64.b64encode(screenshot).decode()
436
 
@@ -444,7 +473,10 @@ class GoogleTrendsAutomator:
444
 
445
  finally:
446
  if self.driver:
447
- self.driver.quit()
 
 
 
448
 
449
  # Claude API๋กœ ์Šคํฌ๋ฆฐ์ƒท ๋ถ„์„
450
  def analyze_with_claude(screenshot_b64: str, region: str, period: str, category: str) -> str:
@@ -455,6 +487,7 @@ def analyze_with_claude(screenshot_b64: str, region: str, period: str, category:
455
  <div style="color: red; padding: 20px; border: 1px solid red; border-radius: 5px;">
456
  <h3>API ํ‚ค ์˜ค๋ฅ˜</h3>
457
  <p><strong>ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!</strong></p>
 
458
  </div>
459
  """
460
 
@@ -486,7 +519,7 @@ HTML ํ˜•ํƒœ๋กœ ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌํ•˜๊ณ , ์ด๋ชจ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์‹œ
486
  # Claude API ํ˜ธ์ถœ (๋น ๋ฅธ ์„ค์ •)
487
  message = claude_client.messages.create(
488
  model="claude-3-5-sonnet-20241022",
489
- max_tokens=1000, # ํ† ํฐ ์ˆ˜ ์ค„์—ฌ์„œ ์†๋„ ํ–ฅ์ƒ
490
  messages=[
491
  {
492
  "role": "user",
@@ -519,7 +552,7 @@ HTML ํ˜•ํƒœ๋กœ ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌํ•˜๊ณ , ์ด๋ชจ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์‹œ
519
  """
520
 
521
  def main_analysis(region: str, period: str, category: str):
522
- """์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ถ„์„ ํ•จ์ˆ˜ (Generator)"""
523
 
524
  start_time = time.time()
525
 
@@ -529,6 +562,7 @@ def main_analysis(region: str, period: str, category: str):
529
  <h3>Google Trends ๋ถ„์„ ์‹œ์ž‘!</h3>
530
  <p><strong>์„ค์ •:</strong> {region} | {period} | {category}</p>
531
  <p>์‹ค์‹œ๊ฐ„์œผ๋กœ ์ง„ํ–‰ ์ƒํ™ฉ์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค...</p>
 
532
  </div>
533
  """
534
 
@@ -542,9 +576,9 @@ def main_analysis(region: str, period: str, category: str):
542
  if not ANTHROPIC_API_KEY or ANTHROPIC_API_KEY == "your_api_key_here":
543
  yield """
544
  <div style="color: red; padding: 20px; border: 1px solid red; border-radius: 5px;">
545
- <h3> API ํ‚ค ๋ˆ„๋ฝ</h3>
546
  <p><strong>ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!</strong></p>
547
- <p><strong>ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:</strong> .env ํŒŒ์ผ์— API ํ‚ค๋ฅผ ์„ค์ •ํ•˜๊ณ  ์•ฑ์„ ์žฌ์‹œ์ž‘ํ•˜์„ธ์š”.</p>
548
  </div>
549
  """
550
  return
@@ -552,37 +586,20 @@ def main_analysis(region: str, period: str, category: str):
552
  # 3๋‹จ๊ณ„: ๋ธŒ๋ผ์šฐ์ € ์‹คํ–‰ ์•Œ๋ฆผ
553
  yield f"""
554
  <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
555
- <h4> 1๋‹จ๊ณ„: ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค! ์ž…๋ ฅํ•˜์‹  ์กฐ๊ฑด์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค...</h4>
556
  <p>Connect Start...</p>
557
  </div>
558
 
559
  <div style="padding: 15px; background-color: #f8f9fa; border-radius: 5px; margin-bottom: 15px;">
560
- <p> <strong>์ง„ํ–‰ ์ค‘:</strong> ๋ธŒ๋ผ์šฐ์ € ๋“œ๋ผ์ด๋ฒ„ ์„ค์ • ๋ฐ ํŽ˜์ด์ง€ ๋กœ๋”ฉ...</p>
 
561
  </div>
562
  """
563
 
564
  # 4๋‹จ๊ณ„: Google Trends ์ž๋™ํ™” ์‹คํ–‰
565
- print(f" ๋ถ„์„ ์‹œ์ž‘: {region} | {period} | {category}")
566
  automator = GoogleTrendsAutomator()
567
 
568
- # 5๋‹จ๊ณ„: ์„ค์ • ๋ณ€๊ฒฝ ์•Œ๋ฆผ
569
- if region != "๋Œ€ํ•œ๋ฏผ๊ตญ" or period != "์ง€๋‚œ 24์‹œ๊ฐ„" or category != "๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ":
570
- yield f"""
571
- <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
572
- <h4>์ž๋™ํ™” ์‹œ์ž‘</h4>
573
- <p>In Running...</p>
574
- </div>
575
-
576
- <div style="padding: 15px; background-color: #e3f2fd; border-radius: 5px; margin-bottom: 15px;">
577
- <p><strong>์„ค์ • ๋ณ€๊ฒฝ ์ค‘:</strong></p>
578
- <ul style="margin: 10px 0;">
579
- {"<li>์ง€์—ญ: " + region + "</li>" if region != "๋Œ€ํ•œ๋ฏผ๊ตญ" else ""}
580
- {"<li>๊ธฐ๊ฐ„: " + period + "</li>" if period != "์ง€๋‚œ 24์‹œ๊ฐ„" else ""}
581
- {"<li>์นดํ…Œ๊ณ ๋ฆฌ: " + category + "</li>" if category != "๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ" else ""}
582
- </ul>
583
- </div>
584
- """
585
-
586
  # ๋ธŒ๋ผ์šฐ์ € ์ž‘์—…์„ ๋ณ„๋„ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰
587
  with ThreadPoolExecutor(max_workers=1) as executor:
588
  future = executor.submit(
@@ -597,12 +614,12 @@ def main_analysis(region: str, period: str, category: str):
597
  elapsed = time.time() - start_time
598
  yield f"""
599
  <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
600
- <h4>์ž๋™ํ™” ์‹œ์ž‘</h4>
601
- <p>In Running...</p>
602
  </div>
603
 
604
  <div style="padding: 15px; background-color: #f8f9fa; border-radius: 5px; margin-bottom: 15px;">
605
- <p><strong>์ง„ํ–‰ ์ค‘:</strong> {current_progress}</p>
606
  <p><strong>๊ฒฝ๊ณผ ์‹œ๊ฐ„:</strong> {elapsed:.1f}์ดˆ</p>
607
  </div>
608
  """
@@ -620,16 +637,21 @@ def main_analysis(region: str, period: str, category: str):
620
  <pre style="background-color: #fff5f5; padding: 15px; border-radius: 5px; margin: 15px 0;">
621
  {status_msg}
622
  </pre>
623
- <p><strong>ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:</strong> Chrome ๋ธŒ๋ผ์šฐ์ €์™€ ChromeDriver๋ฅผ ์„ค์น˜ํ•˜๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•˜์„ธ์š”.</p>
 
 
 
 
 
624
  </div>
625
  """
626
  return
627
 
628
- # 6๋‹จ๊ณ„: ์Šคํฌ๋ฆฐ์ƒท ์™„๋ฃŒ ๋ฐ AI ๋ถ„์„ ์‹œ์ž‘
629
  yield f"""
630
  <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
631
  <h4>2๋‹จ๊ณ„: ์ž…๋ ฅํ•˜์‹  ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ–ˆ๊ณ , ์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค...</h4>
632
- <p>Google Trends ๋ฐ์ดํ„ฐ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์บก์ฒ˜ํ–ˆ์Šต๋‹ˆ๋‹ค.</p>
633
  </div>
634
 
635
  <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #FF6B6B 0%, #FF8E8E 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
@@ -639,65 +661,37 @@ def main_analysis(region: str, period: str, category: str):
639
  </div>
640
  """
641
 
642
- # 7๋‹จ๊ณ„: Claude API ๋ถ„์„
643
- ai_start_time = time.time()
644
  print("์‹ค์‹œ๊ฐ„ ์ธ๊ธฐ ๊ฒ€์ƒ‰์–ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™”๊ณ , Claude ๋ถ„์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค...")
 
645
 
646
- # AI ๋ถ„์„์„ ๋ณ„๋„ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰
647
- with ThreadPoolExecutor(max_workers=1) as executor:
648
- ai_future = executor.submit(
649
- analyze_with_claude,
650
- screenshot_b64, region, period, category
651
- )
652
-
653
- # AI ๋ถ„์„ ์ง„ํ–‰ ์ƒํ™ฉ ํ‘œ์‹œ
654
- while not ai_future.done():
655
- ai_elapsed = time.time() - ai_start_time
656
- total_elapsed = time.time() - start_time
657
- yield f"""
658
- <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
659
- <h4>๐Ÿ“ธ 2๋‹จ๊ณ„: ์ž…๋ ฅํ•˜์‹  ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ–ˆ๊ณ , ์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค...</h4>
660
- <p>Google Trends ๋ฐ์ดํ„ฐ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์บก์ฒ˜ํ–ˆ์Šต๋‹ˆ๋‹ค.</p>
661
- </div>
662
-
663
- <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #FF6B6B 0%, #FF8E8E 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
664
- <h4>3๋‹จ๊ณ„: AI ๋ถ„์„ ์ง„ํ–‰ ์ค‘...</h4>
665
- <p>Claude AI๊ฐ€ ํŠธ๋ Œ๋“œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ({ai_elapsed:.1f}์ดˆ)</p>
666
- <p style="font-size: 14px; opacity: 0.9;">์ด ๊ฒฝ๊ณผ ์‹œ๊ฐ„: {total_elapsed:.1f}์ดˆ</p>
667
- </div>
668
- """
669
- time.sleep(0.5)
670
-
671
- analysis_result = ai_future.result()
672
-
673
- ai_time = time.time() - ai_start_time
674
  total_time = time.time() - start_time
675
 
676
- # 8๋‹จ๊ณ„: ์ตœ์ข… ๊ฒฐ๊ณผ ์ถœ๋ ฅ
677
  yield f"""
678
  <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
679
- <h3> ๋ถ„์„ ์™„๋ฃŒ!</h3>
680
  <p><strong>์„ค์ •:</strong> {region} | {period} | {category}</p>
681
- <p>๋ชจ๋“  ๋‹จ๊ณ„๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.</p>
682
  </div>
683
 
684
  {analysis_result}
685
 
686
  <div style="margin-top: 20px; padding: 15px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; text-align: center;">
687
  <p style="margin: 0; font-size: 14px;">
688
- <strong>๋‹ค๋ฅธ ์„ค์ •์œผ๋กœ ๋‹ค์‹œ ๋ถ„์„ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?</strong><br>
689
  ์œ„์˜ ์„ค์ •์„ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‹ค์‹œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”!
690
  </p>
691
  </div>
692
 
693
  <div style="margin-top: 15px; padding: 10px; background-color: #f9f9f9; border-radius: 5px; font-size: 12px; color: #666; text-align: center;">
694
- <p style="margin: 5px 0;"> <strong>๋ถ„์„ ์™„๋ฃŒ ์‹œ๊ฐ„:</strong> ์•ฝ 15-30์ดˆ</p>
695
- <p style="margin: 5px 0;"> <strong>AI ์—”์ง„:</strong> Claude 3.5 Sonnet</p>
696
- <p style="margin: 5px 0;"> <strong>๋ฐ์ดํ„ฐ ์ถœ์ฒ˜:</strong> Google Trends ์‹ค์‹œ๊ฐ„ ์Šคํฌ๋ฆฐ์ƒท</p>
697
  </div>
698
  """
699
 
700
- print("์ „์ฒด ๋ถ„์„ ํ”„๋กœ์„ธ์Šค ์™„๋ฃŒ!")
701
 
702
  except Exception as e:
703
  error_details = traceback.format_exc()
@@ -713,7 +707,7 @@ def main_analysis(region: str, period: str, category: str):
713
  {error_details}
714
  </pre>
715
  </details>
716
- <p><strong>ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:</strong> ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•˜์„ธ์š”.</p>
717
  </div>
718
  """
719
 
@@ -734,14 +728,25 @@ def create_interface():
734
  ) as interface:
735
 
736
  # Header
737
- gr.HTML("""
738
  <div style="text-align: center; padding: 20px; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
739
  <h1>Google Trends ์‹ค์‹œ๊ฐ„ ๋ถ„์„๊ธฐ</h1>
740
  <p>Google Trends์˜ ์‹ค์‹œ๊ฐ„ ์ธ๊ธฐ ๊ฒ€์ƒ‰์–ด๋ฅผ AI๊ฐ€ ์ž๋™์œผ๋กœ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค</p>
741
  <p style="font-size: 14px; opacity: 0.9;">๊ฐœ์„ ๋œ ๋ฐฉ์‹์œผ๋กœ ๋‹จ๊ณ„๋ณ„ ์ง„ํ–‰์ƒํ™ฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>
 
742
  </div>
743
  """)
744
 
 
 
 
 
 
 
 
 
 
 
745
  # ์ž…๋ ฅ ์„น์…˜
746
  with gr.Row():
747
  with gr.Column(scale=1):
@@ -775,33 +780,34 @@ def create_interface():
775
  size="lg"
776
  )
777
 
778
- # ์ง„ํ–‰ ์ƒํ™ฉ ๋ฐ ๊ฒฐ๊ณผ ์ถœ๋ ฅ
779
  output = gr.HTML(
780
  label="์‹ค์‹œ๊ฐ„ ๋ถ„์„ ๊ฒฐ๊ณผ",
781
- value="""
782
  <div style="text-align: center; padding: 20px; border: 2px dashed #ccc; border-radius: 10px; color: #666;">
783
  <h3>๋ถ„์„ ๋Œ€๊ธฐ ์ค‘</h3>
784
  <p>์œ„์˜ ์„ค์ •์„ ์„ ํƒํ•˜๊ณ  '์‹ค์‹œ๊ฐ„ ํŠธ๋ Œ๋“œ ๋ถ„์„ ์‹œ์ž‘' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”!</p>
785
  <p style="font-size: 14px;">๊ฐœ์„ ๋œ ๋ฐฉ์‹์œผ๋กœ <strong>15-30์ดˆ</strong>๋งŒ์— ๋ถ„์„์ด ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค.</p>
 
786
  </div>
787
  """
788
  )
789
 
790
- # ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ (Generator ํ•จ์ˆ˜๋กœ ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ)
791
  analyze_btn.click(
792
  fn=main_analysis,
793
  inputs=[region_input, period_input, category_input],
794
  outputs=output,
795
- show_progress="hidden" # ๋‚ด์žฅ ์ง„ํ–‰๋ฅ  ํ‘œ์‹œ ์ˆจ๊น€ (์ž์ฒด ์ŠคํŠธ๋ฆฌ๋ฐ ์‚ฌ์šฉ)
796
  )
797
 
798
  # ์‚ฌ์šฉ๋ฒ• ์•ˆ๋‚ด
799
  gr.HTML("""
800
  <div style="margin-top: 30px; padding: 20px; background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); border-radius: 10px;">
801
- <h3 style="margin-top: 0; color: #333;"> ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ!</h3>
802
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; color: #555;">
803
  <div>
804
- <h4> ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ</h4>
805
  <ul style="margin: 0; padding-left: 20px;">
806
  <li>๋ธŒ๋ผ์šฐ์ € ์‹คํ–‰ ์ƒํƒœ ์‹ค์‹œ๊ฐ„ ํ‘œ์‹œ</li>
807
  <li>์„ค์ • ๋ณ€๊ฒฝ ๊ณผ์ • ๋‹จ๊ณ„๋ณ„ ํ™•์ธ</li>
@@ -809,9 +815,9 @@ def create_interface():
809
  </ul>
810
  </div>
811
  <div>
812
- <h4> ์†๋„ ์ตœ์ ํ™”</h4>
813
  <ul style="margin: 0; padding-left: 20px;">
814
- <li>๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋‹จ์ถ• </li>
815
  <li>๋ถˆํ•„์š”ํ•œ ๋กœ๋”ฉ ์‹œ๊ฐ„ ์ œ๊ฑฐ</li>
816
  <li>ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ</li>
817
  </ul>
@@ -820,12 +826,13 @@ def create_interface():
820
  </div>
821
  """)
822
 
823
- # footer
824
- gr.HTML("""
825
  <div style="text-align: center; margin-top: 20px; padding: 15px; color: #666; border-top: 1px solid #eee;">
826
- <p> <strong>์‚ฌ์šฉ๋ฒ•:</strong> ์›ํ•˜๋Š” ์„ค์ •์„ ์„ ํƒํ•˜๊ณ  ๋ถ„์„ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”</p>
827
- <p> <strong>์†๋„:</strong> ์ ์ฐจ์ ์œผ๋กœ ๊ฐœ์„  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.</p>
828
- <p> <strong>Powered by:</strong> Google Trends + Claude AI + ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ</p>
 
829
  </div>
830
  """)
831
 
@@ -835,24 +842,27 @@ def create_interface():
835
  if __name__ == "__main__":
836
  print("=" * 50)
837
  print("Google Trends ์‹ค์‹œ๊ฐ„ ๋ถ„์„๊ธฐ ์‹œ์ž‘!")
 
838
  print("=" * 50)
839
 
840
  # API ํ‚ค ํ™•์ธ
841
  if not ANTHROPIC_API_KEY or ANTHROPIC_API_KEY == "your_api_key_here":
842
- print(" ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์Œ")
843
- print("\n ์ด ์ƒํƒœ๋กœ๋„ ์•ฑ์€ ์‹คํ–‰๋˜์ง€๋งŒ, ๋ถ„์„ ๊ธฐ๋Šฅ์€ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค.")
 
 
844
  print("=" * 50)
845
  else:
846
- print(" Claude API ํ‚ค ํ™•์ธ๋จ")
847
 
848
  # ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
849
- print(" Gradio ์›น ์ธํ„ฐํŽ˜์ด์Šค ์ค€๋น„ ์ค‘...")
850
  app = create_interface()
851
 
852
  # ํ™˜๊ฒฝ๋ณ„ ์‹คํ–‰ ์„ค์ •
853
  if os.getenv("SPACE_ID"):
854
  # Hugging Face Space ํ™˜๊ฒฝ
855
- print(" Hugging Face Space ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ ์ค‘...")
856
  app.launch(
857
  server_name="0.0.0.0",
858
  server_port=7860,
@@ -861,9 +871,9 @@ if __name__ == "__main__":
861
  )
862
  else:
863
  # ๋กœ์ปฌ ํ™˜๊ฒฝ
864
- print(" ๋กœ์ปฌ ์„œ๋ฒ„ ์‹œ์ž‘ ์ค‘...")
865
- print(" ๋ธŒ๋ผ์šฐ์ €์—์„œ http://localhost:7860 ์œผ๋กœ ์ ‘์†")
866
- print(" ์ข…๋ฃŒํ•˜๋ ค๋ฉด Ctrl+C")
867
  print("=" * 50)
868
 
869
  try:
@@ -872,13 +882,902 @@ if __name__ == "__main__":
872
  server_port=7860,
873
  show_error=True,
874
  show_api=False,
875
- share=False, # ๋กœ์ปฌ์—์„œ๋Š” ๊ณต๊ฐœ ๋งํฌ ๋น„ํ™œ์„ฑํ™”
876
- inbrowser=True, # ์ž๋™์œผ๋กœ ๋ธŒ๋ผ์šฐ์ € ์—ด๊ธฐ
877
- quiet=False # ์ƒ์„ธ ๋กœ๊ทธ ํ‘œ์‹œ
878
  )
879
  except KeyboardInterrupt:
880
  print("\n\nGoogle Trends ๋ถ„์„๊ธฐ๊ฐ€ ์ข…๋ฃŒ")
881
- print("๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ")
882
  except Exception as e:
883
- print(f"\n ์„œ๋ฒ„ ์‹คํ–‰ ์ค‘ ์˜ค๋ฅ˜: {e}")
884
- print("ํฌํŠธ 7860์ด ์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ์ง€ ํ™•์ธ")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import base64
4
  import json
5
  import traceback
6
+ import subprocess
7
+ import sys
8
  from typing import Tuple, Optional
9
  import gradio as gr
10
  import anthropic
 
21
  IS_HUGGINGFACE = os.getenv("SPACE_ID") is not None
22
  print(f'IS_HUGGINGFACE = {IS_HUGGINGFACE}')
23
 
24
+ # Hugging Face ํ™˜๊ฒฝ์—์„œ Chrome ์„ค์น˜
25
+ def install_chrome():
26
+ if not IS_HUGGINGFACE:
27
+ return True
28
+
29
+ try:
30
+ print("Chrome ์„ค์น˜ ์‹œ์ž‘...")
31
+
32
+ # Google Chrome ๋‹ค์šด๋กœ๋“œ ๋ฐ ์„ค์น˜
33
+ commands = [
34
+ "wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add -",
35
+ "echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list",
36
+ "apt-get update",
37
+ "apt-get install -y google-chrome-stable"
38
+ ]
39
+
40
+ for cmd in commands:
41
+ print(f"์‹คํ–‰ ์ค‘: {cmd}")
42
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
43
+ if result.returncode != 0:
44
+ print(f"๋ช…๋ น์–ด ์‹คํŒจ: {cmd}")
45
+ print(f"์˜ค๋ฅ˜: {result.stderr}")
46
+ else:
47
+ print(f"์„ฑ๊ณต: {cmd}")
48
+
49
+ # Chrome ๋ฒ„์ „ ํ™•์ธ
50
+ result = subprocess.run("google-chrome --version", shell=True, capture_output=True, text=True)
51
+ if result.returncode == 0:
52
+ print(f"Chrome ์„ค์น˜ ์™„๋ฃŒ: {result.stdout.strip()}")
53
+ return True
54
+ else:
55
+ print("Chrome ์„ค์น˜ ์‹คํŒจ")
56
+ return False
57
+
58
+ except Exception as e:
59
+ print(f"Chrome ์„ค์น˜ ์ค‘ ์˜ค๋ฅ˜: {e}")
60
+ return False
61
+
62
+ # ์‹œ์ž‘ ์‹œ Chrome ์„ค์น˜
63
+ if IS_HUGGINGFACE:
64
+ chrome_installed = install_chrome()
65
+ if not chrome_installed:
66
+ print("Chrome ์„ค์น˜์— ์‹คํŒจํ–ˆ์ง€๋งŒ ๊ณ„์† ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค...")
67
+
68
  # Claude API ์„ค์ • (Hugging Face Secrets์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ)
69
  ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY")
70
 
 
94
 
95
  # Hugging Face Space ํ™˜๊ฒฝ ์„ค์ •
96
  if IS_HUGGINGFACE:
97
+ print("Hugging Face Space ํ™˜๊ฒฝ, Chrome ๋ธŒ๋ผ์šฐ์ €๋กœ ์‹คํ–‰")
98
+ # Chrome ๋ฐ”์ด๋„ˆ๋ฆฌ ๊ฒฝ๋กœ ์„ค์ •
99
+ chrome_paths = [
100
+ "/usr/bin/google-chrome",
101
+ "/usr/bin/google-chrome-stable",
102
+ "/usr/bin/chromium",
103
+ "/usr/bin/chromium-browser"
104
+ ]
105
+
106
+ chrome_binary = None
107
+ for path in chrome_paths:
108
+ if os.path.exists(path):
109
+ chrome_binary = path
110
+ print(f"Chrome ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐœ๊ฒฌ: {path}")
111
+ break
112
+
113
+ if chrome_binary:
114
+ options.binary_location = chrome_binary
115
+
116
+ options.add_argument("--headless=new")
117
  options.add_argument("--no-sandbox")
118
  options.add_argument("--disable-dev-shm-usage")
119
  options.add_argument("--disable-gpu")
120
  options.add_argument("--disable-software-rasterizer")
121
  options.add_argument("--remote-debugging-port=9222")
122
+ options.add_argument("--single-process")
123
+ options.add_argument("--disable-background-timer-throttling")
124
+ options.add_argument("--disable-backgrounding-occluded-windows")
125
+ options.add_argument("--disable-renderer-backgrounding")
126
  else:
127
  options.add_argument("--headless")
128
 
 
 
 
 
 
 
 
 
 
129
  # ์†๋„ ์ตœ์ ํ™” ์„ค์ •
130
+ options.add_argument("--window-size=1280,720")
131
  options.add_argument("--disable-blink-features=AutomationControlled")
132
  options.add_argument("--disable-extensions")
133
  options.add_argument("--disable-web-security")
134
  options.add_argument("--allow-running-insecure-content")
 
 
 
135
  options.add_argument("--disable-features=TranslateUI")
136
  options.add_argument("--disable-ipc-flooding-protection")
137
 
 
141
 
142
  # ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”
143
  options.add_argument("--memory-pressure-off")
144
+ options.add_argument("--max_old_space_size=2048")
 
145
 
146
  # ์ด๋ฏธ์ง€/CSS ๋น„ํ™œ์„ฑํ™”๋กœ ๋กœ๋”ฉ ์†๋„ ํ–ฅ์ƒ
147
  prefs = {
148
+ "profile.managed_default_content_settings.images": 2,
149
+ "profile.default_content_setting_values.notifications": 2,
150
  }
151
  options.add_experimental_option("prefs", prefs)
152
 
153
  # ๋ด‡ ๊ฐ์ง€ ํšŒํ”ผ
154
+ options.add_argument("--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
155
  options.add_experimental_option("excludeSwitches", ["enable-automation"])
156
  options.add_experimental_option('useAutomationExtension', False)
157
 
 
160
  options.add_argument("--accept-lang=ko-KR,ko;q=0.9,en;q=0.8")
161
 
162
  try:
163
+ # Hugging Face ํ™˜๊ฒฝ์—์„œ ChromeDriver ์„ค์ •
164
  if IS_HUGGINGFACE:
165
  try:
166
+ # webdriver-manager ์‚ฌ์šฉ
167
  from webdriver_manager.chrome import ChromeDriverManager
168
  from selenium.webdriver.chrome.service import Service
169
 
 
173
  print("ChromeDriver ์„ค์น˜ ๋ฐ ์ดˆ๊ธฐํ™” ์„ฑ๊ณต")
174
 
175
  except Exception as e:
176
+ print(f"webdriver-manager ์‹คํŒจ: {e}")
177
  # ์‹œ์Šคํ…œ ChromeDriver ์‹œ๋„
178
  try:
179
  self.driver = webdriver.Chrome(options=options)
 
193
  except:
194
  self.driver = webdriver.Chrome(options=options)
195
  print("๋กœ์ปฌ ์‹œ์Šคํ…œ ChromeDriver ์‚ฌ์šฉ")
 
 
196
 
197
+ # ํƒ€์ž„์•„์›ƒ ์„ค์ •
198
+ self.driver.set_page_load_timeout(20 if IS_HUGGINGFACE else 15)
199
+ self.driver.implicitly_wait(3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
 
201
  # ๋ด‡ ๊ฐ์ง€ ๋ฐฉ์ง€ ์Šคํฌ๋ฆฝํŠธ
202
  try:
 
268
  if progress_callback:
269
  progress_callback("์ง€์—ญ ์„ค์ • ๋ณ€๊ฒฝ ์ค‘...")
270
  results['region'] = self.change_region(region)
271
+ time.sleep(0.5)
272
  else:
273
  results['region'] = True
274
 
 
276
  if progress_callback:
277
  progress_callback("๊ธฐ๊ฐ„ ์„ค์ • ๋ณ€๊ฒฝ ์ค‘...")
278
  results['period'] = self.change_time_period(period)
279
+ time.sleep(0.5)
280
  else:
281
  results['period'] = True
282
 
 
284
  if progress_callback:
285
  progress_callback("์นดํ…Œ๊ณ ๋ฆฌ ์„ค์ • ๋ณ€๊ฒฝ ์ค‘...")
286
  results['category'] = self.change_category(category)
287
+ time.sleep(0.5)
288
  else:
289
  results['category'] = True
290
 
 
305
  if not self.safe_click(region_selectors):
306
  return False
307
 
308
+ time.sleep(1)
309
 
310
  region_mapping = {
311
  "์ „์„ธ๊ณ„": ["์ „ ์„ธ๊ณ„", "Worldwide"],
 
341
  if not self.safe_click(period_selectors):
342
  return False
343
 
344
+ time.sleep(1)
345
 
346
  period_mapping = {
347
  "์ง€๋‚œ 1์‹œ๊ฐ„": ["์ง€๋‚œ 1์‹œ๊ฐ„"],
 
377
  if not self.safe_click(category_selectors):
378
  return False
379
 
380
+ time.sleep(1)
381
 
382
  category_mapping = {
383
  "๊ฒŒ์ž„": ["๊ฒŒ์ž„"],
 
423
  # Google Trends ์ ‘์† (ํ•œ๊ตญ์–ด ์ง์ ‘ URL)
424
  self.driver.get("https://trends.google.com/trending?geo=KR&hl=ko")
425
 
426
+ # ๋กœ๋”ฉ ๋Œ€๊ธฐ (Hugging Face์—์„œ๋Š” ์กฐ๊ธˆ ๋” ๊ธฐ๋‹ค๋ฆผ)
427
+ wait_time = 3 if IS_HUGGINGFACE else 1
428
+ time.sleep(wait_time)
429
 
430
  if progress_callback:
431
  progress_callback("ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์™„๋ฃŒ, ์„ค์ • ๋ณ€๊ฒฝ ์‹œ์ž‘...")
432
 
433
  # ํŽ˜์ด์ง€ ์ค€๋น„ ํ™•์ธ (๋น ๋ฅธ ์ฒดํฌ)
434
  try:
435
+ WebDriverWait(self.driver, 8).until(
436
  EC.presence_of_element_located((By.TAG_NAME, "button"))
437
  )
438
  except TimeoutException:
439
+ pass
440
 
441
  # ์„ค์ • ๋ณ€๊ฒฝ (๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ)
442
  settings_result = self.parallel_change_settings(region, period, category, progress_callback)
 
456
  if progress_callback:
457
  progress_callback("์„ค์ • ๋ณ€๊ฒฝ ์™„๋ฃŒ, ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๋Š”์ค‘...")
458
 
459
+ # ์ตœ์ข… ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ๋Œ€๊ธฐ
460
+ time.sleep(2 if IS_HUGGINGFACE else 1)
461
 
462
+ # ์Šคํฌ๋ฆฐ์ƒท ์ดฌ์˜
463
  screenshot = self.driver.get_screenshot_as_png()
464
  screenshot_b64 = base64.b64encode(screenshot).decode()
465
 
 
473
 
474
  finally:
475
  if self.driver:
476
+ try:
477
+ self.driver.quit()
478
+ except:
479
+ pass
480
 
481
  # Claude API๋กœ ์Šคํฌ๋ฆฐ์ƒท ๋ถ„์„
482
  def analyze_with_claude(screenshot_b64: str, region: str, period: str, category: str) -> str:
 
487
  <div style="color: red; padding: 20px; border: 1px solid red; border-radius: 5px;">
488
  <h3>API ํ‚ค ์˜ค๋ฅ˜</h3>
489
  <p><strong>ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!</strong></p>
490
+ <p>Hugging Face Space Settings์—์„œ Secret์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.</p>
491
  </div>
492
  """
493
 
 
519
  # Claude API ํ˜ธ์ถœ (๋น ๋ฅธ ์„ค์ •)
520
  message = claude_client.messages.create(
521
  model="claude-3-5-sonnet-20241022",
522
+ max_tokens=1000,
523
  messages=[
524
  {
525
  "role": "user",
 
552
  """
553
 
554
  def main_analysis(region: str, period: str, category: str):
555
+ # ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ถ„์„ ํ•จ์ˆ˜ (Generator)
556
 
557
  start_time = time.time()
558
 
 
562
  <h3>Google Trends ๋ถ„์„ ์‹œ์ž‘!</h3>
563
  <p><strong>์„ค์ •:</strong> {region} | {period} | {category}</p>
564
  <p>์‹ค์‹œ๊ฐ„์œผ๋กœ ์ง„ํ–‰ ์ƒํ™ฉ์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค...</p>
565
+ {'<p style="font-size: 12px; opacity: 0.8;">Hugging Face Space ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ ์ค‘</p>' if IS_HUGGINGFACE else ''}
566
  </div>
567
  """
568
 
 
576
  if not ANTHROPIC_API_KEY or ANTHROPIC_API_KEY == "your_api_key_here":
577
  yield """
578
  <div style="color: red; padding: 20px; border: 1px solid red; border-radius: 5px;">
579
+ <h3>API ํ‚ค ๋ˆ„๋ฝ</h3>
580
  <p><strong>ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!</strong></p>
581
+ <p><strong>ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:</strong> Hugging Face Space Settings์—์„œ Secret์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.</p>
582
  </div>
583
  """
584
  return
 
586
  # 3๋‹จ๊ณ„: ๋ธŒ๋ผ์šฐ์ € ์‹คํ–‰ ์•Œ๋ฆผ
587
  yield f"""
588
  <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
589
+ <h4>1๋‹จ๊ณ„: ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค! ์ž…๋ ฅํ•˜์‹  ์กฐ๊ฑด์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค...</h4>
590
  <p>Connect Start...</p>
591
  </div>
592
 
593
  <div style="padding: 15px; background-color: #f8f9fa; border-radius: 5px; margin-bottom: 15px;">
594
+ <p><strong>์ง„ํ–‰ ์ค‘:</strong> ๋ธŒ๋ผ์šฐ์ € ๋“œ๋ผ์ด๋ฒ„ ์„ค์ • ๋ฐ ํŽ˜์ด์ง€ ๋กœ๋”ฉ...</p>
595
+ {'<p style="font-size: 12px; color: #666;">Chrome ๋ธŒ๋ผ์šฐ์ € ์ดˆ๊ธฐํ™” ์ค‘...</p>' if IS_HUGGINGFACE else ''}
596
  </div>
597
  """
598
 
599
  # 4๋‹จ๊ณ„: Google Trends ์ž๋™ํ™” ์‹คํ–‰
600
+ print(f"๋ถ„์„ ์‹œ์ž‘: {region} | {period} | {category}")
601
  automator = GoogleTrendsAutomator()
602
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
603
  # ๋ธŒ๋ผ์šฐ์ € ์ž‘์—…์„ ๋ณ„๋„ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰
604
  with ThreadPoolExecutor(max_workers=1) as executor:
605
  future = executor.submit(
 
614
  elapsed = time.time() - start_time
615
  yield f"""
616
  <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
617
+ <h4>์ž๋™ํ™” ์ง„ํ–‰ ์ค‘</h4>
618
+ <p>Chrome ๋ธŒ๋ผ์šฐ์ €์—์„œ Google Trends ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ์ค‘...</p>
619
  </div>
620
 
621
  <div style="padding: 15px; background-color: #f8f9fa; border-radius: 5px; margin-bottom: 15px;">
622
+ <p><strong>ํ˜„์žฌ ์ž‘์—…:</strong> {current_progress}</p>
623
  <p><strong>๊ฒฝ๊ณผ ์‹œ๊ฐ„:</strong> {elapsed:.1f}์ดˆ</p>
624
  </div>
625
  """
 
637
  <pre style="background-color: #fff5f5; padding: 15px; border-radius: 5px; margin: 15px 0;">
638
  {status_msg}
639
  </pre>
640
+ <p><strong>ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:</strong></p>
641
+ <ul>
642
+ <li>packages.txt ํŒŒ์ผ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ค์ •๋˜์—ˆ๋Š”์ง€ ํ™•์ธ</li>
643
+ <li>Space๋ฅผ ์žฌ์‹œ์ž‘ํ•ด๋ณด์„ธ์š”</li>
644
+ <li>์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด๋ณด์„ธ์š”</li>
645
+ </ul>
646
  </div>
647
  """
648
  return
649
 
650
+ # AI ๋ถ„๏ฟฝ๏ฟฝ ์‹œ์ž‘
651
  yield f"""
652
  <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
653
  <h4>2๋‹จ๊ณ„: ์ž…๋ ฅํ•˜์‹  ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ–ˆ๊ณ , ์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค...</h4>
654
+ <p>Google Trends ๋ฐ์ดํ„ฐ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์บก์ฒ˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ({browser_time:.1f}์ดˆ)</p>
655
  </div>
656
 
657
  <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #FF6B6B 0%, #FF8E8E 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
 
661
  </div>
662
  """
663
 
664
+ # Claude ๋ถ„์„
 
665
  print("์‹ค์‹œ๊ฐ„ ์ธ๊ธฐ ๊ฒ€์ƒ‰์–ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™”๊ณ , Claude ๋ถ„์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค...")
666
+ analysis_result = analyze_with_claude(screenshot_b64, region, period, category)
667
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
668
  total_time = time.time() - start_time
669
 
670
+ # ์ตœ์ข… ๊ฒฐ๊ณผ
671
  yield f"""
672
  <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
673
+ <h3>๋ถ„์„ ์™„๋ฃŒ!</h3>
674
  <p><strong>์„ค์ •:</strong> {region} | {period} | {category}</p>
675
+ <p><strong>์ด ์†Œ์š” ์‹œ๊ฐ„:</strong> {total_time:.1f}์ดˆ</p>
676
  </div>
677
 
678
  {analysis_result}
679
 
680
  <div style="margin-top: 20px; padding: 15px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; text-align: center;">
681
  <p style="margin: 0; font-size: 14px;">
682
+ <strong>๋‹ค๋ฅธ ์„ค์ •์œผ๋กœ ๋‹ค์‹œ ๋ถ„์„ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?</strong><br>
683
  ์œ„์˜ ์„ค์ •์„ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‹ค์‹œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”!
684
  </p>
685
  </div>
686
 
687
  <div style="margin-top: 15px; padding: 10px; background-color: #f9f9f9; border-radius: 5px; font-size: 12px; color: #666; text-align: center;">
688
+ <p style="margin: 5px 0;"><strong>์‹คํ–‰ ํ™˜๊ฒฝ:</strong> {'Hugging Face Space (Chrome)' if IS_HUGGINGFACE else 'Local'}</p>
689
+ <p style="margin: 5px 0;"><strong>AI ์—”์ง„:</strong> Claude 3.5 Sonnet</p>
690
+ <p style="margin: 5px 0;"><strong>๋ฐ์ดํ„ฐ ์ถœ์ฒ˜:</strong> Google Trends ์‹ค์‹œ๊ฐ„ ์Šคํฌ๋ฆฐ์ƒท</p>
691
  </div>
692
  """
693
 
694
+ print(f"์ „์ฒด ๋ถ„์„ ํ”„๋กœ์„ธ์Šค ์™„๋ฃŒ! ์ด {total_time:.1f}์ดˆ")
695
 
696
  except Exception as e:
697
  error_details = traceback.format_exc()
 
707
  {error_details}
708
  </pre>
709
  </details>
710
+ <p><strong>ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:</strong> Space๋ฅผ ์žฌ์‹œ์ž‘ํ•˜๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•˜์„ธ์š”.</p>
711
  </div>
712
  """
713
 
 
728
  ) as interface:
729
 
730
  # Header
731
+ gr.HTML(f"""
732
  <div style="text-align: center; padding: 20px; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
733
  <h1>Google Trends ์‹ค์‹œ๊ฐ„ ๋ถ„์„๊ธฐ</h1>
734
  <p>Google Trends์˜ ์‹ค์‹œ๊ฐ„ ์ธ๊ธฐ ๊ฒ€์ƒ‰์–ด๋ฅผ AI๊ฐ€ ์ž๋™์œผ๋กœ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค</p>
735
  <p style="font-size: 14px; opacity: 0.9;">๊ฐœ์„ ๋œ ๋ฐฉ์‹์œผ๋กœ ๋‹จ๊ณ„๋ณ„ ์ง„ํ–‰์ƒํ™ฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>
736
+ {'<p style="font-size: 12px; opacity: 0.7;">Hugging Face Space (Chrome ๋ธŒ๋ผ์šฐ์ €)</p>' if IS_HUGGINGFACE else ''}
737
  </div>
738
  """)
739
 
740
+ # API ํ‚ค ์ƒํƒœ ํ‘œ์‹œ
741
+ if not ANTHROPIC_API_KEY:
742
+ gr.HTML("""
743
+ <div style="color: red; padding: 15px; border: 1px solid red; border-radius: 5px; margin-bottom: 20px;">
744
+ <h3>์„ค์ • ํ•„์š”</h3>
745
+ <p><strong>ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!</strong></p>
746
+ <p>Hugging Face Space Settings์—์„œ Secret์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.</p>
747
+ </div>
748
+ """)
749
+
750
  # ์ž…๋ ฅ ์„น์…˜
751
  with gr.Row():
752
  with gr.Column(scale=1):
 
780
  size="lg"
781
  )
782
 
783
+ # ๊ฒฐ๊ณผ ์ถœ๋ ฅ
784
  output = gr.HTML(
785
  label="์‹ค์‹œ๊ฐ„ ๋ถ„์„ ๊ฒฐ๊ณผ",
786
+ value=f"""
787
  <div style="text-align: center; padding: 20px; border: 2px dashed #ccc; border-radius: 10px; color: #666;">
788
  <h3>๋ถ„์„ ๋Œ€๊ธฐ ์ค‘</h3>
789
  <p>์œ„์˜ ์„ค์ •์„ ์„ ํƒํ•˜๊ณ  '์‹ค์‹œ๊ฐ„ ํŠธ๋ Œ๋“œ ๋ถ„์„ ์‹œ์ž‘' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”!</p>
790
  <p style="font-size: 14px;">๊ฐœ์„ ๋œ ๋ฐฉ์‹์œผ๋กœ <strong>15-30์ดˆ</strong>๋งŒ์— ๋ถ„์„์ด ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค.</p>
791
+ {'<p style="font-size: 12px; color: #666;">Chrome ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ</p>' if IS_HUGGINGFACE else ''}
792
  </div>
793
  """
794
  )
795
 
796
+ # ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
797
  analyze_btn.click(
798
  fn=main_analysis,
799
  inputs=[region_input, period_input, category_input],
800
  outputs=output,
801
+ show_progress="hidden"
802
  )
803
 
804
  # ์‚ฌ์šฉ๋ฒ• ์•ˆ๋‚ด
805
  gr.HTML("""
806
  <div style="margin-top: 30px; padding: 20px; background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); border-radius: 10px;">
807
+ <h3 style="margin-top: 0; color: #333;">์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ!</h3>
808
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; color: #555;">
809
  <div>
810
+ <h4>์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ</h4>
811
  <ul style="margin: 0; padding-left: 20px;">
812
  <li>๋ธŒ๋ผ์šฐ์ € ์‹คํ–‰ ์ƒํƒœ ์‹ค์‹œ๊ฐ„ ํ‘œ์‹œ</li>
813
  <li>์„ค์ • ๋ณ€๊ฒฝ ๊ณผ์ • ๋‹จ๊ณ„๋ณ„ ํ™•์ธ</li>
 
815
  </ul>
816
  </div>
817
  <div>
818
+ <h4>์†๋„ ์ตœ์ ํ™”</h4>
819
  <ul style="margin: 0; padding-left: 20px;">
820
+ <li>๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋‹จ์ถ•</li>
821
  <li>๋ถˆํ•„์š”ํ•œ ๋กœ๋”ฉ ์‹œ๊ฐ„ ์ œ๊ฑฐ</li>
822
  <li>ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ</li>
823
  </ul>
 
826
  </div>
827
  """)
828
 
829
+ # Footer
830
+ gr.HTML(f"""
831
  <div style="text-align: center; margin-top: 20px; padding: 15px; color: #666; border-top: 1px solid #eee;">
832
+ <p><strong>์‚ฌ์šฉ๋ฒ•:</strong> ์›ํ•˜๋Š” ์„ค์ •์„ ์„ ํƒํ•˜๊ณ  ๋ถ„์„ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”</p>
833
+ <p><strong>์†๋„:</strong> ์ ์ฐจ์ ์œผ๋กœ ๊ฐœ์„  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.</p>
834
+ <p><strong>Powered by:</strong> Google Trends + Claude AI + ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ</p>
835
+ {'<p style="font-size: 12px;">Running on Hugging Face Spaces with Chrome Browser</p>' if IS_HUGGINGFACE else ''}
836
  </div>
837
  """)
838
 
 
842
  if __name__ == "__main__":
843
  print("=" * 50)
844
  print("Google Trends ์‹ค์‹œ๊ฐ„ ๋ถ„์„๊ธฐ ์‹œ์ž‘!")
845
+ print(f"์‹คํ–‰ ํ™˜๊ฒฝ: {'Hugging Face Space' if IS_HUGGINGFACE else 'Local'}")
846
  print("=" * 50)
847
 
848
  # API ํ‚ค ํ™•์ธ
849
  if not ANTHROPIC_API_KEY or ANTHROPIC_API_KEY == "your_api_key_here":
850
+ print("ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์Œ")
851
+ if IS_HUGGINGFACE:
852
+ print("Hugging Face Space Settings์—์„œ Secret์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”!")
853
+ print("์ด ์ƒํƒœ๋กœ๋„ ์•ฑ์€ ์‹คํ–‰๋˜์ง€๋งŒ, ๋ถ„์„ ๊ธฐ๋Šฅ์€ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
854
  print("=" * 50)
855
  else:
856
+ print("Claude API ํ‚ค ํ™•์ธ๋จ")
857
 
858
  # ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
859
+ print("Gradio ์›น ์ธํ„ฐํŽ˜์ด์Šค ์ค€๋น„ ์ค‘...")
860
  app = create_interface()
861
 
862
  # ํ™˜๊ฒฝ๋ณ„ ์‹คํ–‰ ์„ค์ •
863
  if os.getenv("SPACE_ID"):
864
  # Hugging Face Space ํ™˜๊ฒฝ
865
+ print("Hugging Face Space ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ ์ค‘...")
866
  app.launch(
867
  server_name="0.0.0.0",
868
  server_port=7860,
 
871
  )
872
  else:
873
  # ๋กœ์ปฌ ํ™˜๊ฒฝ
874
+ print("๋กœ์ปฌ ์„œ๋ฒ„ ์‹œ์ž‘ ์ค‘...")
875
+ print("๋ธŒ๋ผ์šฐ์ €์—์„œ http://localhost:7860 ์œผ๋กœ ์ ‘์†")
876
+ print("์ข…๋ฃŒํ•˜๋ ค๋ฉด Ctrl+C")
877
  print("=" * 50)
878
 
879
  try:
 
882
  server_port=7860,
883
  show_error=True,
884
  show_api=False,
885
+ share=False,
886
+ inbrowser=True,
887
+ quiet=False
888
  )
889
  except KeyboardInterrupt:
890
  print("\n\nGoogle Trends ๋ถ„์„๊ธฐ๊ฐ€ ์ข…๋ฃŒ")
891
+ print("๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!")
892
  except Exception as e:
893
+ print(f"\n์„œ๋ฒ„ ์‹คํ–‰ ์ค‘ ์˜ค๋ฅ˜: {e}")
894
+ print("ํฌํŠธ 7860์ด ์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ์ง€ ํ™•์ธ")
895
+
896
+
897
+
898
+
899
+
900
+ # import os
901
+ # import time
902
+ # import base64
903
+ # import json
904
+ # import traceback
905
+ # from typing import Tuple, Optional
906
+ # import gradio as gr
907
+ # import anthropic
908
+ # from selenium import webdriver
909
+ # from selenium.webdriver.chrome.options import Options
910
+ # from selenium.webdriver.common.by import By
911
+ # from selenium.webdriver.support.ui import WebDriverWait
912
+ # from selenium.webdriver.support import expected_conditions as EC
913
+ # from selenium.common.exceptions import TimeoutException, NoSuchElementException
914
+ # from concurrent.futures import ThreadPoolExecutor
915
+ # import threading
916
+
917
+ # # Hugging Face Space ํ™˜๊ฒฝ ํ™•์ธ
918
+ # IS_HUGGINGFACE = os.getenv("SPACE_ID") is not None
919
+ # print(f'IS_HUGGINGFACE = {IS_HUGGINGFACE}')
920
+
921
+ # # Claude API ์„ค์ • (Hugging Face Secrets์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ)
922
+ # ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY")
923
+
924
+ # if not ANTHROPIC_API_KEY:
925
+ # print("ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์Œ, Secret ํ™•์ธ ํ•„์š”")
926
+ # else:
927
+ # print("Claude API ํ‚ค ํ™•์ธ ์™„๋ฃŒ")
928
+
929
+ # # Claude ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ƒ์„ฑ
930
+ # def create_claude_client():
931
+ # try:
932
+ # client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)
933
+ # print("Claude API ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ ์„ฑ๊ณต")
934
+ # return client
935
+ # except Exception as e:
936
+ # print(f"Claude API ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ ์‹คํŒจ: {e}")
937
+ # return None
938
+
939
+ # # Google Trends ์ž๋™ํ™” ํด๋ž˜์Šค
940
+ # class GoogleTrendsAutomator:
941
+ # def __init__(self):
942
+ # self.driver = None
943
+
944
+ # # Chrome ๋“œ๋ผ์ด๋ฒ„ ์„ค์ •
945
+ # def setup_driver(self):
946
+ # options = Options()
947
+
948
+ # # Hugging Face Space ํ™˜๊ฒฝ ์„ค์ •
949
+ # if IS_HUGGINGFACE:
950
+ # print("Hugging Face Space ํ™˜๊ฒฝ, ํ—ค๋“œ๋ฆฌ์Šค ๋ชจ๋“œ๋กœ ์‹คํ–‰")
951
+ # options.add_argument("--headless=new") # ์ƒˆ๋กœ์šด ํ—ค๋“œ๋ฆฌ์Šค ๋ชจ๋“œ
952
+ # options.add_argument("--no-sandbox")
953
+ # options.add_argument("--disable-dev-shm-usage")
954
+ # options.add_argument("--disable-gpu")
955
+ # options.add_argument("--disable-software-rasterizer")
956
+ # options.add_argument("--remote-debugging-port=9222")
957
+ # else:
958
+ # options.add_argument("--headless")
959
+
960
+ # # # ํ™˜๊ฒฝ๋ณ„ ์„ค์ •
961
+ # # if os.getenv("SPACE_ID"): # Hugging Face Space
962
+ # # options.add_argument("--headless")
963
+ # # options.add_argument("--no-sandbox")
964
+ # # options.add_argument("--disable-dev-shm-usage")
965
+ # # options.add_argument("--disable-gpu")
966
+ # # else: # ๋กœ์ปฌ ํ™˜๊ฒฝ
967
+ # # options.add_argument("--headless")
968
+
969
+ # # ์†๋„ ์ตœ์ ํ™” ์„ค์ •
970
+ # options.add_argument("--window-size=1280,720") # ํ•ด์ƒ๋„ ์ค„์ž„
971
+ # options.add_argument("--disable-blink-features=AutomationControlled")
972
+ # options.add_argument("--disable-extensions")
973
+ # options.add_argument("--disable-web-security")
974
+ # options.add_argument("--allow-running-insecure-content")
975
+ # options.add_argument("--disable-background-timer-throttling")
976
+ # options.add_argument("--disable-backgrounding-occluded-windows")
977
+ # options.add_argument("--disable-renderer-backgrounding")
978
+ # options.add_argument("--disable-features=TranslateUI")
979
+ # options.add_argument("--disable-ipc-flooding-protection")
980
+
981
+ # # ๋„คํŠธ์›Œํฌ ์ตœ์ ํ™”
982
+ # options.add_argument("--aggressive-cache-discard")
983
+ # options.add_argument("--disable-background-networking")
984
+
985
+ # # ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”
986
+ # options.add_argument("--memory-pressure-off")
987
+ # # options.add_argument("--max_old_space_size=4096")
988
+ # options.add_argument("--max_old_space_size=2048")# ๋ฉ”๋ชจ๋ฆฌ ์ œํ•œ
989
+
990
+ # # ์ด๋ฏธ์ง€/CSS ๋น„ํ™œ์„ฑํ™”๋กœ ๋กœ๋”ฉ ์†๋„ ํ–ฅ์ƒ
991
+ # prefs = {
992
+ # "profile.managed_default_content_settings.images": 2, # ์ด๋ฏธ์ง€ ์ฐจ๋‹จ
993
+ # "profile.default_content_setting_values.notifications": 2, # ์•Œ๋ฆผ ์ฐจ๋‹จ
994
+ # }
995
+ # options.add_experimental_option("prefs", prefs)
996
+
997
+ # # ๋ด‡ ๊ฐ์ง€ ํšŒํ”ผ
998
+ # options.add_argument("--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")
999
+ # options.add_experimental_option("excludeSwitches", ["enable-automation"])
1000
+ # options.add_experimental_option('useAutomationExtension', False)
1001
+
1002
+ # # ์–ธ์–ด ์„ค์ •
1003
+ # options.add_argument("--lang=ko")
1004
+ # options.add_argument("--accept-lang=ko-KR,ko;q=0.9,en;q=0.8")
1005
+
1006
+ # try:
1007
+ # # Hugging Face ํ™˜๊ฒฝ์—์„œ๋Š” webdriver-manager ์šฐ์„  ์‚ฌ์šฉ
1008
+ # if IS_HUGGINGFACE:
1009
+ # try:
1010
+ # from webdriver_manager.chrome import ChromeDriverManager
1011
+ # from selenium.webdriver.chrome.service import Service
1012
+
1013
+ # print("ChromeDriver ์ž๋™ ์„ค์น˜ ์ค‘...")
1014
+ # service = Service(ChromeDriverManager().install())
1015
+ # self.driver = webdriver.Chrome(service=service, options=options)
1016
+ # print("ChromeDriver ์„ค์น˜ ๋ฐ ์ดˆ๊ธฐํ™” ์„ฑ๊ณต")
1017
+
1018
+ # except Exception as e:
1019
+ # print(f"webdriver-manager ์„ค์น˜ ์‹คํŒจ: {e}")
1020
+ # # ์‹œ์Šคํ…œ ChromeDriver ์‹œ๋„
1021
+ # try:
1022
+ # self.driver = webdriver.Chrome(options=options)
1023
+ # print("์‹œ์Šคํ…œ ChromeDriver ์‚ฌ์šฉ ์„ฑ๊ณต")
1024
+ # except Exception as sys_e:
1025
+ # print(f"์‹œ์Šคํ…œ ChromeDriver๋„ ์‹คํŒจ: {sys_e}")
1026
+ # return False
1027
+ # else:
1028
+ # # ๋กœ์ปฌ ํ™˜๊ฒฝ
1029
+ # try:
1030
+ # from webdriver_manager.chrome import ChromeDriverManager
1031
+ # from selenium.webdriver.chrome.service import Service
1032
+
1033
+ # service = Service(ChromeDriverManager().install())
1034
+ # self.driver = webdriver.Chrome(service=service, options=options)
1035
+ # print("๋กœ์ปฌ ChromeDriver ์„ค์น˜ ์„ฑ๊ณต")
1036
+ # except:
1037
+ # self.driver = webdriver.Chrome(options=options)
1038
+ # print("๋กœ์ปฌ ์‹œ์Šคํ…œ ChromeDriver ์‚ฌ์šฉ")
1039
+ # # # ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•์œผ๋กœ ChromeDriver ์‹œ๋„
1040
+ # # driver_created = False
1041
+
1042
+ # # # ๋ฐฉ๋ฒ• 1: webdriver-manager ์‚ฌ์šฉ
1043
+ # # try:
1044
+ # # from webdriver_manager.chrome import ChromeDriverManager
1045
+ # # from selenium.webdriver.chrome.service import Service
1046
+
1047
+ # # service = Service(ChromeDriverManager().install())
1048
+ # # self.driver = webdriver.Chrome(service=service, options=options)
1049
+ # # print("webdriver-manager๋กœ ChromeDriver ์ž๋™ ์„ค์น˜ ์„ฑ๊ณต")
1050
+ # # driver_created = True
1051
+
1052
+ # # except Exception as wm_error:
1053
+ # # print(f"webdriver-manager ์‹คํŒจ: {wm_error}")
1054
+
1055
+ # # # ๋ฐฉ๋ฒ• 2: ์‹œ์Šคํ…œ ChromeDriver ์‚ฌ์šฉ
1056
+ # # if not driver_created:
1057
+ # # try:
1058
+ # # self.driver = webdriver.Chrome(options=options)
1059
+ # # print("์‹œ์Šคํ…œ ChromeDriver ์‚ฌ์šฉ ์„ฑ๊ณต")
1060
+ # # driver_created = True
1061
+ # # except Exception as sys_error:
1062
+ # # print(f"์‹œ์Šคํ…œ ChromeDriver ์‹คํŒจ: {sys_error}")
1063
+
1064
+ # # if not driver_created:
1065
+ # # print("๋ชจ๋“  ChromeDriver ์ดˆ๊ธฐํ™” ๋ฐฉ๋ฒ• ์‹คํŒจ")
1066
+ # # return False
1067
+
1068
+ # # ํƒ€์ž„์•„์›ƒ ์„ค์ • (์†๋„ ์ตœ์ ํ™”)
1069
+ # self.driver.set_page_load_timeout(15) # ํŽ˜์ด์ง€ ๋กœ๋”ฉ ํƒ€์ž„์•„์›ƒ 15์ดˆ
1070
+ # self.driver.implicitly_wait(2) # ์š”์†Œ ๋Œ€๊ธฐ ์‹œ๊ฐ„ 2์ดˆ๋กœ ๋‹จ์ถ•
1071
+
1072
+ # # ๋ด‡ ๊ฐ์ง€ ๋ฐฉ์ง€ ์Šคํฌ๋ฆฝํŠธ
1073
+ # try:
1074
+ # self.driver.execute_script("""
1075
+ # Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
1076
+ # Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]});
1077
+ # Object.defineProperty(navigator, 'languages', {get: () => ['ko-KR', 'ko', 'en']});
1078
+ # """)
1079
+ # except Exception as script_error:
1080
+ # print(f"๋ด‡ ๊ฐ์ง€ ๋ฐฉ์ง€ ์Šคํฌ๋ฆฝํŠธ ์‹คํŒจ (๋ฌด์‹œ): {script_error}")
1081
+
1082
+ # return True
1083
+
1084
+ # except Exception as e:
1085
+ # print(f"๋“œ๋ผ์ด๋ฒ„ ์„ค์ • ์ตœ์ข… ์‹คํŒจ: {e}")
1086
+ # return False
1087
+
1088
+ # # ๋น ๋ฅด๊ฒŒ Element ์ฐพ๊ธฐ, Timeout ๋‹จ์ถ• ๊ณ ๋ ค
1089
+ # def safe_find_element(self, selectors: list, timeout: int = 3):
1090
+ # for selector_type, selector in selectors:
1091
+ # try:
1092
+ # if selector_type == "xpath":
1093
+ # element = WebDriverWait(self.driver, timeout).until(
1094
+ # EC.presence_of_element_located((By.XPATH, selector))
1095
+ # )
1096
+ # elif selector_type == "css":
1097
+ # element = WebDriverWait(self.driver, timeout).until(
1098
+ # EC.presence_of_element_located((By.CSS_SELECTOR, selector))
1099
+ # )
1100
+ # elif selector_type == "text":
1101
+ # element = WebDriverWait(self.driver, timeout).until(
1102
+ # EC.presence_of_element_located((By.XPATH, f"//*[contains(text(), '{selector}')]"))
1103
+ # )
1104
+
1105
+ # if element:
1106
+ # return element
1107
+ # except:
1108
+ # continue
1109
+ # return None
1110
+
1111
+ # # ์ฆ‰์‹œ ํด๋ฆญ
1112
+ # def safe_click(self, selectors: list, timeout: int = 3) -> bool:
1113
+ # element = self.safe_find_element(selectors, timeout)
1114
+ # if element:
1115
+ # try:
1116
+ # # JavaScript ํด๋ฆญ ์šฐ์„  ์‹œ๋„ (๋” ๋น ๋ฆ„)
1117
+ # self.driver.execute_script("arguments[0].click();", element)
1118
+ # return True
1119
+ # except:
1120
+ # try:
1121
+ # element.click()
1122
+ # return True
1123
+ # except:
1124
+ # return False
1125
+ # return False
1126
+
1127
+ # # ๋ณ‘๋ ฌ๋กœ ์„ค์ • ๋ณ€๊ฒฝ ์‹œ๋„
1128
+ # def parallel_change_settings(self, region: str, period: str, category: str, progress_callback=None) -> dict:
1129
+ # results = {
1130
+ # 'region': False,
1131
+ # 'period': False,
1132
+ # 'category': False,
1133
+ # 'errors': []
1134
+ # }
1135
+
1136
+ # # ์ˆœ์ฐจ์ ์œผ๋กœ ๋ณ€๊ฒฝ (๋ณ‘๋ ฌ์€ selenium์—์„œ ์•ˆ์ „ํ•˜์ง€ ์•Š์Œ)
1137
+ # try:
1138
+ # if region != "๋Œ€ํ•œ๋ฏผ๊ตญ":
1139
+ # if progress_callback:
1140
+ # progress_callback("์ง€์—ญ ์„ค์ • ๋ณ€๊ฒฝ ์ค‘...")
1141
+ # results['region'] = self.change_region(region)
1142
+ # time.sleep(0.5) # ์ตœ์†Œ ๋Œ€๊ธฐ
1143
+ # else:
1144
+ # results['region'] = True
1145
+
1146
+ # if period != "์ง€๋‚œ 24์‹œ๊ฐ„":
1147
+ # if progress_callback:
1148
+ # progress_callback("๊ธฐ๊ฐ„ ์„ค์ • ๋ณ€๊ฒฝ ์ค‘...")
1149
+ # results['period'] = self.change_time_period(period)
1150
+ # time.sleep(0.5) # ์ตœ์†Œ ๋Œ€๊ธฐ
1151
+ # else:
1152
+ # results['period'] = True
1153
+
1154
+ # if category != "๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ":
1155
+ # if progress_callback:
1156
+ # progress_callback("์นดํ…Œ๊ณ ๋ฆฌ ์„ค์ • ๋ณ€๊ฒฝ ์ค‘...")
1157
+ # results['category'] = self.change_category(category)
1158
+ # time.sleep(0.5) # ์ตœ์†Œ ๋Œ€๊ธฐ
1159
+ # else:
1160
+ # results['category'] = True
1161
+
1162
+ # except Exception as e:
1163
+ # results['errors'].append(str(e))
1164
+
1165
+ # return results
1166
+
1167
+ # # ์ง€์—ญ ๋ณ€๊ฒฝ
1168
+ # def change_region(self, target_region: str) -> bool:
1169
+ # try:
1170
+ # region_selectors = [
1171
+ # ("xpath", "//button[@aria-label='๋Œ€ํ•œ๋ฏผ๊ตญ, ์œ„์น˜ ์„ ํƒ']"),
1172
+ # ("xpath", "//button[contains(@aria-label, '์œ„์น˜ ์„ ํƒ')]"),
1173
+ # ("xpath", "//span[contains(text(), '๋Œ€ํ•œ๋ฏผ๊ตญ')]//parent::button"),
1174
+ # ]
1175
+
1176
+ # if not self.safe_click(region_selectors):
1177
+ # return False
1178
+
1179
+ # time.sleep(1) # ๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋‹จ์ถ•
1180
+
1181
+ # region_mapping = {
1182
+ # "์ „์„ธ๊ณ„": ["์ „ ์„ธ๊ณ„", "Worldwide"],
1183
+ # "๋ฏธ๊ตญ": ["๋ฏธ๊ตญ", "United States"],
1184
+ # "์ผ๋ณธ": ["์ผ๋ณธ", "Japan"],
1185
+ # "์ค‘๊ตญ": ["์ค‘๊ตญ", "China"]
1186
+ # }
1187
+
1188
+ # if target_region in region_mapping:
1189
+ # for region_text in region_mapping[target_region]:
1190
+ # region_option_selectors = [
1191
+ # ("xpath", f"//span[contains(text(), '{region_text}')]"),
1192
+ # ("xpath", f"//*[contains(text(), '{region_text}')]"),
1193
+ # ]
1194
+
1195
+ # if self.safe_click(region_option_selectors, timeout=2):
1196
+ # return True
1197
+
1198
+ # return False
1199
+
1200
+ # except Exception as e:
1201
+ # print(f"์ง€์—ญ ๋ณ€๊ฒฝ ์‹คํŒจ: {e}")
1202
+ # return False
1203
+
1204
+ # # ๊ธฐ๊ฐ„ ๋ณ€๊ฒฝ
1205
+ # def change_time_period(self, target_period: str) -> bool:
1206
+ # try:
1207
+ # period_selectors = [
1208
+ # ("xpath", "//button[contains(@aria-label, '๊ธฐ๊ฐ„ ์„ ํƒ')]"),
1209
+ # ("xpath", "//span[contains(text(), '์ง€๋‚œ 24์‹œ๊ฐ„')]//parent::button"),
1210
+ # ]
1211
+
1212
+ # if not self.safe_click(period_selectors):
1213
+ # return False
1214
+
1215
+ # time.sleep(1) # ๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋‹จ์ถ•
1216
+
1217
+ # period_mapping = {
1218
+ # "์ง€๋‚œ 1์‹œ๊ฐ„": ["์ง€๋‚œ 1์‹œ๊ฐ„"],
1219
+ # "์ง€๋‚œ 4์‹œ๊ฐ„": ["์ง€๋‚œ 4์‹œ๊ฐ„"],
1220
+ # "์ง€๋‚œ 1์ผ": ["์ง€๋‚œ 1์ผ"],
1221
+ # "์ง€๋‚œ 7์ผ": ["์ง€๋‚œ 7์ผ", "์ง€๋‚œ ์ฃผ"]
1222
+ # }
1223
+
1224
+ # if target_period in period_mapping:
1225
+ # for period_text in period_mapping[target_period]:
1226
+ # period_option_selectors = [
1227
+ # ("xpath", f"//span[contains(text(), '{period_text}')]"),
1228
+ # ("xpath", f"//*[contains(text(), '{period_text}')]"),
1229
+ # ]
1230
+
1231
+ # if self.safe_click(period_option_selectors, timeout=2):
1232
+ # return True
1233
+
1234
+ # return False
1235
+
1236
+ # except Exception as e:
1237
+ # print(f"๊ธฐ๊ฐ„ ๋ณ€๊ฒฝ ์‹คํŒจ: {e}")
1238
+ # return False
1239
+
1240
+ # # ์นดํ…Œ๊ณ ๋ฆฌ ๋ณ€๊ฒฝ
1241
+ # def change_category(self, target_category: str) -> bool:
1242
+ # try:
1243
+ # category_selectors = [
1244
+ # ("xpath", "//button[contains(@aria-label, '์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ')]"),
1245
+ # ("xpath", "//span[contains(text(), '๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ')]//parent::button"),
1246
+ # ]
1247
+
1248
+ # if not self.safe_click(category_selectors):
1249
+ # return False
1250
+
1251
+ # time.sleep(1) # ๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋‹จ์ถ•
1252
+
1253
+ # category_mapping = {
1254
+ # "๊ฒŒ์ž„": ["๊ฒŒ์ž„"],
1255
+ # "๊ฑด๊ฐ•": ["๊ฑด๊ฐ•"],
1256
+ # "๊ธฐ์ˆ ": ["์ปดํ“จํ„ฐ ๋ฐ ์ „์ž์ œํ’ˆ", "๊ธฐ์ˆ "],
1257
+ # "์Šคํฌ์ธ ": ["์Šคํฌ์ธ "],
1258
+ # "์—”ํ„ฐํ…Œ์ธ๋จผํŠธ": ["์˜ˆ์ˆ  ๋ฐ ์—”ํ„ฐํ…Œ์ธ๋จผํŠธ", "์—”ํ„ฐํ…Œ์ธ๋จผํŠธ"],
1259
+ # "๋‰ด์Šค": ["๋‰ด์Šค"],
1260
+ # "๋น„์ฆˆ๋‹ˆ์Šค": ["๋น„์ฆˆ๋‹ˆ์Šค"]
1261
+ # }
1262
+
1263
+ # if target_category in category_mapping:
1264
+ # for category_text in category_mapping[target_category]:
1265
+ # category_option_selectors = [
1266
+ # ("xpath", f"//span[contains(text(), '{category_text}')]"),
1267
+ # ("xpath", f"//*[contains(text(), '{category_text}')]"),
1268
+ # ]
1269
+
1270
+ # if self.safe_click(category_option_selectors, timeout=2):
1271
+ # return True
1272
+
1273
+ # return False
1274
+
1275
+ # except Exception as e:
1276
+ # print(f"์นดํ…Œ๊ณ ๋ฆฌ ๋ณ€๊ฒฝ ์‹คํŒจ: {e}")
1277
+ # return False
1278
+
1279
+ # # Google Trends ์บก์ฒ˜
1280
+ # def capture_trends(self, region: str, period: str, category: str, progress_callback=None) -> Tuple[Optional[str], bool, str]:
1281
+ # error_msg = ""
1282
+
1283
+ # try:
1284
+ # if progress_callback:
1285
+ # progress_callback("๋ธŒ๋ผ์šฐ์ € ๋“œ๋ผ์ด๋ฒ„ ์ดˆ๊ธฐํ™” ์ค‘...")
1286
+
1287
+ # # ๋“œ๋ผ์ด๋ฒ„ ์„ค์ •
1288
+ # if not self.setup_driver():
1289
+ # return None, False, "๋ธŒ๋ผ์šฐ์ € ๋“œ๋ผ์ด๋ฒ„ ์„ค์ • ์‹คํŒจ"
1290
+
1291
+ # if progress_callback:
1292
+ # progress_callback("Google Trends ํŽ˜์ด์ง€ ์ ‘์† ์ค‘...")
1293
+
1294
+ # # Google Trends ์ ‘์† (ํ•œ๊ตญ์–ด ์ง์ ‘ URL)
1295
+ # self.driver.get("https://trends.google.com/trending?geo=KR&hl=ko")
1296
+
1297
+ # # ์ตœ์†Œํ•œ์˜ ๋กœ๋”ฉ ๋Œ€๊ธฐ (1์ดˆ๋กœ ๋‹จ์ถ•)
1298
+ # time.sleep(1)
1299
+
1300
+ # if progress_callback:
1301
+ # progress_callback("ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์™„๋ฃŒ, ์„ค์ • ๋ณ€๊ฒฝ ์‹œ์ž‘...")
1302
+
1303
+ # # ํŽ˜์ด์ง€ ์ค€๋น„ ํ™•์ธ (๋น ๋ฅธ ์ฒดํฌ)
1304
+ # try:
1305
+ # WebDriverWait(self.driver, 5).until(
1306
+ # EC.presence_of_element_located((By.TAG_NAME, "button"))
1307
+ # )
1308
+ # except TimeoutException:
1309
+ # pass # ๊ณ„์† ์ง„ํ–‰
1310
+
1311
+ # # ์„ค์ • ๋ณ€๊ฒฝ (๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ)
1312
+ # settings_result = self.parallel_change_settings(region, period, category, progress_callback)
1313
+
1314
+ # # ๊ฒฐ๊ณผ ํ™•์ธ
1315
+ # changes_made = []
1316
+ # if settings_result['region']:
1317
+ # changes_made.append(f"์ง€์—ญ: {region}")
1318
+ # if settings_result['period']:
1319
+ # changes_made.append(f"๊ธฐ๊ฐ„: {period}")
1320
+ # if settings_result['category']:
1321
+ # changes_made.append(f"์นดํ…Œ๊ณ ๋ฆฌ: {category}")
1322
+
1323
+ # if settings_result['errors']:
1324
+ # error_msg = "\n".join(settings_result['errors'])
1325
+
1326
+ # if progress_callback:
1327
+ # progress_callback("์„ค์ • ๋ณ€๊ฒฝ ์™„๋ฃŒ, ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๋Š”์ค‘...")
1328
+
1329
+ # # ์ตœ์ข… ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ๋Œ€๊ธฐ (1์ดˆ๋กœ ๋‹จ์ถ•)
1330
+ # time.sleep(1)
1331
+
1332
+ # # ์Šคํฌ๋ฆฐ์ƒท ์ดฌ์˜ (ํ•„์š”ํ•œ ์˜์—ญ๋งŒ)
1333
+ # screenshot = self.driver.get_screenshot_as_png()
1334
+ # screenshot_b64 = base64.b64encode(screenshot).decode()
1335
+
1336
+ # success_msg = f"์„ค์ • ์™„๋ฃŒ: {', '.join(changes_made) if changes_made else '๊ธฐ๋ณธ ์„ค์ • ์‚ฌ์šฉ'}"
1337
+
1338
+ # return screenshot_b64, True, success_msg + ("\n" + error_msg if error_msg else "")
1339
+
1340
+ # except Exception as e:
1341
+ # error_msg = f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
1342
+ # return None, False, error_msg
1343
+
1344
+ # finally:
1345
+ # if self.driver:
1346
+ # self.driver.quit()
1347
+
1348
+ # # Claude API๋กœ ์Šคํฌ๋ฆฐ์ƒท ๋ถ„์„
1349
+ # def analyze_with_claude(screenshot_b64: str, region: str, period: str, category: str) -> str:
1350
+ # try:
1351
+ # # API ํ‚ค ์ฒดํฌ
1352
+ # if not ANTHROPIC_API_KEY or ANTHROPIC_API_KEY == "your_api_key_here":
1353
+ # return """
1354
+ # <div style="color: red; padding: 20px; border: 1px solid red; border-radius: 5px;">
1355
+ # <h3>API ํ‚ค ์˜ค๋ฅ˜</h3>
1356
+ # <p><strong>ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!</strong></p>
1357
+ # </div>
1358
+ # """
1359
+
1360
+ # # Claude ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ
1361
+ # claude_client = create_claude_client()
1362
+
1363
+ # if claude_client is None:
1364
+ # return """
1365
+ # <div style="color: red; padding: 20px; border: 1px solid red; border-radius: 5px;">
1366
+ # <h3>Claude API ์—ฐ๊ฒฐ ์‹คํŒจ</h3>
1367
+ # <p>API ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.</p>
1368
+ # </div>
1369
+ # """
1370
+
1371
+ # # ๊ฐ„๊ฒฐํ•œ ํ”„๋กฌํ”„ํŠธ (์†๋„ ์ตœ์ ํ™”)
1372
+ # prompt = f"""
1373
+ # ์ด Google Trends ์Šคํฌ๋ฆฐ์ƒท์„ ๋ถ„์„ํ•ด์ฃผ์„ธ์š”.
1374
+ # ์„ค์ •: {region} | {period} | {category}
1375
+
1376
+ # ๋‹ค์Œ์„ HTML ํ‘œ ํ˜•ํƒœ๋กœ ์ •ํ™•ํ•˜๊ฒŒ ์ •๋ฆฌํ•ด์ฃผ์„ธ์š”:
1377
+ # 1. ์‹ค์‹œ๊ฐ„ ์ธ๊ธฐ ๊ฒ€์ƒ‰์–ด ์ƒ์œ„ 10๊ฐœ (์ˆœ์œ„, ๊ฒ€์ƒ‰์–ด, ๊ฒ€์ƒ‰๋Ÿ‰, ์ƒ์Šน๋ฅ )
1378
+ # 2. ์ฃผ์š” ํŠธ๋ Œ๋“œ ํ‚ค์›Œ๋“œ 3-5๊ฐœ์˜ ํŠน์ง• ์„ค๋ช…
1379
+ # 3. ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ํŠน์ด์‚ฌํ•ญ (์žˆ๋Š” ๊ฒฝ์šฐ)
1380
+
1381
+ # HTML ํ˜•ํƒœ๋กœ ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌํ•˜๊ณ , ์ด๋ชจ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์‹œ๊ฐ์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.
1382
+ # ํ•œ๊ธ€ ํ…์ŠคํŠธ๋ฅผ ์ •ํ™•ํžˆ ์ฝ์–ด์„œ ๋ถ„์„ํ•ด์ฃผ์„ธ์š”.
1383
+ # """
1384
+
1385
+ # # Claude API ํ˜ธ์ถœ (๋น ๋ฅธ ์„ค์ •)
1386
+ # message = claude_client.messages.create(
1387
+ # model="claude-3-5-sonnet-20241022",
1388
+ # max_tokens=1000, # ํ† ํฐ ์ˆ˜ ์ค„์—ฌ์„œ ์†๋„ ํ–ฅ์ƒ
1389
+ # messages=[
1390
+ # {
1391
+ # "role": "user",
1392
+ # "content": [
1393
+ # {
1394
+ # "type": "text",
1395
+ # "text": prompt
1396
+ # },
1397
+ # {
1398
+ # "type": "image",
1399
+ # "source": {
1400
+ # "type": "base64",
1401
+ # "media_type": "image/png",
1402
+ # "data": screenshot_b64
1403
+ # }
1404
+ # }
1405
+ # ]
1406
+ # }
1407
+ # ]
1408
+ # )
1409
+
1410
+ # return message.content[0].text
1411
+
1412
+ # except Exception as e:
1413
+ # return f"""
1414
+ # <div style="color: red; padding: 20px; border: 1px solid red; border-radius: 5px;">
1415
+ # <h3>Claude API ๋ถ„์„ ์‹คํŒจ</h3>
1416
+ # <p><strong>์˜ค๋ฅ˜:</strong> {str(e)}</p>
1417
+ # </div>
1418
+ # """
1419
+
1420
+ # def main_analysis(region: str, period: str, category: str):
1421
+ # """์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ถ„์„ ํ•จ์ˆ˜ (Generator)"""
1422
+
1423
+ # start_time = time.time()
1424
+
1425
+ # # 1๋‹จ๊ณ„: ์‹œ์ž‘ ๋ฉ”์‹œ์ง€
1426
+ # yield f"""
1427
+ # <div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
1428
+ # <h3>Google Trends ๋ถ„์„ ์‹œ์ž‘!</h3>
1429
+ # <p><strong>์„ค์ •:</strong> {region} | {period} | {category}</p>
1430
+ # <p>์‹ค์‹œ๊ฐ„์œผ๋กœ ์ง„ํ–‰ ์ƒํ™ฉ์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค...</p>
1431
+ # </div>
1432
+ # """
1433
+
1434
+ # progress_status = {"current": ""}
1435
+
1436
+ # def progress_callback(message):
1437
+ # progress_status["current"] = message
1438
+
1439
+ # try:
1440
+ # # 2๋‹จ๊ณ„: API ํ‚ค ํ™•์ธ
1441
+ # if not ANTHROPIC_API_KEY or ANTHROPIC_API_KEY == "your_api_key_here":
1442
+ # yield """
1443
+ # <div style="color: red; padding: 20px; border: 1px solid red; border-radius: 5px;">
1444
+ # <h3> API ํ‚ค ๋ˆ„๋ฝ</h3>
1445
+ # <p><strong>ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค!</strong></p>
1446
+ # <p><strong>ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:</strong> .env ํŒŒ์ผ์— API ํ‚ค๋ฅผ ์„ค์ •ํ•˜๊ณ  ์•ฑ์„ ์žฌ์‹œ์ž‘ํ•˜์„ธ์š”.</p>
1447
+ # </div>
1448
+ # """
1449
+ # return
1450
+
1451
+ # # 3๋‹จ๊ณ„: ๋ธŒ๋ผ์šฐ์ € ์‹คํ–‰ ์•Œ๋ฆผ
1452
+ # yield f"""
1453
+ # <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
1454
+ # <h4> 1๋‹จ๊ณ„: ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค! ์ž…๋ ฅํ•˜์‹  ์กฐ๊ฑด์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค...</h4>
1455
+ # <p>Connect Start...</p>
1456
+ # </div>
1457
+
1458
+ # <div style="padding: 15px; background-color: #f8f9fa; border-radius: 5px; margin-bottom: 15px;">
1459
+ # <p> <strong>์ง„ํ–‰ ์ค‘:</strong> ๋ธŒ๋ผ์šฐ์ € ๋“œ๋ผ์ด๋ฒ„ ์„ค์ • ๋ฐ ํŽ˜์ด์ง€ ๋กœ๋”ฉ...</p>
1460
+ # </div>
1461
+ # """
1462
+
1463
+ # # 4๋‹จ๊ณ„: Google Trends ์ž๋™ํ™” ์‹คํ–‰
1464
+ # print(f" ๋ถ„์„ ์‹œ์ž‘: {region} | {period} | {category}")
1465
+ # automator = GoogleTrendsAutomator()
1466
+
1467
+ # # 5๋‹จ๊ณ„: ์„ค์ • ๋ณ€๊ฒฝ ์•Œ๋ฆผ
1468
+ # if region != "๋Œ€ํ•œ๋ฏผ๊ตญ" or period != "์ง€๋‚œ 24์‹œ๊ฐ„" or category != "๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ":
1469
+ # yield f"""
1470
+ # <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
1471
+ # <h4>์ž๋™ํ™” ์‹œ์ž‘</h4>
1472
+ # <p>In Running...</p>
1473
+ # </div>
1474
+
1475
+ # <div style="padding: 15px; background-color: #e3f2fd; border-radius: 5px; margin-bottom: 15px;">
1476
+ # <p><strong>์„ค์ • ๋ณ€๊ฒฝ ์ค‘:</strong></p>
1477
+ # <ul style="margin: 10px 0;">
1478
+ # {"<li>์ง€์—ญ: " + region + "</li>" if region != "๋Œ€ํ•œ๋ฏผ๊ตญ" else ""}
1479
+ # {"<li>๊ธฐ๊ฐ„: " + period + "</li>" if period != "์ง€๋‚œ 24์‹œ๊ฐ„" else ""}
1480
+ # {"<li>์นดํ…Œ๊ณ ๋ฆฌ: " + category + "</li>" if category != "๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ" else ""}
1481
+ # </ul>
1482
+ # </div>
1483
+ # """
1484
+
1485
+ # # ๋ธŒ๋ผ์šฐ์ € ์ž‘์—…์„ ๋ณ„๋„ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰
1486
+ # with ThreadPoolExecutor(max_workers=1) as executor:
1487
+ # future = executor.submit(
1488
+ # automator.capture_trends,
1489
+ # region, period, category, progress_callback
1490
+ # )
1491
+
1492
+ # # ์ง„ํ–‰ ์ƒํ™ฉ ์—…๋ฐ์ดํŠธ
1493
+ # while not future.done():
1494
+ # current_progress = progress_status["current"]
1495
+ # if current_progress:
1496
+ # elapsed = time.time() - start_time
1497
+ # yield f"""
1498
+ # <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
1499
+ # <h4>์ž๋™ํ™” ์‹œ์ž‘</h4>
1500
+ # <p>In Running...</p>
1501
+ # </div>
1502
+
1503
+ # <div style="padding: 15px; background-color: #f8f9fa; border-radius: 5px; margin-bottom: 15px;">
1504
+ # <p><strong>์ง„ํ–‰ ์ค‘:</strong> {current_progress}</p>
1505
+ # <p><strong>๊ฒฝ๊ณผ ์‹œ๊ฐ„:</strong> {elapsed:.1f}์ดˆ</p>
1506
+ # </div>
1507
+ # """
1508
+ # time.sleep(0.5)
1509
+
1510
+ # # ๊ฒฐ๊ณผ ๋ฐ›๊ธฐ
1511
+ # screenshot_b64, success, status_msg = future.result()
1512
+
1513
+ # browser_time = time.time() - start_time
1514
+
1515
+ # if not success:
1516
+ # yield f"""
1517
+ # <div style="color: red; padding: 20px; border: 1px solid red; border-radius: 5px;">
1518
+ # <h3>Google Trends ์ ‘์† ์‹คํŒจ</h3>
1519
+ # <pre style="background-color: #fff5f5; padding: 15px; border-radius: 5px; margin: 15px 0;">
1520
+ # {status_msg}
1521
+ # </pre>
1522
+ # <p><strong>ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:</strong> Chrome ๋ธŒ๋ผ์šฐ์ €์™€ ChromeDriver๋ฅผ ์„ค์น˜ํ•˜๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•˜์„ธ์š”.</p>
1523
+ # </div>
1524
+ # """
1525
+ # return
1526
+
1527
+ # # 6๋‹จ๊ณ„: ์Šคํฌ๋ฆฐ์ƒท ์™„๋ฃŒ ๋ฐ AI ๋ถ„์„ ์‹œ์ž‘
1528
+ # yield f"""
1529
+ # <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
1530
+ # <h4>2๋‹จ๊ณ„: ์ž…๋ ฅํ•˜์‹  ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ–ˆ๊ณ , ์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค...</h4>
1531
+ # <p>Google Trends ๋ฐ์ดํ„ฐ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์บก์ฒ˜ํ–ˆ์Šต๋‹ˆ๋‹ค.</p>
1532
+ # </div>
1533
+
1534
+ # <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #FF6B6B 0%, #FF8E8E 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
1535
+ # <h4>3๋‹จ๊ณ„: AI ๋ถ„์„ ์ง„ํ–‰ ์ค‘...</h4>
1536
+ # <p>Claude AI๊ฐ€ ํŠธ๋ Œ๋“œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.</p>
1537
+ # <p style="font-size: 14px; opacity: 0.9;">์•ฝ 10-20์ดˆ ์†Œ์š” ์˜ˆ์ƒ</p>
1538
+ # </div>
1539
+ # """
1540
+
1541
+ # # 7๋‹จ๊ณ„: Claude API ๋ถ„์„
1542
+ # ai_start_time = time.time()
1543
+ # print("์‹ค์‹œ๊ฐ„ ์ธ๊ธฐ ๊ฒ€์ƒ‰์–ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™”๊ณ , Claude ๋ถ„์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค...")
1544
+
1545
+ # # AI ๋ถ„์„์„ ๋ณ„๋„ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰
1546
+ # with ThreadPoolExecutor(max_workers=1) as executor:
1547
+ # ai_future = executor.submit(
1548
+ # analyze_with_claude,
1549
+ # screenshot_b64, region, period, category
1550
+ # )
1551
+
1552
+ # # AI ๋ถ„์„ ์ง„ํ–‰ ์ƒํ™ฉ ํ‘œ์‹œ
1553
+ # while not ai_future.done():
1554
+ # ai_elapsed = time.time() - ai_start_time
1555
+ # total_elapsed = time.time() - start_time
1556
+ # yield f"""
1557
+ # <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
1558
+ # <h4>๐Ÿ“ธ 2๋‹จ๊ณ„: ์ž…๋ ฅํ•˜์‹  ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ–ˆ๊ณ , ์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค...</h4>
1559
+ # <p>Google Trends ๋ฐ์ดํ„ฐ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์บก์ฒ˜ํ–ˆ์Šต๋‹ˆ๋‹ค.</p>
1560
+ # </div>
1561
+
1562
+ # <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #FF6B6B 0%, #FF8E8E 100%); color: white; border-radius: 10px; margin-bottom: 15px;">
1563
+ # <h4>3๋‹จ๊ณ„: AI ๋ถ„์„ ์ง„ํ–‰ ์ค‘...</h4>
1564
+ # <p>Claude AI๊ฐ€ ํŠธ๋ Œ๋“œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ({ai_elapsed:.1f}์ดˆ)</p>
1565
+ # <p style="font-size: 14px; opacity: 0.9;">์ด ๊ฒฝ๊ณผ ์‹œ๊ฐ„: {total_elapsed:.1f}์ดˆ</p>
1566
+ # </div>
1567
+ # """
1568
+ # time.sleep(0.5)
1569
+
1570
+ # analysis_result = ai_future.result()
1571
+
1572
+ # ai_time = time.time() - ai_start_time
1573
+ # total_time = time.time() - start_time
1574
+
1575
+ # # 8๋‹จ๊ณ„: ์ตœ์ข… ๊ฒฐ๊ณผ ์ถœ๋ ฅ
1576
+ # yield f"""
1577
+ # <div style="text-align: center; padding: 15px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
1578
+ # <h3> ๋ถ„์„ ์™„๋ฃŒ!</h3>
1579
+ # <p><strong>์„ค์ •:</strong> {region} | {period} | {category}</p>
1580
+ # <p>๋ชจ๋“  ๋‹จ๊ณ„๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.</p>
1581
+ # </div>
1582
+
1583
+ # {analysis_result}
1584
+
1585
+ # <div style="margin-top: 20px; padding: 15px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; text-align: center;">
1586
+ # <p style="margin: 0; font-size: 14px;">
1587
+ # <strong>๋‹ค๋ฅธ ์„ค์ •์œผ๋กœ ๋‹ค์‹œ ๋ถ„์„ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?</strong><br>
1588
+ # ์œ„์˜ ์„ค์ •์„ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‹ค์‹œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”!
1589
+ # </p>
1590
+ # </div>
1591
+
1592
+ # <div style="margin-top: 15px; padding: 10px; background-color: #f9f9f9; border-radius: 5px; font-size: 12px; color: #666; text-align: center;">
1593
+ # <p style="margin: 5px 0;"> <strong>๋ถ„์„ ์™„๋ฃŒ ์‹œ๊ฐ„:</strong> ์•ฝ 15-30์ดˆ</p>
1594
+ # <p style="margin: 5px 0;"> <strong>AI ์—”์ง„:</strong> Claude 3.5 Sonnet</p>
1595
+ # <p style="margin: 5px 0;"> <strong>๋ฐ์ดํ„ฐ ์ถœ์ฒ˜:</strong> Google Trends ์‹ค์‹œ๊ฐ„ ์Šคํฌ๋ฆฐ์ƒท</p>
1596
+ # </div>
1597
+ # """
1598
+
1599
+ # print("์ „์ฒด ๋ถ„์„ ํ”„๋กœ์„ธ์Šค ์™„๋ฃŒ!")
1600
+
1601
+ # except Exception as e:
1602
+ # error_details = traceback.format_exc()
1603
+ # print(f"๋ฉ”์ธ ๋ถ„์„ ์ค‘ ์˜ˆ์™ธ ๋ฐœ์ƒ: {e}")
1604
+
1605
+ # yield f"""
1606
+ # <div style="color: red; padding: 20px; border: 1px solid red; border-radius: 5px;">
1607
+ # <h3>์˜ค๋ฅ˜ ๋ฐœ์ƒ</h3>
1608
+ # <p><strong>์˜ค๋ฅ˜:</strong> {str(e)}</p>
1609
+ # <details>
1610
+ # <summary>์ƒ์„ธ ์˜ค๋ฅ˜ ์ •๋ณด (ํด๋ฆญํ•˜์—ฌ ํŽผ์น˜๊ธฐ)</summary>
1611
+ # <pre style="background-color: #f5f5f5; padding: 10px; margin-top: 10px; overflow-x: auto; font-size: 11px;">
1612
+ # {error_details}
1613
+ # </pre>
1614
+ # </details>
1615
+ # <p><strong>ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:</strong> ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๊ณ  ๋‹ค์‹œ ์‹œ๋„ํ•˜์„ธ์š”.</p>
1616
+ # </div>
1617
+ # """
1618
+
1619
+ # # Gradio ์ธํ„ฐํŽ˜์ด์Šค (์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ํ˜•ํƒœ๋กœ)
1620
+ # def create_interface():
1621
+
1622
+ # with gr.Blocks(
1623
+ # theme=gr.themes.Soft(),
1624
+ # title="Google Trends ์‹ค์‹œ๊ฐ„ ๋ถ„์„๊ธฐ",
1625
+ # css="""
1626
+ # .gradio-container {
1627
+ # max-width: 1200px !important;
1628
+ # }
1629
+ # .output-html {
1630
+ # max-height: none !important;
1631
+ # }
1632
+ # """
1633
+ # ) as interface:
1634
+
1635
+ # # Header
1636
+ # gr.HTML("""
1637
+ # <div style="text-align: center; padding: 20px; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; margin-bottom: 20px;">
1638
+ # <h1>Google Trends ์‹ค์‹œ๊ฐ„ ๋ถ„์„๊ธฐ</h1>
1639
+ # <p>Google Trends์˜ ์‹ค์‹œ๊ฐ„ ์ธ๊ธฐ ๊ฒ€์ƒ‰์–ด๋ฅผ AI๊ฐ€ ์ž๋™์œผ๋กœ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค</p>
1640
+ # <p style="font-size: 14px; opacity: 0.9;">๊ฐœ์„ ๋œ ๋ฐฉ์‹์œผ๋กœ ๋‹จ๊ณ„๋ณ„ ์ง„ํ–‰์ƒํ™ฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>
1641
+ # </div>
1642
+ # """)
1643
+
1644
+ # # ์ž…๋ ฅ ์„น์…˜
1645
+ # with gr.Row():
1646
+ # with gr.Column(scale=1):
1647
+ # region_input = gr.Dropdown(
1648
+ # choices=["๋Œ€ํ•œ๋ฏผ๊ตญ", "์ „์„ธ๊ณ„", "๋ฏธ๊ตญ", "์ผ๋ณธ", "์ค‘๊ตญ"],
1649
+ # value="๋Œ€ํ•œ๋ฏผ๊ตญ",
1650
+ # label="์ง€์—ญ ์„ ํƒ",
1651
+ # info="๋ถ„์„ํ•  ์ง€์—ญ์„ ์„ ํƒํ•˜์„ธ์š”"
1652
+ # )
1653
+
1654
+ # with gr.Column(scale=1):
1655
+ # period_input = gr.Dropdown(
1656
+ # choices=["์ง€๋‚œ 24์‹œ๊ฐ„", "์ง€๋‚œ 1์‹œ๊ฐ„", "์ง€๋‚œ 4์‹œ๊ฐ„", "์ง€๋‚œ 1์ผ", "์ง€๋‚œ 7์ผ"],
1657
+ # value="์ง€๋‚œ 7์ผ",
1658
+ # label="๊ธฐ๊ฐ„ ์„ ํƒ",
1659
+ # info="ํŠธ๋ Œ๋“œ ๋ถ„์„ ๊ธฐ๊ฐ„์„ ์„ ํƒํ•˜์„ธ์š”"
1660
+ # )
1661
+
1662
+ # with gr.Column(scale=1):
1663
+ # category_input = gr.Dropdown(
1664
+ # choices=["๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ", "๊ฒŒ์ž„", "๊ฑด๊ฐ•", "๊ธฐ์ˆ ", "์Šคํฌ์ธ ", "์—”ํ„ฐํ…Œ์ธ๋จผํŠธ", "๋‰ด์Šค", "๋น„์ฆˆ๋‹ˆ์Šค"],
1665
+ # value="๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ",
1666
+ # label="์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ",
1667
+ # info="๊ด€์‹ฌ ๋ถ„์•ผ๋ฅผ ์„ ํƒํ•˜์„ธ์š”"
1668
+ # )
1669
+
1670
+ # # ๋ถ„์„ ๋ฒ„ํŠผ
1671
+ # analyze_btn = gr.Button(
1672
+ # "์‹ค์‹œ๊ฐ„ ํŠธ๋ Œ๋“œ ๋ถ„์„ ์‹œ์ž‘ (๋น ๋ฅธ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ชจ๋“œ)",
1673
+ # variant="primary",
1674
+ # size="lg"
1675
+ # )
1676
+
1677
+ # # ์ง„ํ–‰ ์ƒํ™ฉ ๋ฐ ๊ฒฐ๊ณผ ์ถœ๋ ฅ
1678
+ # output = gr.HTML(
1679
+ # label="์‹ค์‹œ๊ฐ„ ๋ถ„์„ ๊ฒฐ๊ณผ",
1680
+ # value="""
1681
+ # <div style="text-align: center; padding: 20px; border: 2px dashed #ccc; border-radius: 10px; color: #666;">
1682
+ # <h3>๋ถ„์„ ๋Œ€๊ธฐ ์ค‘</h3>
1683
+ # <p>์œ„์˜ ์„ค์ •์„ ์„ ํƒํ•˜๊ณ  '์‹ค์‹œ๊ฐ„ ํŠธ๋ Œ๋“œ ๋ถ„์„ ์‹œ์ž‘' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”!</p>
1684
+ # <p style="font-size: 14px;">๊ฐœ์„ ๋œ ๋ฐฉ์‹์œผ๋กœ <strong>15-30์ดˆ</strong>๋งŒ์— ๋ถ„์„์ด ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค.</p>
1685
+ # </div>
1686
+ # """
1687
+ # )
1688
+
1689
+ # # ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ (Generator ํ•จ์ˆ˜๋กœ ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ)
1690
+ # analyze_btn.click(
1691
+ # fn=main_analysis,
1692
+ # inputs=[region_input, period_input, category_input],
1693
+ # outputs=output,
1694
+ # show_progress="hidden" # ๋‚ด์žฅ ์ง„ํ–‰๋ฅ  ํ‘œ์‹œ ์ˆจ๊น€ (์ž์ฒด ์ŠคํŠธ๋ฆฌ๋ฐ ์‚ฌ์šฉ)
1695
+ # )
1696
+
1697
+ # # ์‚ฌ์šฉ๋ฒ• ์•ˆ๋‚ด
1698
+ # gr.HTML("""
1699
+ # <div style="margin-top: 30px; padding: 20px; background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); border-radius: 10px;">
1700
+ # <h3 style="margin-top: 0; color: #333;"> ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ!</h3>
1701
+ # <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; color: #555;">
1702
+ # <div>
1703
+ # <h4> ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ</h4>
1704
+ # <ul style="margin: 0; padding-left: 20px;">
1705
+ # <li>๋ธŒ๋ผ์šฐ์ € ์‹คํ–‰ ์ƒํƒœ ์‹ค์‹œ๊ฐ„ ํ‘œ์‹œ</li>
1706
+ # <li>์„ค์ • ๋ณ€๊ฒฝ ๊ณผ์ • ๋‹จ๊ณ„๋ณ„ ํ™•์ธ</li>
1707
+ # <li>AI ๋ถ„์„ ์ง„ํ–‰์ƒํ™ฉ ํ‘œ์‹œ</li>
1708
+ # </ul>
1709
+ # </div>
1710
+ # <div>
1711
+ # <h4> ์†๋„ ์ตœ์ ํ™”</h4>
1712
+ # <ul style="margin: 0; padding-left: 20px;">
1713
+ # <li>๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋‹จ์ถ• </li>
1714
+ # <li>๋ถˆํ•„์š”ํ•œ ๋กœ๋”ฉ ์‹œ๊ฐ„ ์ œ๊ฑฐ</li>
1715
+ # <li>ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ</li>
1716
+ # </ul>
1717
+ # </div>
1718
+ # </div>
1719
+ # </div>
1720
+ # """)
1721
+
1722
+ # # footer
1723
+ # gr.HTML("""
1724
+ # <div style="text-align: center; margin-top: 20px; padding: 15px; color: #666; border-top: 1px solid #eee;">
1725
+ # <p> <strong>์‚ฌ์šฉ๋ฒ•:</strong> ์›ํ•˜๋Š” ์„ค์ •์„ ์„ ํƒํ•˜๊ณ  ๋ถ„์„ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”</p>
1726
+ # <p> <strong>์†๋„:</strong> ์ ์ฐจ์ ์œผ๋กœ ๊ฐœ์„  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.</p>
1727
+ # <p> <strong>Powered by:</strong> Google Trends + Claude AI + ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ</p>
1728
+ # </div>
1729
+ # """)
1730
+
1731
+ # return interface
1732
+
1733
+ # # ๋ฉ”์ธ ์‹คํ–‰
1734
+ # if __name__ == "__main__":
1735
+ # print("=" * 50)
1736
+ # print("Google Trends ์‹ค์‹œ๊ฐ„ ๋ถ„์„๊ธฐ ์‹œ์ž‘!")
1737
+ # print("=" * 50)
1738
+
1739
+ # # API ํ‚ค ํ™•์ธ
1740
+ # if not ANTHROPIC_API_KEY or ANTHROPIC_API_KEY == "your_api_key_here":
1741
+ # print(" ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์Œ")
1742
+ # print("\n ์ด ์ƒํƒœ๋กœ๋„ ์•ฑ์€ ์‹คํ–‰๋˜์ง€๋งŒ, ๋ถ„์„ ๊ธฐ๋Šฅ์€ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค.")
1743
+ # print("=" * 50)
1744
+ # else:
1745
+ # print(" Claude API ํ‚ค ํ™•์ธ๋จ")
1746
+
1747
+ # # ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
1748
+ # print(" Gradio ์›น ์ธํ„ฐํŽ˜์ด์Šค ์ค€๋น„ ์ค‘...")
1749
+ # app = create_interface()
1750
+
1751
+ # # ํ™˜๊ฒฝ๋ณ„ ์‹คํ–‰ ์„ค์ •
1752
+ # if os.getenv("SPACE_ID"):
1753
+ # # Hugging Face Space ํ™˜๊ฒฝ
1754
+ # print(" Hugging Face Space ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ ์ค‘...")
1755
+ # app.launch(
1756
+ # server_name="0.0.0.0",
1757
+ # server_port=7860,
1758
+ # show_error=True,
1759
+ # show_api=False
1760
+ # )
1761
+ # else:
1762
+ # # ๋กœ์ปฌ ํ™˜๊ฒฝ
1763
+ # print(" ๋กœ์ปฌ ์„œ๋ฒ„ ์‹œ์ž‘ ์ค‘...")
1764
+ # print(" ๋ธŒ๋ผ์šฐ์ €์—์„œ http://localhost:7860 ์œผ๋กœ ์ ‘์†")
1765
+ # print(" ์ข…๋ฃŒํ•˜๋ ค๋ฉด Ctrl+C")
1766
+ # print("=" * 50)
1767
+
1768
+ # try:
1769
+ # app.launch(
1770
+ # server_name="127.0.0.1",
1771
+ # server_port=7860,
1772
+ # show_error=True,
1773
+ # show_api=False,
1774
+ # share=False, # ๋กœ์ปฌ์—์„œ๋Š” ๊ณต๊ฐœ ๋งํฌ ๋น„ํ™œ์„ฑํ™”
1775
+ # inbrowser=True, # ์ž๋™์œผ๋กœ ๋ธŒ๋ผ์šฐ์ € ์—ด๊ธฐ
1776
+ # quiet=False # ์ƒ์„ธ ๋กœ๊ทธ ํ‘œ์‹œ
1777
+ # )
1778
+ # except KeyboardInterrupt:
1779
+ # print("\n\nGoogle Trends ๋ถ„์„๊ธฐ๊ฐ€ ์ข…๋ฃŒ")
1780
+ # print("๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ")
1781
+ # except Exception as e:
1782
+ # print(f"\n ์„œ๋ฒ„ ์‹คํ–‰ ์ค‘ ์˜ค๋ฅ˜: {e}")
1783
+ # print("ํฌํŠธ 7860์ด ์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ์ง€ ํ™•์ธ")
packages.txt CHANGED
@@ -1,3 +1,5 @@
1
-
2
- chromium-browser
3
- chromium-chromedriver
 
 
 
1
+ wget
2
+ unzip
3
+ curl
4
+ gnupg
5
+ ca-certificates