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