HanJun commited on
Commit
1603d09
ยท
verified ยท
1 Parent(s): 6ce9424

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -203
app.py CHANGED
@@ -48,41 +48,34 @@ def get_claude_client():
48
  return anthropic.Anthropic(api_key=api_key)
49
 
50
  def install_browsers():
51
- """Playwright ๋ธŒ๋ผ์šฐ์ € ๋ฐ ํ•œ๊ธ€ ํฐํŠธ ์„ค์น˜"""
52
  try:
53
- print("Playwright ๋ธŒ๋ผ์šฐ์ € ์„ค์น˜ ์ค‘...")
54
- os.system("playwright install chromium")
55
-
56
- print("ํ•œ๊ธ€ ํฐํŠธ ์„ค์น˜ ์ค‘...")
57
- # ํ•œ๊ธ€ ํฐํŠธ ์„ค์น˜
58
- os.system("apt-get update")
59
- os.system("apt-get install -y fonts-nanum fonts-nanum-coding fonts-nanum-extra")
60
- os.system("fc-cache -fv")
61
-
62
- print("Playwright ๋ธŒ๋ผ์šฐ์ € ๋ฐ ํฐํŠธ ์„ค์น˜ ์™„๋ฃŒ!")
63
  return True
64
  except Exception as e:
65
- print(f"๋ธŒ๋ผ์šฐ์ €/ํฐํŠธ ์„ค์น˜ ์‹คํŒจ: {e}")
66
  return False
67
 
68
  def init_browser():
69
- """Playwright ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  Google Trends ํŽ˜์ด์ง€๋กœ ์ด๋™"""
70
  global browser, page, playwright_instance, is_browser_ready, browser_thread_id
71
 
72
  try:
73
- # ํ˜„์žฌ ์Šค๋ ˆ๋“œ ID ์ €์žฅ
74
  browser_thread_id = threading.current_thread().ident
75
- print(f"๋ธŒ๋ผ์šฐ์ € ์ดˆ๊ธฐํ™” ์Šค๋ ˆ๋“œ ID: {browser_thread_id}")
76
-
77
- print("Playwright ๋ธŒ๋ผ์šฐ์ € ์ดˆ๊ธฐํ™” ์ค‘...")
78
 
79
- # ๋ธŒ๋ผ์šฐ์ € ์„ค์น˜
80
  install_browsers()
81
 
82
  # Playwright ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
83
  playwright_instance = sync_playwright().start()
84
 
85
- # ๋ธŒ๋ผ์šฐ์ € ์‹คํ–‰ (Chromium ์‚ฌ์šฉ)
86
  browser = playwright_instance.chromium.launch(
87
  headless=True,
88
  args=[
@@ -91,52 +84,44 @@ def init_browser():
91
  '--disable-gpu',
92
  '--disable-extensions',
93
  '--disable-web-security',
94
- '--allow-running-insecure-content',
95
  '--disable-features=VizDisplayCompositor',
96
  '--disable-background-timer-throttling',
97
- '--disable-backgrounding-occluded-windows',
98
  '--disable-renderer-backgrounding',
99
- '--disable-features=TranslateUI',
100
- '--disable-ipc-flooding-protection',
101
  '--disable-plugins',
102
- '--disable-images',
 
 
103
  '--force-device-scale-factor=1',
104
- '--font-render-hinting=medium',
105
- '--disable-font-subpixel-positioning'
106
  ]
107
  )
108
 
109
  # ์ƒˆ ํŽ˜์ด์ง€ ์ƒ์„ฑ
110
  page = browser.new_page()
111
 
112
- # ๋ทฐํฌํŠธ ์„ค์ •
113
- page.set_viewport_size({"width": 1920, "height": 1080})
114
 
115
- # User-Agent ์„ค์ •
116
  page.set_extra_http_headers({
117
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
118
- "Accept-Language": "ko-KR,ko;q=0.9,en;q=0.8"
119
  })
120
 
121
- # ํ•œ๊ธ€ ํฐํŠธ ์„ค์ •์„ ์œ„ํ•œ CSS ์ถ”๊ฐ€
122
- page.add_style_tag(content="""
123
- * {
124
- font-family: 'Nanum Gothic', 'Malgun Gothic', '๋ง‘์€ ๊ณ ๋”•', 'Noto Sans CJK KR', 'Apple SD Gothic Neo', sans-serif !important;
125
- }
126
- """)
127
 
128
- print("Google Trends ํŽ˜์ด์ง€๋กœ ์ด๋™ ์ค‘...")
129
- page.goto("https://trends.google.com/trending?geo=KR", wait_until="networkidle", timeout=30000)
130
-
131
- # ์ถ”๊ฐ€ ๋กœ๋”ฉ ์‹œ๊ฐ„
132
- time.sleep(5)
133
 
134
  is_browser_ready = True
135
- print("๋ธŒ๋ผ์šฐ์ € ์ดˆ๊ธฐํ™” ์™„๋ฃŒ!")
136
 
137
  except Exception as e:
138
  print(f"๋ธŒ๋ผ์šฐ์ € ์ดˆ๊ธฐํ™” ์‹คํŒจ: {str(e)}")
139
- print(traceback.format_exc())
140
  is_browser_ready = False
141
  cleanup_browser()
142
 
@@ -177,16 +162,15 @@ def cleanup_browser():
177
  print(f"๋ธŒ๋ผ์šฐ์ € ์ •๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {e}")
178
 
179
  def apply_filters(geo: str, time_range: str, category: str) -> bool:
180
- """Google Trends ํ•„ํ„ฐ ์ ์šฉ"""
181
  global page
182
 
183
- # ๋ธŒ๋ผ์šฐ์ € ์ค€๋น„ ์ƒํƒœ ํ™•์ธ
184
  if not ensure_browser_ready():
185
  print("๋ธŒ๋ผ์šฐ์ € ์ค€๋น„ ์‹คํŒจ")
186
  return False
187
 
188
  try:
189
- print(f"ํ•„ํ„ฐ ์ ์šฉ: ์ง€์—ญ={geo}")
190
 
191
  # ์ง€์—ญ ์„ค์ •์„ ์œ„ํ•œ URL ๊ตฌ์„ฑ
