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__":