Files changed (1) hide show
  1. app.py +98 -64
app.py CHANGED
@@ -25,6 +25,10 @@ last_update_time = None
25
  update_thread = None
26
  is_updating = False
27
 
 
 
 
 
28
  def get_claude_client():
29
  """Claude API ν΄λΌμ΄μ–ΈνŠΈ μ΄ˆκΈ°ν™”"""
30
  api_key = os.getenv("ANTHROPIC_API_KEY")
@@ -48,52 +52,88 @@ def install_browsers():
48
  print(f"λΈŒλΌμš°μ € μ„€μΉ˜ μ‹€νŒ¨: {e}")
49
  return False
50
 
51
- def create_browser_for_thread():
52
- """μŠ€λ ˆλ“œλ³„ 독립적인 λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€ 생성"""
 
 
53
  try:
54
- print(f"[Thread {threading.current_thread().name}] λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€ 생성 쀑...")
55
-
56
- playwright = sync_playwright().start()
57
-
58
- # λΈŒλΌμš°μ € μ‹€ν–‰ μ˜΅μ…˜
59
- browser_args = [
60
- '--no-sandbox',
61
- '--disable-dev-shm-usage',
62
- '--disable-gpu',
63
- '--disable-extensions',
64
- '--disable-web-security',
65
- '--disable-blink-features=AutomationControlled',
66
- '--disable-background-timer-throttling',
67
- '--disable-backgrounding-occluded-windows',
68
- '--disable-renderer-backgrounding',
69
- '--disable-features=TranslateUI',
70
- '--disable-ipc-flooding-protection'
71
- ]
72
-
73
- browser = playwright.chromium.launch(
74
- headless=True,
75
- args=browser_args
76
- )
77
-
78
- print(f"[Thread {threading.current_thread().name}] λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€ 생성 μ™„λ£Œ")
79
- return playwright, browser
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
  except Exception as e:
82
- print(f"[Thread {threading.current_thread().name}] λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€ 생성 μ‹€νŒ¨: {str(e)}")
83
  print(f"상세 였λ₯˜: {traceback.format_exc()}")
84
- return None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
  def capture_google_trends() -> Optional[Image.Image]:
87
- """Google Trends μŠ€ν¬λ¦°μƒ· 캑처 (독립 λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€ μ‚¬μš©)"""
88
- playwright = None
89
- browser = None
90
  page = None
91
 
92
  try:
93
  print("Google Trends μŠ€ν¬λ¦°μƒ· μ‹œμž‘...")
94
-
95
- # μŠ€λ ˆλ“œλ³„ 독립 λΈŒλΌμš°μ € 생성
96
- playwright, browser = create_browser_for_thread()
97
  if not browser:
98
  print("λΈŒλΌμš°μ € μ΄ˆκΈ°ν™” μ‹€νŒ¨")
99
  return None
@@ -166,34 +206,19 @@ def capture_google_trends() -> Optional[Image.Image]:
166
  print(f"상세 였λ₯˜: {traceback.format_exc()}")
167
  return None
168
  finally:
169
- # λ¦¬μ†ŒμŠ€ 정리
170
  if page:
171
  try:
172
  page.close()
173
  except:
174
  pass
175
- if browser:
176
- try:
177
- browser.close()
178
- except:
179
- pass
180
- if playwright:
181
- try:
182
- playwright.stop()
183
- except:
184
- pass
185
 
186
  def capture_ezme_trends() -> Optional[Image.Image]:
187
- """rank.ezme.net μŠ€ν¬λ¦°μƒ· 캑처 (독립 λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€ μ‚¬μš©)"""
188
- playwright = None
189
- browser = None
190
  page = None
191
 
192
  try:
193
  print("rank.ezme.net μŠ€ν¬λ¦°μƒ· μ‹œμž‘...")
194
-
195
- # μŠ€λ ˆλ“œλ³„ 독립 λΈŒλΌμš°μ € 생성
196
- playwright, browser = create_browser_for_thread()
197
  if not browser:
198
  print("λΈŒλΌμš°μ € μ΄ˆκΈ°ν™” μ‹€νŒ¨")
199
  return None
@@ -250,22 +275,11 @@ def capture_ezme_trends() -> Optional[Image.Image]:
250
  print(f"상세 였λ₯˜: {traceback.format_exc()}")
251
  return None
252
  finally:
253
- # λ¦¬μ†ŒμŠ€ 정리
254
  if page:
255
  try:
256
  page.close()
257
  except:
258
  pass
259
- if browser:
260
- try:
261
- browser.close()
262
- except:
263
- pass
264
- if playwright:
265
- try:
266
- playwright.stop()
267
- except:
268
- pass
269
 
