HanJun commited on
Commit
7fe908b
ยท
verified ยท
1 Parent(s): 2c476a3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -97
app.py CHANGED
@@ -15,20 +15,21 @@ from selenium.common.exceptions import TimeoutException, NoSuchElementException
15
  from concurrent.futures import ThreadPoolExecutor
16
  import threading
17
 
18
- # Hugging Face Space ํ™˜๊ฒฝ ํ™•์ธ
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
 
25
- if not ANTHROPIC_API_KEY:
26
- print("ANTHROPIC_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์Œ, Secret ํ™•์ธ ํ•„์š”")
27
- else:
28
- print("Claude API ํ‚ค ํ™•์ธ ์™„๋ฃŒ")
29
-
30
- # Claude ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ƒ์„ฑ
31
  def create_claude_client():
 
32
  try:
33
  client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)
34
  print("Claude API ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ ์„ฑ๊ณต")
@@ -37,36 +38,25 @@ def create_claude_client():
37
  print(f"Claude API ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ ์‹คํŒจ: {e}")
38
  return None
39
 
40
- # Google Trends ์ž๋™ํ™” ํด๋ž˜์Šค
41
  class GoogleTrendsAutomator:
 
 
42
  def __init__(self):
43
  self.driver = None
44
-
45
- # Chrome ๋“œ๋ผ์ด๋ฒ„ ์„ค์ •
46
  def setup_driver(self):
 
47
  options = Options()
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")
@@ -85,8 +75,7 @@ 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 = {
@@ -105,66 +94,34 @@ 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
-
114
- print("ChromeDriver ์ž๋™ ์„ค์น˜ ์ค‘...")
115
- service = Service(ChromeDriverManager().install())
116
- self.driver = webdriver.Chrome(service=service, options=options)
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)
124
- print("์‹œ์Šคํ…œ ChromeDriver ์‚ฌ์šฉ ์„ฑ๊ณต")
125
- except Exception as sys_e:
126
- print(f"์‹œ์Šคํ…œ ChromeDriver๋„ ์‹คํŒจ: {sys_e}")
127
- return False
128
- else:
129
- # ๋กœ์ปฌ ํ™˜๊ฒฝ
130
- try:
131
- from webdriver_manager.chrome import ChromeDriverManager
132
- from selenium.webdriver.chrome.service import Service
133
-
134
- service = Service(ChromeDriverManager().install())
135
- self.driver = webdriver.Chrome(service=service, options=options)
136
- print("๋กœ์ปฌ ChromeDriver ์„ค์น˜ ์„ฑ๊ณต")
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์ดˆ
@@ -186,8 +143,8 @@ class GoogleTrendsAutomator:
186
  print(f"๋“œ๋ผ์ด๋ฒ„ ์„ค์ • ์ตœ์ข… ์‹คํŒจ: {e}")
187
  return False
188
 
189
- # ๋น ๋ฅด๊ฒŒ Element ์ฐพ๊ธฐ, Timeout ๋‹จ์ถ• ๊ณ ๋ ค
190
  def safe_find_element(self, selectors: list, timeout: int = 3):
 
191
  for selector_type, selector in selectors:
192
  try:
193
  if selector_type == "xpath":
@@ -209,8 +166,8 @@ class GoogleTrendsAutomator:
209
  continue
210
  return None
211
 
212
- # ์ฆ‰์‹œ ํด๋ฆญ
213
  def safe_click(self, selectors: list, timeout: int = 3) -> bool:
 
214
  element = self.safe_find_element(selectors, timeout)
215
  if element:
216
  try:
@@ -225,8 +182,8 @@ class GoogleTrendsAutomator:
225
  return False
226
  return False
227
 
228
- # ๋ณ‘๋ ฌ๋กœ ์„ค์ • ๋ณ€๊ฒฝ ์‹œ๋„
229
  def parallel_change_settings(self, region: str, period: str, category: str, progress_callback=None) -> dict:
 