192
  geo_map = {
@@ -198,119 +182,61 @@ def apply_filters(geo: str, time_range: str, category: str) -> bool:
198
  geo_param = geo_map.get(geo, "KR")
199
  url = f"{base_url}?geo={geo_param}"
200
 
201
- print(f"ํ•„ํ„ฐ ์ ์šฉ๋œ URL๋กœ ์ด๋™: {url}")
202
- page.goto(url, wait_until="networkidle", timeout=30000)
 
203
 
204
- # ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์™„๋ฃŒ ๋Œ€๊ธฐ
205
- time.sleep(3)
206
 
207
- # ์‹ค์ œ ํ•„ํ„ฐ ์š”์†Œ๋“ค์ด ์žˆ๋‹ค๋ฉด ํด๋ฆญ์œผ๋กœ ์„ค์ •
208
- try:
209
- # ์‹œ๊ฐ„ ๋ฒ”์œ„ ํ•„ํ„ฐ ์‹œ๋„
210
- if time_range != "24์‹œ๊ฐ„":
211
- time_selector = page.locator('[data-bucket="0"]').first
212
- if time_selector.is_visible():
213
- time_selector.click()
214
- time.sleep(1)
215
-
216
- # ์‹œ๊ฐ„ ์˜ต์…˜๋“ค ์ค‘ ์„ ํƒ
217
- if time_range == "7์ผ":
218
- page.locator('text="Past 7 days"').first.click()
219
- elif time_range == "30์ผ":
220
- page.locator('text="Past 30 days"').first.click()
221
- time.sleep(2)
222
-
223
- # ์นดํ…Œ๊ณ ๋ฆฌ ํ•„ํ„ฐ ์‹œ๋„
224
- if category != "๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ":
225
- category_selector = page.locator('[data-bucket="2"]').first
226
- if category_selector.is_visible():
227
- category_selector.click()
228
- time.sleep(1)
229
-
230
- # ์นดํ…Œ๊ณ ๋ฆฌ ๋งคํ•‘
231
- category_map = {
232
- "์—”ํ„ฐํ…Œ์ธ๋จผํŠธ": "Entertainment",
233
- "์Šคํฌ์ธ ": "Sports",
234
- "๋น„์ฆˆ๋‹ˆ์Šค": "Business",
235
- "๊ณผํ•™๊ธฐ์ˆ ": "Science & Tech",
236
- "๊ฑด๊ฐ•": "Health"
237
- }
238
-
239
- if category in category_map:
240
- page.locator(f'text="{category_map[category]}"').first.click()
241
- time.sleep(2)
242
-
243
- except Exception as filter_error:
244
- print(f"ํ•„ํ„ฐ ์š”์†Œ ํด๋ฆญ ์‹คํŒจ (๊ณ„์† ์ง„ํ–‰): {filter_error}")
245
-
246
- print("ํ•„ํ„ฐ ์ ์šฉ ์™„๋ฃŒ")
247
  return True
248
 
249
  except Exception as e:
250
  print(f"ํ•„ํ„ฐ ์ ์šฉ ์‹คํŒจ: {str(e)}")
251
- print(traceback.format_exc())
252
- return False
253
 
254
  def capture_screenshot() -> Optional[Image.Image]:
255
- """ํ˜„์žฌ ํŽ˜์ด์ง€์˜ ์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ (ํŠธ๋ Œ๋“œ ํ…Œ์ด๋ธ” ์˜์—ญ๋งŒ)"""
256
  global page
257
 
258
- # ๋ธŒ๋ผ์šฐ์ € ์ค€๋น„ ์ƒํƒœ ํ™•์ธ
259
  if not ensure_browser_ready():
260
  print("๋ธŒ๋ผ์šฐ์ € ์ค€๋น„ ์‹คํŒจ")
261
  return None
262
 
263
  try:
264
- print("์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ ์ค‘...")
265
 
266
- # ํŽ˜์ด์ง€๊ฐ€ ์™„์ „ํžˆ ๋กœ๋“œ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ
267
- time.sleep(2)
268
 
269
- # ํŠธ๋ Œ๋“œ ํ…Œ์ด๋ธ” ์˜์—ญ ์ฐพ๊ธฐ
270
  try:
271
- # ์—ฌ๋Ÿฌ ๊ฐ€๋Šฅํ•œ ์…€๋ ‰ํ„ฐ ์‹œ๋„
272
- selectors = [
273
- '[data-module="TrendingSearches"]', # ๋ฉ”์ธ ํŠธ๋ Œ๋“œ ๋ชจ๋“ˆ
274
- '.trends-table', # ํŠธ๋ Œ๋“œ ํ…Œ์ด๋ธ”
275
- '.trending-searches', # ํŠธ๋ Œ๋“œ ๊ฒ€์ƒ‰์–ด ์˜์—ญ
276
- '[role="main"]', # ๋ฉ”์ธ ์ฝ˜ํ…์ธ  ์˜์—ญ
277
- 'main' # ๊ธฐ๋ณธ ๋ฉ”์ธ ํƒœ๊ทธ
278
- ]
279
-
280
- element = None
281
- for selector in selectors:
282
- try:
283
- element = page.locator(selector).first
284
- if element.is_visible():
285
- print(f"์บก์ฒ˜ ์˜์—ญ ๋ฐœ๊ฒฌ: {selector}")
286
- break
287
- except:
288
- continue
289
-
290
- if element and element.is_visible():
291
- # ํŠน์ • ์˜์—ญ๋งŒ ์Šคํฌ๋ฆฐ์ƒท
292
  screenshot_bytes = element.screenshot()
293
- print("ํŠธ๋ Œ๋“œ ํ…Œ์ด๋ธ” ์˜์—ญ ์บก์ฒ˜ ์™„๋ฃŒ")
294
  else:
295
- # ๋Œ€์•ˆ: ์ „์ฒด ํŽ˜์ด์ง€์—์„œ ์ƒ๋‹จ ๋ถ€๋ถ„๋งŒ ์บก์ฒ˜
296
- print("ํŠน์ • ์˜์—ญ์„ ์ฐพ์ง€ ๋ชปํ•ด ํŽ˜์ด์ง€ ์ƒ๋‹จ ์˜์—ญ ์บก์ฒ˜")
297
- screenshot_bytes = page.screenshot(clip={"x": 0, "y": 100, "width": 1920, "height": 800})
298
 
299
- except Exception as selector_error:
300
- print(f"์˜์—ญ ์„ ํƒ ์‹คํŒจ, ์ „์ฒด ํŽ˜์ด์ง€ ์บก์ฒ˜: {selector_error}")
301
- screenshot_bytes = page.screenshot(full_page=True)
 
302
 
303
  screenshot = Image.open(io.BytesIO(screenshot_bytes))
304
- print("์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ ์™„๋ฃŒ")
305
  return screenshot
306
 
307
  except Exception as e:
308
  print(f"์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ ์‹คํŒจ: {str(e)}")
309
- print(traceback.format_exc())
310
  return None
311
 
312
  def analyze_with_claude(image: Image.Image) -> str:
313
- """Claude API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€ ๋ถ„์„"""
314
  try:
315
  client = get_claude_client()
316
 
@@ -319,22 +245,16 @@ def analyze_with_claude(image: Image.Image) -> str:
319
  image.save(buffer, format="PNG")
320
  image_base64 = base64.b64encode(buffer.getvalue()).decode()
321
 
322
- # Claude API ํ˜ธ์ถœ์„ ์œ„ํ•œ ํ”„๋กฌํ”„ํŠธ ๊ตฌ์„ฑ
323
- prompt = """
324
- ์ด Google Trends ์‹ค์‹œ๊ฐ„ ์ธ๊ธฐ ๊ฒ€์ƒ‰์–ด ์Šคํฌ๋ฆฐ์ƒท์„ ๋ถ„์„ํ•ด์ฃผ์„ธ์š”.
325
-
326
- ๋‹ค์Œ ๋‚ด์šฉ์„ ํฌํ•จํ•ด์„œ ๋ถ„์„ํ•ด์ฃผ์„ธ์š”:
327
- 1. ํ˜„์žฌ ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ๊ฒ€์ƒ‰์–ด๋“ค๊ณผ ๊ทธ ์ˆœ์œ„
328
- 2. ๊ฐ ๊ฒ€์ƒ‰์–ด์˜ ์ฆ๊ฐ€ ์ถ”์„ธ๋‚˜ ํŠน์ด์‚ฌํ•ญ
329
- 3. ๊ฒ€์ƒ‰์–ด๋“ค์—์„œ ๋ฐœ๊ฒฌ๋˜๋Š” ์ฃผ์š” ํŠธ๋ Œ๋“œ๋‚˜ ํŒจํ„ด
330
- 4. ์‹œ์‚ฌ์ /๋ฌธํ™”์  ๋งฅ๋ฝ์—์„œ์˜ ํ•ด์„
331
-
332
- ํ•œ๊ตญ์–ด๋กœ ์ƒ์„ธํ•˜๊ณ  ๊ตฌ์ฒด์ ์œผ๋กœ ๋ถ„์„ํ•ด์ฃผ์„ธ์š”.
333
- """
334
 
335
  response = client.messages.create(
336
  model="claude-3-5-sonnet-20241022",
337
- max_tokens=1500,
338
  messages=[
339
  {
340
  "role": "user",
@@ -364,78 +284,88 @@ def analyze_with_claude(image: Image.Image) -> str:
364
  return error_msg
365
 
366
  def reinit_browser():
367
- """๋ธŒ๋ผ์šฐ์ € ์žฌ์ดˆ๊ธฐํ™”"""
368
- global is_browser_ready
369
-
370
- print("๋ธŒ๋ผ์šฐ์ € ์žฌ์ดˆ๊ธฐํ™” ์‹œ์ž‘...")
371
-
372
- # ๊ธฐ์กด ๋ธŒ๋ผ์šฐ์ € ์ •๋ฆฌ
373
- cleanup_browser()
374
- is_browser_ready = False
375
-
376
- # ์ƒˆ๋กœ ์ดˆ๊ธฐํ™”
377
- init_browser()
378
 
379
- if is_browser_ready:
380
- return "๋ธŒ๋ผ์šฐ์ € ์žฌ์ดˆ๊ธฐํ™” ์™„๋ฃŒ!"
381
- else:
382
- return "๋ธŒ๋ผ์šฐ์ € ์žฌ์ดˆ๊ธฐํ™” ์‹คํŒจ. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
 
384
  def refresh_trends_page():
385
- """Google Trends ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ"""
386
  global page
387
 
388
  if not ensure_browser_ready():
389
  return "๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
390
 
391
  try:
392
- page.reload(wait_until="networkidle", timeout=30000)
393
- time.sleep(3)
394
- return "ํŽ˜์ด์ง€๊ฐ€ ์ƒˆ๋กœ๊ณ ์นจ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
 
 
 
395
  except Exception as e:
396
  return f"ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ ์‹คํŒจ: {str(e)}"
397
 
398
  def main_process_screenshot(geo: str, time_range: str, category: str) -> Tuple[Optional[Image.Image], str]:
399
- """์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜๋งŒ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜"""
400
  try:
401
- # 1. ๋ธŒ๋ผ์šฐ์ € ์ค€๋น„ ํ™•์ธ
402
  if not ensure_browser_ready():
403
- return None, "๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. '๋ธŒ๋ผ์šฐ์ € ์žฌ์ดˆ๊ธฐํ™”' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด์ฃผ์„ธ์š”."
404
 
405
- # 2. ํ•„ํ„ฐ ์ ์šฉ
406
- print(f"ํ•„ํ„ฐ ์ ์šฉ ์ค‘: ์ง€์—ญ={geo}, ์‹œ๊ฐ„={time_range}, ์นดํ…Œ๊ณ ๋ฆฌ={category}")
407
  filter_success = apply_filters(geo, time_range, category)
408
 
 
409
  if not filter_success:
410
- return None, "ํ•„ํ„ฐ ์ ์šฉ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."
411
 
412
- # 3. ์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜
413
- print("์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ ์ค‘...")
414
  screenshot = capture_screenshot()
415
 
416
  if screenshot is None:
417
  return None, "์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."
418
 
419
- return screenshot, "์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ ์™„๋ฃŒ! Claude ๋ถ„์„์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค..."
420
 
421
  except Exception as e:
422
- error_msg = f"์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
423
  print(error_msg)
424
- print(traceback.format_exc())
425
  return None, error_msg
426
 
427
  def analyze_screenshot_with_claude(screenshot: Image.Image) -> str:
428
- """Claude API๋กœ ์Šคํฌ๋ฆฐ์ƒท ๋ถ„์„"""
429
  if screenshot is None:
430
  return "๋ถ„์„ํ•  ์Šคํฌ๋ฆฐ์ƒท์ด ์—†์Šต๋‹ˆ๋‹ค."
431
 
432
  try:
433
- print("Claude API๋กœ ๋ถ„์„ ์ค‘...")
434
  analysis_result = analyze_with_claude(screenshot)
435
  return analysis_result
436
 
437
  except Exception as e:
438
- error_msg = f"Claude ๋ถ„์„ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
439
  print(error_msg)
440
  return error_msg
441
 
@@ -473,30 +403,30 @@ def main_process(geo: str, time_range: str, category: str) -> Tuple[Optional[Ima
473
  return None, error_msg
474
 
475
  def check_browser_status():
476
- """๋ธŒ๋ผ์šฐ์ € ์ƒํƒœ ํ™•์ธ"""
477
  global is_browser_ready, page, browser_thread_id
478
 
479
  current_thread_id = threading.current_thread().ident
480
 
481
  if not page:
482
- return "๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
483
 
484
  if not is_browser_ready:
485
- return "๋ธŒ๋ผ์šฐ์ € ์ดˆ๊ธฐํ™” ์ค‘์ž…๋‹ˆ๋‹ค..."
486
 
487
  try:
488
  current_url = page.url
489
- return f"๋ธŒ๋ผ์šฐ์ € ์ค€๋น„ ์™„๋ฃŒ - ํ˜„์žฌ URL: {current_url} (์Šค๋ ˆ๋“œ: {current_thread_id}, ๋ธŒ๋ผ์šฐ์ € ์Šค๋ ˆ๋“œ: {browser_thread_id})"
490
  except Exception as e:
491
- return f"๋ธŒ๋ผ์šฐ์ € ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
492
 
493
  # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
494
  def create_interface():
495
  """Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ"""
496
 
497
- with gr.Blocks(title="Google Trends ์‹ค์‹œ๊ฐ„ ๋ถ„์„๊ธฐ", theme=gr.themes.Soft()) as interface:
498
- gr.Markdown("# Google Trends ์‹ค์‹œ๊ฐ„ ์ธ๊ธฐ ๊ฒ€์ƒ‰์–ด ๋ถ„์„๊ธฐ")
499
- gr.Markdown("Google Trends์˜ ์‹ค์‹œ๊ฐ„ ์ธ๊ธฐ ๊ฒ€์ƒ‰์–ด๋ฅผ ์บก์ฒ˜ํ•˜๊ณ  Claude AI๋กœ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.")
500
 
501
  # ์ƒํƒœ ์ €์žฅ์„ ์œ„ํ•œ State ์ปดํฌ๋„ŒํŠธ
502
  screenshot_state = gr.State(None)
@@ -522,27 +452,27 @@ def create_interface():
522
  )
523
 
524
  with gr.Row():
525
- capture_btn = gr.Button("๐ŸŽฏ ์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜", variant="primary")
526
- analyze_btn = gr.Button("๐Ÿค– Claude ๋ถ„์„", variant="secondary", interactive=False)
527
 
528
  with gr.Row():
529
- refresh_btn = gr.Button("๐Ÿ”„ ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ", variant="secondary")
530
- reinit_btn = gr.Button("โš™๏ธ ๋ธŒ๋ผ์šฐ์ € ์ดˆ๊ธฐํ™”", variant="secondary")
531
- status_btn = gr.Button("๐Ÿ“Š ๋ธŒ๋ผ์šฐ์ € ์ƒํƒœ ํ™•์ธ", variant="secondary")
532
 
533
  with gr.Column(scale=2):
534
  screenshot_output = gr.Image(label="Google Trends ์Šคํฌ๋ฆฐ์ƒท")
535
  analysis_output = gr.Textbox(
536
- label="Claude AI ๋ถ„์„ ๊ฒฐ๊ณผ",
537
- lines=10,
538
- max_lines=20,
539
- placeholder="์Šคํฌ๋ฆฐ์ƒท์„ ์บก์ฒ˜ํ•œ ํ›„ 'Claude ๋ถ„์„' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”."
540
  )
541
  status_output = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€")
542
 
543
  # ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
544
 
545
- # ์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ ๋ฒ„ํŠผ
546
  def on_capture_click(geo, time_range, category):
547
  screenshot, message = main_process_screenshot(geo, time_range, category)
548
  if screenshot is not None:
@@ -557,10 +487,10 @@ def create_interface():
557
  outputs=[screenshot_output, screenshot_state, status_output, analyze_btn]
558
  )
559
 
560
- # Claude ๋ถ„์„ ๋ฒ„ํŠผ
561
  def on_analyze_click(screenshot):
562
  if screenshot is None:
563
- return "๋ถ„์„ํ•  ์Šคํฌ๋ฆฐ์ƒท์ด ์—†์Šต๋‹ˆ๋‹ค. ๋จผ์ € '์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”."
564
 
565
  analysis_result = analyze_screenshot_with_claude(screenshot)
566
  return analysis_result
@@ -590,12 +520,12 @@ def create_interface():
590
  return interface
591
 
592
  def main():
593
- """๋ฉ”์ธ ํ•จ์ˆ˜"""
594
  print("=" * 50)
595
- print("Google Trends ์‹ค์‹œ๊ฐ„ ๋ถ„์„๊ธฐ ์‹œ์ž‘!")
596
  print("=" * 50)
597
 
598
- # API ํ‚ค ์กด์žฌ ์—ฌ๋ถ€๋งŒ ํ™•์ธ (์‹ค์ œ ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”๋Š” ๋‚˜์ค‘์—)
599
  api_key = os.getenv("ANTHROPIC_API_KEY")
600
  if not api_key:
601
  print("โœ— ANTHROPIC_API_KEY ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
@@ -603,13 +533,17 @@ def main():
603
  else:
604
  print("โœ“ Claude API ํ‚ค ํ™•์ธ๋จ")
605
 
606
- print("โœ“ Gradio ์›น ์ธํ„ฐํŽ˜์ด์Šค ์ค€๋น„ ์ค‘...")
607
 
608
  # ํ™˜๊ฒฝ ํ™•์ธ
609
  if os.getenv("SPACE_ID"):
610
  print("โœ“ Hugging Face Space ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ ์ค‘...")
611
 
612
- print("โœ“ ๋ธŒ๋ผ์šฐ์ €๋Š” ์ฒซ ๋ฒˆ์งธ ์‚ฌ์šฉ ์‹œ ์ž๋™์œผ๋กœ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค.")
 
 
 
 
613
 
614
  # ์ข…๋ฃŒ ์‹œ ๋ธŒ๋ผ์šฐ์ € ์ •๋ฆฌ
615
  atexit.register(cleanup_browser)
@@ -617,13 +551,15 @@ def main():
617
  # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
618
  interface = create_interface()
619
 
620
- # Hugging Face Space์—์„œ๋Š” share=False๋กœ ์‹คํ–‰
 
 
621
  interface.launch(
622
  server_name="0.0.0.0",
623
  server_port=7860,
624
  share=False,
625
  show_error=True,
626
- quiet=False
627
  )
628
 
629
  if __name__ == "__main__":
 
48
  return anthropic.Anthropic(api_key=api_key)
49
 
50
  def install_browsers():
51
+ """Playwright ๋ธŒ๋ผ์šฐ์ € ๋ฐ ํ•œ๊ธ€ ํฐํŠธ ์„ค์น˜ (ํ•œ๋ฒˆ๋งŒ ์‹คํ–‰)"""
52
  try:
53
+ print("Playwright ๋ธŒ๋ผ์šฐ์ € ๋น ๋ฅธ ์„ค์น˜ ์ค‘...")
54
+ # ๋ณ‘๋ ฌ ์„ค์น˜๋กœ ์†๋„ ํ–ฅ์ƒ
55
+ os.system("playwright install chromium --force")
56
+ print("ํ•œ๊ธ€ ํฐํŠธ ๋น ๋ฅธ ์„ค์น˜ ์ค‘...")
57
+ os.system("apt-get install -y fonts-nanum fonts-noto-cjk &")
58
+ print("๋ธŒ๋ผ์šฐ์ € ๋ฐ ํฐํŠธ ์„ค์น˜ ์™„๋ฃŒ!")
 
 
 
 
59
  return True
60
  except Exception as e:
61
+ print(f"์„ค์น˜ ์‹คํŒจ: {e}")
62
  return False
63
 
64
  def init_browser():
65
+ """Playwright ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  Google Trends ํŽ˜์ด์ง€๋กœ ์ด๋™ (WarmUp ์ „๋žต)"""
66
  global browser, page, playwright_instance, is_browser_ready, browser_thread_id
67
 
68
  try:
 
69
  browser_thread_id = threading.current_thread().ident
70
+ print(f"๋ธŒ๋ผ์šฐ์ € WarmUp ์‹œ์ž‘ (์Šค๋ ˆ๋“œ: {browser_thread_id})")
 
 
71
 
72
+ # ๋น ๋ฅธ ๋ธŒ๋ผ์šฐ์ € ์„ค์น˜
73
  install_browsers()
74
 
75
  # Playwright ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
76
  playwright_instance = sync_playwright().start()
77
 
78
+ # ์†๋„ ์ตœ์ ํ™”๋œ ๋ธŒ๋ผ์šฐ์ € ์‹คํ–‰
79
  browser = playwright_instance.chromium.launch(
80
  headless=True,
81
  args=[
 
84
  '--disable-gpu',
85
  '--disable-extensions',
86
  '--disable-web-security',
 
87
  '--disable-features=VizDisplayCompositor',
88
  '--disable-background-timer-throttling',
 
89
  '--disable-renderer-backgrounding',
 
 
90
  '--disable-plugins',
91
+ '--disable-images', # ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ๋น„ํ™œ์„ฑํ™”๋กœ ์†๋„ ํ–ฅ์ƒ
92
+ '--disable-javascript', # JS ๋น„ํ™œ์„ฑํ™”๋กœ ์†๋„ ํ–ฅ์ƒ
93
+ '--disable-css', # CSS ๋น„ํ™œ์„ฑํ™”๋กœ ์†๋„ ํ–ฅ์ƒ
94
  '--force-device-scale-factor=1',
95
+ '--aggressive-cache-discard',
96
+ '--memory-pressure-off'
97
  ]
98
  )
99
 
100
  # ์ƒˆ ํŽ˜์ด์ง€ ์ƒ์„ฑ
101
  page = browser.new_page()
102
 
103
+ # ๋ทฐํฌํŠธ ์„ค์ • (์ž‘๊ฒŒ ์„ค์ •ํ•˜์—ฌ ์†๋„ ํ–ฅ์ƒ)
104
+ page.set_viewport_size({"width": 1200, "height": 800})
105
 
106
+ # ๋น ๋ฅธ ํ—ค๋” ์„ค์ •
107
  page.set_extra_http_headers({
108
+ "Accept-Language": "ko-KR,ko;q=0.9"
 
109
  })
110
 
111
+ print("Google Trends ํŽ˜์ด์ง€๋กœ ๋น ๋ฅธ ์ด๋™ ์ค‘...")
112
+ # ๋น ๋ฅธ ๋กœ๋”ฉ์„ ์œ„ํ•ด domcontentloaded ์‚ฌ์šฉ, ํƒ€์ž„์•„์›ƒ ๋‹จ์ถ•
113
+ page.goto("https://trends.google.com/trending?geo=KR",
114
+ wait_until="domcontentloaded",
115
+ timeout=15000)
 
116
 
117
+ # ์ตœ์†Œ ๋Œ€๊ธฐ ์‹œ๊ฐ„์œผ๋กœ ๋‹จ์ถ•
118
+ time.sleep(2)
 
 
 
119
 
120
  is_browser_ready = True
121
+ print("๋ธŒ๋ผ์šฐ์ € WarmUp ์™„๏ฟฝ๏ฟฝ๏ฟฝ! (๊ณ ์† ๋ชจ๋“œ)")
122
 
123
  except Exception as e:
124
  print(f"๋ธŒ๋ผ์šฐ์ € ์ดˆ๊ธฐํ™” ์‹คํŒจ: {str(e)}")
 
125
  is_browser_ready = False
126
  cleanup_browser()
127
 
 
162
  print(f"๋ธŒ๋ผ์šฐ์ € ์ •๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {e}")
163
 
164
  def apply_filters(geo: str, time_range: str, category: str) -> bool:
165
+ """Google Trends ํ•„ํ„ฐ ์ ์šฉ (๊ณ ์† ๋ชจ๋“œ)"""
166
  global page
167
 
 
168
  if not ensure_browser_ready():
169
  print("๋ธŒ๋ผ์šฐ์ € ์ค€๋น„ ์‹คํŒจ")
170
  return False
171
 
172
  try:
173
+ print(f"๊ณ ์† ํ•„ํ„ฐ ์ ์šฉ: ์ง€์—ญ={geo}")
174
 
175
  # ์ง€์—ญ ์„ค์ •์„ ์œ„ํ•œ URL ๊ตฌ์„ฑ
176
  geo_map = {
 
182
  geo_param = geo_map.get(geo, "KR")
183
  url = f"{base_url}?geo={geo_param}"
184
 
185
+ print(f"๋น ๋ฅธ ์ด๋™: {url}")
186
+ # ๋น ๋ฅธ ๋กœ๋”ฉ์„ ์œ„ํ•ด domcontentloaded ์‚ฌ์šฉ, ํƒ€์ž„์•„์›ƒ ๋Œ€ํญ ๋‹จ์ถ•
187
+ page.goto(url, wait_until="domcontentloaded", timeout=10000)
188
 
189
+ # ์ตœ์†Œ ๋Œ€๊ธฐ ์‹œ๊ฐ„
190
+ time.sleep(1)
191
 
192
+ print("๊ณ ์† ํ•„ํ„ฐ ์ ์šฉ ์™„๋ฃŒ")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  return True
194
 
195
  except Exception as e:
196
  print(f"ํ•„ํ„ฐ ์ ์šฉ ์‹คํŒจ: {str(e)}")
197
+ # ์‹คํŒจํ•ด๋„ ํ˜„์žฌ ํŽ˜์ด์ง€์—์„œ ์Šคํฌ๋ฆฐ์ƒท ์‹œ๋„
198
+ return True # ์‹คํŒจํ•ด๋„ ๊ณ„์† ์ง„ํ–‰
199
 
200
  def capture_screenshot() -> Optional[Image.Image]:
201
+ """ํ˜„์žฌ ํŽ˜์ด์ง€์˜ ์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ (๊ณ ์† ๋ชจ๋“œ)"""
202
  global page
203
 
 
204
  if not ensure_browser_ready():
205
  print("๋ธŒ๋ผ์šฐ์ € ์ค€๋น„ ์‹คํŒจ")
206
  return None
207
 
208
  try:
209
+ print("๊ณ ์† ์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ ์ค‘...")
210
 
211
+ # ๋Œ€๊ธฐ ์‹œ๊ฐ„ ์ตœ์†Œํ™”
212
+ time.sleep(0.5)
213
 
214
+ # ๋น ๋ฅธ ์Šคํฌ๋ฆฐ์ƒท์„ ์œ„ํ•ด ํŠน์ • ์˜์—ญ๋งŒ ์บก์ฒ˜
215
  try:
216
+ # ํŠธ๋ Œ๋“œ ํ…Œ์ด๋ธ” ์˜์—ญ ์ฐพ๊ธฐ (๋น ๋ฅธ ์…€๋ ‰ํ„ฐ๋งŒ ์‚ฌ์šฉ)
217
+ element = page.locator('main').first
218
+ if element.is_visible(timeout=2000):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  screenshot_bytes = element.screenshot()
220
+ print("๋ฉ”์ธ ์˜์—ญ ๊ณ ์† ์บก์ฒ˜ ์™„๋ฃŒ")
221
  else:
222
+ # ๋Œ€์•ˆ: ํŽ˜์ด์ง€ ์ค‘์•™ ์˜์—ญ๋งŒ ๋น ๋ฅด๊ฒŒ ์บก์ฒ˜
223
+ screenshot_bytes = page.screenshot(clip={"x": 0, "y": 150, "width": 1200, "height": 600})
224
+ print("์ค‘์•™ ์˜์—ญ ๊ณ ์† ์บก์ฒ˜ ์™„๋ฃŒ")
225
 
226
+ except Exception:
227
+ # ์ตœ์ข… ๋Œ€์•ˆ: ์ „์ฒด ํŽ˜์ด์ง€ ์บก์ฒ˜
228
+ screenshot_bytes = page.screenshot()
229
+ print("์ „์ฒด ํŽ˜์ด์ง€ ๊ณ ์† ์บก์ฒ˜ ์™„๋ฃŒ")
230
 
231
  screenshot = Image.open(io.BytesIO(screenshot_bytes))
 
232
  return screenshot
233
 
234
  except Exception as e:
235
  print(f"์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ ์‹คํŒจ: {str(e)}")
 
236
  return None
237
 
238
  def analyze_with_claude(image: Image.Image) -> str:
239
+ """Claude API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€ ๋ถ„์„ (๊ณ ์† ๋ชจ๋“œ)"""
240
  try:
241
  client = get_claude_client()
242
 
 
245
  image.save(buffer, format="PNG")
246
  image_base64 = base64.b64encode(buffer.getvalue()).decode()
247
 
248
+ # ์ถ•์•ฝ๋œ ํ”„๋กฌํ”„ํŠธ (์†๋„ ํ–ฅ์ƒ)
249
+ prompt = """Google Trends ์Šคํฌ๋ฆฐ์ƒท์„ ๊ฐ„๋‹จํžˆ ๋ถ„์„ํ•ด์ฃผ์„ธ์š”:
250
+ 1. ์ƒ์œ„ 5๊ฐœ ๊ฒ€์ƒ‰์–ด์™€ ์ˆœ์œ„
251
+ 2. ์ฃผ์š” ํŠธ๋ Œ๋“œ ํŒจํ„ด
252
+ 3. ํŠน์ด์‚ฌํ•ญ
253
+ ํ•œ๊ตญ์–ด๋กœ ๊ฐ„๋‹จ๋ช…๋ฃŒํ•˜๊ฒŒ ๋‹ต๋ณ€ํ•˜์„ธ์š”."""
 
 
 
 
 
 
254
 
255
  response = client.messages.create(
256
  model="claude-3-5-sonnet-20241022",
257
+ max_tokens=800, # ํ† ํฐ ์ˆ˜ ๋Œ€ํญ ์ถ•์†Œ
258
  messages=[
259
  {
260
  "role": "user",
 
284
  return error_msg
285
 
286
  def reinit_browser():
287
+ """๋ธŒ๋ผ์šฐ์ € ๊ฒฝ๋Ÿ‰ ์žฌ์ดˆ๊ธฐํ™” (๊ธฐ์กด ๋ธŒ๋ผ์šฐ์ € ์œ ์ง€)"""
288
+ global page
 
 
 
 
 
 
 
 
 
289
 
290
+ try:
291
+ print("๊ฒฝ๋Ÿ‰ ๋ธŒ๋ผ์šฐ์ € ์žฌ์ดˆ๊ธฐํ™” ์‹œ์ž‘...")
292
+
293
+ if page:
294
+ # ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์™„์ „ํžˆ ์žฌ์‹œ์ž‘ํ•˜์ง€ ์•Š๊ณ  ํŽ˜์ด์ง€๋งŒ ์ƒˆ๋กœ๊ณ ์นจ
295
+ page.goto("https://trends.google.com/trending?geo=KR",
296
+ wait_until="domcontentloaded",
297
+ timeout=10000)
298
+ time.sleep(1)
299
+ return "๋ธŒ๋ผ์šฐ์ € ๊ฒฝ๋Ÿ‰ ์žฌ์ดˆ๊ธฐํ™” ์™„๋ฃŒ! (๊ธฐ์กด ๋ธŒ๋ผ์šฐ์ € ์œ ์ง€)"
300
+ else:
301
+ # ํŽ˜์ด์ง€๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ์—๋งŒ ์™„์ „ ์žฌ์ดˆ๊ธฐํ™”
302
+ init_browser()
303
+ if is_browser_ready:
304
+ return "๋ธŒ๋ผ์šฐ์ € ์™„์ „ ์žฌ์ดˆ๊ธฐํ™” ์™„๋ฃŒ!"
305
+ else:
306
+ return "๋ธŒ๋ผ์šฐ์ € ์žฌ์ดˆ๊ธฐํ™” ์‹คํŒจ"
307
+
308
+ except Exception as e:
309
+ print(f"์žฌ์ดˆ๊ธฐํ™” ์‹คํŒจ: {e}")
310
+ return f"์žฌ์ดˆ๊ธฐํ™” ์‹คํŒจ: {str(e)}"
311
 
312
  def refresh_trends_page():
313
+ """Google Trends ํŽ˜์ด์ง€ ๋น ๋ฅธ ์ƒˆ๋กœ๊ณ ์นจ"""
314
  global page
315
 
316
  if not ensure_browser_ready():
317
  return "๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
318
 
319
  try:
320
+ print("๋น ๋ฅธ ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ...")
321
+ # reload() ๋Œ€์‹  ํ˜„์žฌ URL๋กœ ๋น ๋ฅด๊ฒŒ ์ด๋™
322
+ current_url = page.url
323
+ page.goto(current_url, wait_until="domcontentloaded", timeout=8000)
324
+ time.sleep(0.5)
325
+ return "ํŽ˜์ด์ง€ ๋น ๋ฅธ ์ƒˆ๋กœ๊ณ ์นจ ์™„๋ฃŒ!"
326
  except Exception as e:
327
  return f"ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ ์‹คํŒจ: {str(e)}"
328
 
329
  def main_process_screenshot(geo: str, time_range: str, category: str) -> Tuple[Optional[Image.Image], str]:
330
+ """์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜๋งŒ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜ (๊ณ ์† ๋ชจ๋“œ)"""
331
  try:
332
+ # 1. ๋ธŒ๋ผ์šฐ์ € ์ค€๋น„ ํ™•์ธ (๋น ๋ฅธ ํ™•์ธ)
333
  if not ensure_browser_ready():
334
+ return None, "๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
335
 
336
+ # 2. ๊ณ ์† ํ•„ํ„ฐ ์ ์šฉ
337
+ print(f"๊ณ ์† ์ฒ˜๋ฆฌ: ์ง€์—ญ={geo}")
338
  filter_success = apply_filters(geo, time_range, category)
339
 
340
+ # ํ•„ํ„ฐ ์‹คํŒจํ•ด๋„ ํ˜„์žฌ ํŽ˜์ด์ง€์—์„œ ์Šคํฌ๋ฆฐ์ƒท ์‹œ๋„
341
  if not filter_success:
342
+ print("ํ•„ํ„ฐ ์ ์šฉ ์‹คํŒจํ–ˆ์ง€๋งŒ ํ˜„์žฌ ํŽ˜์ด์ง€์—์„œ ์Šคํฌ๋ฆฐ์ƒท ์‹œ๋„")
343
 
344
+ # 3. ๊ณ ์† ์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜
 
345
  screenshot = capture_screenshot()
346
 
347
  if screenshot is None:
348
  return None, "์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."
349
 
350
+ return screenshot, "๊ณ ์† ์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ ์™„๋ฃŒ!"
351
 
352
  except Exception as e:
353
+ error_msg = f"๊ณ ์† ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {str(e)}"
354
  print(error_msg)
 
355
  return None, error_msg
356
 
357
  def analyze_screenshot_with_claude(screenshot: Image.Image) -> str:
358
+ """Claude API๋กœ ์Šคํฌ๋ฆฐ์ƒท ๋ถ„์„ (๊ณ ์† ๋ชจ๋“œ)"""
359
  if screenshot is None:
360
  return "๋ถ„์„ํ•  ์Šคํฌ๋ฆฐ์ƒท์ด ์—†์Šต๋‹ˆ๋‹ค."
361
 
362
  try:
363
+ print("Claude ๊ณ ์† ๋ถ„์„ ์ค‘...")
364
  analysis_result = analyze_with_claude(screenshot)
365
  return analysis_result
366
 
367
  except Exception as e:
368
+ error_msg = f"Claude ๋ถ„์„ ์‹คํŒจ: {str(e)}"
369
  print(error_msg)
370
  return error_msg
371
 
 
403
  return None, error_msg
404
 
405
  def check_browser_status():
406
+ """๋ธŒ๋ผ์šฐ์ € ์ƒํƒœ ํ™•์ธ (๊ณ ์†)"""
407
  global is_browser_ready, page, browser_thread_id
408
 
409
  current_thread_id = threading.current_thread().ident
410
 
411
  if not page:
412
+ return "๋ธŒ๋ผ์šฐ์ € ๋ฏธ์ดˆ๊ธฐํ™”"
413
 
414
  if not is_browser_ready:
415
+ return "๋ธŒ๋ผ์šฐ์ € ์ดˆ๊ธฐํ™” ์ค‘..."
416
 
417
  try:
418
  current_url = page.url
419
+ return f"๋ธŒ๋ผ์šฐ์ € ์ค€๋น„์™„๋ฃŒ - URL: {current_url[:50]}..."
420
  except Exception as e:
421
+ return f"๋ธŒ๋ผ์šฐ์ € ์—ฐ๊ฒฐ ๋ฌธ์ œ: {str(e)[:30]}..."
422
 
423
  # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
424
  def create_interface():
425
  """Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ"""
426
 
427
+ with gr.Blocks(title="Google Trends ๊ณ ์† ๋ถ„์„๊ธฐ", theme=gr.themes.Soft()) as interface:
428
+ gr.Markdown("# โšก Google Trends ๊ณ ์† ๋ถ„์„๊ธฐ")
429
+ gr.Markdown("**์ดˆ๊ณ ์† ๋ชจ๋“œ**: Google Trends ์‹ค์‹œ๊ฐ„ ๊ฒ€์ƒ‰์–ด๋ฅผ ๋น ๋ฅด๊ฒŒ ์บก์ฒ˜ํ•˜๊ณ  AI ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.")
430
 
431
  # ์ƒํƒœ ์ €์žฅ์„ ์œ„ํ•œ State ์ปดํฌ๋„ŒํŠธ
432
  screenshot_state = gr.State(None)
 
452
  )
453
 
454
  with gr.Row():
455
+ capture_btn = gr.Button("โšก ๊ณ ์† ์บก์ฒ˜", variant="primary", size="lg")
456
+ analyze_btn = gr.Button("๐Ÿค– AI ๋ถ„์„", variant="secondary", interactive=False)
457
 
458
  with gr.Row():
459
+ refresh_btn = gr.Button("๐Ÿ”„ ๋น ๋ฅธ ์ƒˆ๋กœ๊ณ ์นจ", variant="secondary", size="sm")
460
+ reinit_btn = gr.Button("โš™๏ธ ๊ฒฝ๋Ÿ‰ ์žฌ์‹œ์ž‘", variant="secondary", size="sm")
461
+ status_btn = gr.Button("๐Ÿ“Š ์ƒํƒœ ํ™•์ธ", variant="secondary", size="sm")
462
 
463
  with gr.Column(scale=2):
464
  screenshot_output = gr.Image(label="Google Trends ์Šคํฌ๋ฆฐ์ƒท")
465
  analysis_output = gr.Textbox(
466
+ label="Claude AI ๊ณ ์† ๋ถ„์„ ๊ฒฐ๊ณผ",
467
+ lines=8,
468
+ max_lines=15,
469
+ placeholder="๊ณ ์† ์บก์ฒ˜ ํ›„ 'AI ๋ถ„์„' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”."
470
  )
471
  status_output = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€")
472
 
473
  # ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
474
 
475
+ # ๊ณ ์† ์บก์ฒ˜ ๋ฒ„ํŠผ
476
  def on_capture_click(geo, time_range, category):
477
  screenshot, message = main_process_screenshot(geo, time_range, category)
478
  if screenshot is not None:
 
487
  outputs=[screenshot_output, screenshot_state, status_output, analyze_btn]
488
  )
489
 
490
+ # AI ๋ถ„์„ ๋ฒ„ํŠผ
491
  def on_analyze_click(screenshot):
492
  if screenshot is None:
493
+ return "๋ถ„์„ํ•  ์Šคํฌ๋ฆฐ์ƒท์ด ์—†์Šต๋‹ˆ๋‹ค. ๋จผ์ € '๊ณ ์† ์บก์ฒ˜' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”."
494
 
495
  analysis_result = analyze_screenshot_with_claude(screenshot)
496
  return analysis_result
 
520
  return interface
521
 
522
  def main():
523
+ """๋ฉ”์ธ ํ•จ์ˆ˜ (WarmUp ์ „๋žต ์ ์šฉ)"""
524
  print("=" * 50)
525
+ print("Google Trends ๊ณ ์† ๋ถ„์„๊ธฐ ์‹œ์ž‘!")
526
  print("=" * 50)
527
 
528
+ # API ํ‚ค ํ™•์ธ
529
  api_key = os.getenv("ANTHROPIC_API_KEY")
530
  if not api_key:
531
  print("โœ— ANTHROPIC_API_KEY ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
 
533
  else:
534
  print("โœ“ Claude API ํ‚ค ํ™•์ธ๋จ")
535
 
536
+ print("โœ“ Gradio ๊ณ ์† ์ธํ„ฐํŽ˜์ด์Šค ์ค€๋น„ ์ค‘...")
537
 
538
  # ํ™˜๊ฒฝ ํ™•์ธ
539
  if os.getenv("SPACE_ID"):
540
  print("โœ“ Hugging Face Space ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ ์ค‘...")
541
 
542
+ print("โœ“ ๋ธŒ๋ผ์šฐ์ € WarmUp ์‹œ์ž‘ (๋ฐฑ๊ทธ๋ผ์šด๋“œ)")
543
+
544
+ # WarmUp ์ „๋žต: ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋ธŒ๋ผ์šฐ์ € ๋ฏธ๋ฆฌ ์ค€๋น„
545
+ warmup_thread = threading.Thread(target=init_browser, daemon=True)
546
+ warmup_thread.start()
547
 
548
  # ์ข…๋ฃŒ ์‹œ ๋ธŒ๋ผ์šฐ์ € ์ •๋ฆฌ
549
  atexit.register(cleanup_browser)
 
551
  # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
552
  interface = create_interface()
553
 
554
+ print("โœ“ ๊ณ ์† ๋ถ„์„๊ธฐ ์ค€๋น„ ์™„๋ฃŒ!")
555
+
556
+ # ๋น ๋ฅธ ์‹คํ–‰์„ ์œ„ํ•œ ์„ค์ •
557
  interface.launch(
558
  server_name="0.0.0.0",
559
  server_port=7860,
560
  share=False,
561
  show_error=True,
562
+ quiet=True # ๋กœ๊ทธ ์ถœ๋ ฅ ์ตœ์†Œํ™”
563
  )
564
 
565
  if __name__ == "__main__":