270
  def analyze_google_trends_with_claude(image: Image.Image) -> str:
271
  """Google Trends 이미지 Claude 뢄석"""
@@ -449,6 +463,21 @@ def combine_analysis_results(google_analysis: str, ezme_analysis: str) -> str:
449
 
450
  {ezme_analysis}
451
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
 
453
  return combined_result
454
 
@@ -723,6 +752,9 @@ def main():
723
  else:
724
  print("Claude API ν‚€ 확인됨")
725
 
 
 
 
726
  # 초기 톡합 뢄석 μˆ˜ν–‰
727
  print("초기 톡합 뢄석 μ‹œμž‘...")
728
  initial_success, _ = perform_parallel_analysis()
@@ -754,6 +786,8 @@ def main():
754
  except Exception as e:
755
  print(f"μΈν„°νŽ˜μ΄μŠ€ μ‹œμž‘ μ‹€νŒ¨: {e}")
756
  print(f"상세 였λ₯˜: {traceback.format_exc()}")
 
 
757
 
758
  if __name__ == "__main__":
759
  main()
 
25
  update_thread = None
26
  is_updating = False
27
 
28
+ # μ „μ—­ λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€
29
+ global_playwright = None
30
+ global_browser = None
31
+
32
  def get_claude_client():
33
  """Claude API ν΄λΌμ΄μ–ΈνŠΈ μ΄ˆκΈ°ν™”"""
34
  api_key = os.getenv("ANTHROPIC_API_KEY")
 
52
  print(f"λΈŒλΌμš°μ € μ„€μΉ˜ μ‹€νŒ¨: {e}")
53
  return False
54
 
55
+ def get_shared_browser():
56
+ """곡유 λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€ λ°˜ν™˜ (μž¬μ‚¬μš© μ΅œμ ν™” + μ•ˆμ •μ„± κ°•ν™”)"""
57
+ global global_playwright, global_browser
58
+
59
  try:
60
+ # Playwright μΈμŠ€ν„΄μŠ€ μ΄ˆκΈ°ν™”
61
+ if global_playwright is None:
62
+ print("Playwright μΈμŠ€ν„΄μŠ€ μ΄ˆκΈ°ν™”...")
63
+ global_playwright = sync_playwright().start()
64
+ print("Playwright μ΄ˆκΈ°ν™” μ™„λ£Œ")
65
+
66
+ # λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€ 확인 및 생성
67
+ if global_browser is None or not global_browser.is_connected():
68
+ print("λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€ 생성 쀑...")
69
+
70
+ # λΈŒλΌμš°μ € μ‹€ν–‰ μ˜΅μ…˜
71
+ browser_args = [
72
+ '--no-sandbox',
73
+ '--disable-dev-shm-usage',
74
+ '--disable-gpu',
75
+ '--disable-extensions',
76
+ '--disable-web-security',
77
+ '--disable-blink-features=AutomationControlled',
78
+ '--disable-background-timer-throttling',
79
+ '--disable-backgrounding-occluded-windows',
80
+ '--disable-renderer-backgrounding',
81
+ '--disable-features=TranslateUI',
82
+ '--disable-ipc-flooding-protection'
83
+ ]
84
+
85
+ global_browser = global_playwright.chromium.launch(
86
+ headless=True,
87
+ args=browser_args
88
+ )
89
+ print("λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€ 생성 μ™„λ£Œ")
90
+
91
+ # μ—°κ²° μƒνƒœ μž¬ν™•μΈ
92
+ if global_browser.is_connected():
93
+ print("λΈŒλΌμš°μ € μ—°κ²° μƒνƒœ μ–‘ν˜Έ")
94
+ return global_browser
95
+ else:
96
+ print("λΈŒλΌμš°μ € μ—°κ²° μƒνƒœ λΆˆλŸ‰, μž¬μƒμ„± μ‹œλ„")
97
+ global_browser = None
98
+ return get_shared_browser() # μž¬κ·€ 호좜둜 μž¬μƒμ„±
99
 
100
  except Exception as e:
101
+ print(f"λΈŒλΌμš°μ € μΈμŠ€ν„΄μŠ€ 생성 μ‹€νŒ¨: {str(e)}")
102
  print(f"상세 였λ₯˜: {traceback.format_exc()}")
103
+
104
+ # μ‹€νŒ¨μ‹œ μ •λ¦¬ν•˜κ³  None λ°˜ν™˜
105
+ try:
106
+ if global_browser:
107
+ global_browser.close()
108
+ if global_playwright:
109
+ global_playwright.stop()
110
+ except:
111
+ pass
112
+
113
+ global_browser = None
114
+ global_playwright = None
115
+ return None
116
+
117
+ def cleanup_browser():
118
+ """λΈŒλΌμš°μ € 정리"""
119
+ global global_playwright, global_browser
120
+ try:
121
+ if global_browser:
122
+ global_browser.close()
123
+ global_browser = None
124
+ if global_playwright:
125
+ global_playwright.stop()
126
+ global_playwright = None
127
+ except:
128
+ pass
129
 