230
  results = {
231
  'region': False,
232
  'period': False,
@@ -265,8 +222,8 @@ class GoogleTrendsAutomator:
265
 
266
  return results
267
 
268
- # ์ง€์—ญ ๋ณ€๊ฒฝ
269
  def change_region(self, target_region: str) -> bool:
 
270
  try:
271
  region_selectors = [
272
  ("xpath", "//button[@aria-label='๋Œ€ํ•œ๋ฏผ๊ตญ, ์œ„์น˜ ์„ ํƒ']"),
@@ -302,8 +259,8 @@ class GoogleTrendsAutomator:
302
  print(f"์ง€์—ญ ๋ณ€๊ฒฝ ์‹คํŒจ: {e}")
303
  return False
304
 
305
- # ๊ธฐ๊ฐ„ ๋ณ€๊ฒฝ
306
  def change_time_period(self, target_period: str) -> bool:
 
307
  try:
308
  period_selectors = [
309
  ("xpath", "//button[contains(@aria-label, '๊ธฐ๊ฐ„ ์„ ํƒ')]"),
@@ -338,8 +295,8 @@ class GoogleTrendsAutomator:
338
  print(f"๊ธฐ๊ฐ„ ๋ณ€๊ฒฝ ์‹คํŒจ: {e}")
339
  return False
340
 
341
- # ์นดํ…Œ๊ณ ๋ฆฌ ๏ฟฝ๏ฟฝ๊ฒฝ
342
  def change_category(self, target_category: str) -> bool:
 
343
  try:
344
  category_selectors = [
345
  ("xpath", "//button[contains(@aria-label, '์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ')]"),
@@ -377,8 +334,8 @@ class GoogleTrendsAutomator:
377
  print(f"์นดํ…Œ๊ณ ๋ฆฌ ๋ณ€๊ฒฝ ์‹คํŒจ: {e}")
378
  return False
379
 
380
- # Google Trends ์บก์ฒ˜
381
  def capture_trends(self, region: str, period: str, category: str, progress_callback=None) -> Tuple[Optional[str], bool, str]:
 
382
  error_msg = ""
383
 
384
  try:
@@ -446,8 +403,8 @@ class GoogleTrendsAutomator:
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:
 
451
  try:
452
  # API ํ‚ค ์ฒดํฌ
453
  if not ANTHROPIC_API_KEY or ANTHROPIC_API_KEY == "your_api_key_here":
 
15
  from concurrent.futures import ThreadPoolExecutor
16
  import threading
17
 
18
+ # .env ํŒŒ์ผ ๋กœ๋“œ (์žˆ๋Š” ๊ฒฝ์šฐ)
19
+ try:
20
+ from dotenv import load_dotenv
21
+ load_dotenv()
22
+ print(".env ํŒŒ์ผ ๋กœ๋“œ ์™„๋ฃŒ")
23
+ except ImportError:
24
+ print("python-dotenv๊ฐ€ ์„ค์น˜๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ˆ˜๋™์œผ๋กœ ์„ค์ •ํ•ด์ฃผ์„ธ์š”.")
25
+ except Exception as e:
26
+ print(f".env ํŒŒ์ผ ๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜: {e}")
27
 
28
+ # Claude API ์„ค์ •
29
  ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY")
30
 
 
 
 
 
 
 
31
  def create_claude_client():
32
+ """Claude ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜ (๋น ๋ฅธ ๋ฒ„์ „)"""
33
  try:
34
  client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)
35
  print("Claude API ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ ์„ฑ๊ณต")
 
38
  print(f"Claude API ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ ์‹คํŒจ: {e}")
39
  return None
40
 
 
41
  class GoogleTrendsAutomator:
42
+ """์ตœ์ ํ™”๋œ Google Trends ์ž๋™ํ™” ํด๋ž˜์Šค"""
43
+
44
  def __init__(self):
45
  self.driver = None
46
+
 
47
  def setup_driver(self):
48
+ """์ดˆ๊ณ ์† Chrome ๋“œ๋ผ์ด๋ฒ„ ์„ค์ •"""
49
  options = Options()
50
 
51
+ # ํ™˜๊ฒฝ๋ณ„ ์„ค์ •
52
+ if os.getenv("SPACE_ID"): # Hugging Face Space
53
+ options.add_argument("--headless")
 
54
  options.add_argument("--no-sandbox")
55
  options.add_argument("--disable-dev-shm-usage")
56
  options.add_argument("--disable-gpu")
57
+ else: # ๋กœ์ปฌ ํ™˜๊ฒฝ
 
 
58
  options.add_argument("--headless")
59
 
 
 
 
 
 
 
 
 
 
60
  # ์†๋„ ์ตœ์ ํ™” ์„ค์ •
61
  options.add_argument("--window-size=1280,720") # ํ•ด์ƒ๋„ ์ค„์ž„
62
  options.add_argument("--disable-blink-features=AutomationControlled")
 
75
 
76
  # ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”
77
  options.add_argument("--memory-pressure-off")
78
+ options.add_argument("--max_old_space_size=4096")
 
79
 
80
  # ์ด๋ฏธ์ง€/CSS ๋น„ํ™œ์„ฑํ™”๋กœ ๋กœ๋”ฉ ์†๋„ ํ–ฅ์ƒ
81
  prefs = {
 
94
  options.add_argument("--accept-lang=ko-KR,ko;q=0.9,en;q=0.8")
95
 
96
  try:
97
+ # ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•์œผ๋กœ ChromeDriver ์‹œ๋„
98
+ driver_created = False
99
+
100
+ # ๋ฐฉ๋ฒ• 1: webdriver-manager ์‚ฌ์šฉ
101
+ try:
102
+ from webdriver_manager.chrome import ChromeDriverManager
103
+ from selenium.webdriver.chrome.service import Service
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
+ service = Service(ChromeDriverManager().install())
106
+ self.driver = webdriver.Chrome(service=service, options=options)
107
+ print("webdriver-manager๋กœ ChromeDriver ์ž๋™ ์„ค์น˜ ์„ฑ๊ณต")
108
+ driver_created = True
109
 
110
+ except Exception as wm_error:
111
+ print(f"webdriver-manager ์‹คํŒจ: {wm_error}")
112
+
113
+ # ๋ฐฉ๋ฒ• 2: ์‹œ์Šคํ…œ ChromeDriver ์‚ฌ์šฉ
114
+ if not driver_created:
115
+ try:
116
+ self.driver = webdriver.Chrome(options=options)
117
+ print("์‹œ์Šคํ…œ ChromeDriver ์‚ฌ์šฉ ์„ฑ๊ณต")
118
+ driver_created = True
119
+ except Exception as sys_error:
120
+ print(f"์‹œ์Šคํ…œ ChromeDriver ์‹คํŒจ: {sys_error}")
121
+
122
+ if not driver_created:
123
+ print("๋ชจ๋“  ChromeDriver ์ดˆ๊ธฐํ™” ๋ฐฉ๋ฒ• ์‹คํŒจ")
124
+ return False
125
 
126
  # ํƒ€์ž„์•„์›ƒ ์„ค์ • (์†๋„ ์ตœ์ ํ™”)
127
  self.driver.set_page_load_timeout(15) # ํŽ˜์ด์ง€ ๋กœ๋”ฉ ํƒ€์ž„์•„์›ƒ 15์ดˆ
 
143
  print(f"๋“œ๋ผ์ด๋ฒ„ ์„ค์ • ์ตœ์ข… ์‹คํŒจ: {e}")
144
  return False
145
 
 
146
  def safe_find_element(self, selectors: list, timeout: int = 3):
147
+ """์ดˆ๊ณ ์† ์š”์†Œ ์ฐพ๊ธฐ (ํƒ€์ž„์•„์›ƒ ๋‹จ์ถ•)"""
148
  for selector_type, selector in selectors:
149
  try:
150
  if selector_type == "xpath":
 
166
  continue
167
  return None
168
 
 
169
  def safe_click(self, selectors: list, timeout: int = 3) -> bool:
170
+ """์ฆ‰์‹œ ํด๋ฆญ (๋Œ€๊ธฐ ์‹œ๊ฐ„ ์ตœ์†Œํ™”)"""
171
  element = self.safe_find_element(selectors, timeout)
172
  if element:
173
  try:
 
182
  return False
183
  return False
184
 
 
185
  def parallel_change_settings(self, region: str, period: str, category: str, progress_callback=None) -> dict:
186
+ """๋ณ‘๋ ฌ๋กœ ์„ค์ • ๋ณ€๊ฒฝ ์‹œ๋„ (์‹คํ—˜์ )"""
187
  results = {
188
  'region': False,
189
  'period': False,
 
222
 
223
  return results
224
 
 
225
  def change_region(self, target_region: str) -> bool:
226
+ """์ดˆ๊ณ ์† ์ง€์—ญ ๋ณ€๊ฒฝ"""
227
  try:
228
  region_selectors = [
229
  ("xpath", "//button[@aria-label='๋Œ€ํ•œ๋ฏผ๊ตญ, ์œ„์น˜ ์„ ํƒ']"),
 
259
  print(f"์ง€์—ญ ๋ณ€๊ฒฝ ์‹คํŒจ: {e}")
260
  return False
261
 
 
262
  def change_time_period(self, target_period: str) -> bool:
263
+ """์ดˆ๊ณ ์† ๊ธฐ๊ฐ„ ๋ณ€๊ฒฝ"""
264
  try:
265
  period_selectors = [
266
  ("xpath", "//button[contains(@aria-label, '๊ธฐ๊ฐ„ ์„ ํƒ')]"),
 
295
  print(f"๊ธฐ๊ฐ„ ๋ณ€๊ฒฝ ์‹คํŒจ: {e}")
296
  return False
297
 
 
298
  def change_category(self, target_category: str) -> bool:
299
+ """์ดˆ๊ณ ์† ์นดํ…Œ๊ณ ๋ฆฌ ๋ณ€๊ฒฝ"""
300
  try:
301
  category_selectors = [
302
  ("xpath", "//button[contains(@aria-label, '์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ')]"),
 
334
  print(f"์นดํ…Œ๊ณ ๋ฆฌ ๋ณ€๊ฒฝ ์‹คํŒจ: {e}")
335
  return False
336
 
 
337
  def capture_trends(self, region: str, period: str, category: str, progress_callback=None) -> Tuple[Optional[str], bool, str]:
338
+ """Google Trends ์บก์ฒ˜ (์ตœ์ ํ™”๋œ ๋ฒ„์ „)"""
339
  error_msg = ""
340
 
341
  try:
 
403
  if self.driver:
404
  self.driver.quit()
405
 
 
406
  def analyze_with_claude(screenshot_b64: str, region: str, period: str, category: str) -> str:
407
+ """Claude API๋กœ ์Šคํฌ๋ฆฐ์ƒท ๋ถ„์„ (์ตœ์ ํ™”๋œ ๋ฒ„์ „)"""
408
  try:
409
  # API ํ‚ค ์ฒดํฌ
410
  if not ANTHROPIC_API_KEY or ANTHROPIC_API_KEY == "your_api_key_here":