130
  def capture_google_trends() -> Optional[Image.Image]:
131
+ """Google Trends μŠ€ν¬λ¦°μƒ· 캑처 (동적 λ‘œλ”© λŒ€μ‘)"""
 
 
132
  page = None
133
 
134
  try:
135
  print("Google Trends μŠ€ν¬λ¦°μƒ· μ‹œμž‘...")
136
+ browser = get_shared_browser()
 
 
137
  if not browser:
138
  print("λΈŒλΌμš°μ € μ΄ˆκΈ°ν™” μ‹€νŒ¨")
139
  return None
 
206
  print(f"상세 였λ₯˜: {traceback.format_exc()}")
207
  return None
208
  finally:
 
209
  if page:
210
  try:
211
  page.close()
212
  except:
213
  pass
 
 
 
 
 
 
 
 
 
 
214
 
215
  def capture_ezme_trends() -> Optional[Image.Image]:
216
+ """rank.ezme.net μŠ€ν¬λ¦°μƒ· 캑처 (동적 λ‘œλ”© λŒ€μ‘)"""
 
 
217
  page = None
218
 
219
  try:
220
  print("rank.ezme.net μŠ€ν¬λ¦°μƒ· μ‹œμž‘...")
221
+ browser = get_shared_browser()
 
 
222
  if not browser:
223
  print("λΈŒλΌμš°μ € μ΄ˆκΈ°ν™” μ‹€νŒ¨")
224
  return None
 
275
  print(f"상세 였λ₯˜: {traceback.format_exc()}")
276
  return None
277
  finally:
 
278
  if page:
279
  try:
280
  page.close()
281
  except:
282
  pass
 
 
 
 
 
 
 
 
 
 
283
 
284
  def analyze_google_trends_with_claude(image: Image.Image) -> str:
285
  """Google Trends 이미지 Claude 뢄석"""
 
463
 
464
  {ezme_analysis}
465
  """
466
+
467
+ # ---
468
+
469
+ # ## μ’…ν•© 뢄석 및 μΈμ‚¬μ΄νŠΈ
470
+
471
+ # ### μ£Όμš” 곡톡 이슈
472
+ # - Google Trends와 ν•œκ΅­ ν¬ν„Έμ—μ„œ κ³΅ν†΅μ μœΌλ‘œ μƒμœ„κΆŒμ— λ‚˜νƒ€λ‚˜λŠ” 검색어듀을 톡해 ν˜„μž¬ κ°€μž₯ 뜨거운 이슈λ₯Ό νŒŒμ•…ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
473
+
474
+ # ### ν”Œλž«νΌλ³„ νŠΉμ„±
475
+ # - **Google Trends**: 전세계적 관심사와 μž₯기적 νŠΈλ Œλ“œ 반영
476
+ # - **ν•œκ΅­ 포털 (Zum, Nate, Google)**: ν•œκ΅­ μ‚¬μš©μžμ˜ μ‹€μ‹œκ°„ 관심사와 μ¦‰μ‹œμ„± μžˆλŠ” 이슈 반영
477
+
478
+ # ### μ‹œκ°„λŒ€λ³„ 검색 νŒ¨ν„΄
479
+ # - 각 μ‹œκ°„λŒ€λ³„λ‘œ λ‚˜νƒ€λ‚˜λŠ” 검색어 λ³€ν™”λ₯Ό 톡해 ν•œκ΅­ μ‚¬μš©μžλ“€μ˜ 일상 νŒ¨ν„΄κ³Ό 관심사 λ³€ν™”λ₯Ό κ΄€μ°°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
480
+
481
 
482
  return combined_result
483
 
 
752
  else:
753
  print("Claude API ν‚€ 확인됨")
754
 
755
+ # μ’…λ£Œμ‹œ λΈŒλΌμš°μ € 정리
756
+ atexit.register(cleanup_browser)
757
+
758
  # 초기 톡합 뢄석 μˆ˜ν–‰
759
  print("초기 톡합 뢄석 μ‹œμž‘...")
760
  initial_success, _ = perform_parallel_analysis()
 
786
  except Exception as e:
787
  print(f"μΈν„°νŽ˜μ΄μŠ€ μ‹œμž‘ μ‹€νŒ¨: {e}")
788
  print(f"상세 였λ₯˜: {traceback.format_exc()}")
789
+ finally:
790
+ cleanup_browser()
791
 
792
  if __name__ == "__main__":
793
  main()