Update app.py
Browse files
app.py
CHANGED
@@ -1,15 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import time
|
2 |
import hashlib
|
3 |
import hmac
|
4 |
import base64
|
5 |
import requests
|
6 |
-
import gradio as gr
|
7 |
import urllib.request
|
8 |
import urllib.parse
|
9 |
import json
|
10 |
-
import pandas as pd
|
11 |
from concurrent.futures import ThreadPoolExecutor
|
12 |
-
import os
|
13 |
import tempfile
|
14 |
from datetime import datetime
|
15 |
from dotenv import load_dotenv # dotenv μΆκ°
|
@@ -100,18 +104,10 @@ def get_blog_count(keyword):
|
|
100 |
print(f"Error fetching blog count for keyword '{keyword}': {e}")
|
101 |
return 0
|
102 |
|
103 |
-
def
|
104 |
-
api = NaverAPI(BASE_URL, API_KEY, SECRET_KEY, CUSTOMER_ID)
|
105 |
-
return api.get_keywords_data(chunk)
|
106 |
-
|
107 |
-
def get_blog_count_parallel(keyword):
|
108 |
-
return (keyword, get_blog_count(keyword))
|
109 |
-
|
110 |
-
def get_search_volumes(keyword):
|
111 |
"""
|
112 |
λ¨μΌ ν€μλμ μ κ²μλμ κ°μ Έμ€λ ν¨μ.
|
113 |
"""
|
114 |
-
api = NaverAPI(BASE_URL, API_KEY, SECRET_KEY, CUSTOMER_ID)
|
115 |
try:
|
116 |
data = api.get_keywords_data([keyword])
|
117 |
if 'keywordList' in data and len(data['keywordList']) > 0:
|
@@ -135,131 +131,93 @@ def get_search_volumes(keyword):
|
|
135 |
monthly_mobile = 0
|
136 |
|
137 |
total_searches = monthly_pc + monthly_mobile
|
138 |
-
|
|
|
139 |
# μ
λ ₯ν ν€μλμ μΌμΉνλ νλͺ©μ΄ μμ κ²½μ°
|
140 |
-
return (
|
141 |
else:
|
142 |
-
return (
|
143 |
except Exception as e:
|
144 |
print(f"Error fetching search volumes for keyword '{keyword}': {e}")
|
145 |
-
return (
|
146 |
-
|
147 |
-
def
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
if not all_data:
|
165 |
-
return [("Error", "λ°μ΄ν°κ° λ°νλμ§ μμκ±°λ API μλ΅μ΄ μ ν¨νμ§ μμ΅λλ€.", "", "", "")]
|
166 |
-
|
167 |
-
unique_keywords = set()
|
168 |
-
for item in all_data:
|
169 |
-
keyword = item['relKeyword']
|
170 |
-
if keyword not in unique_keywords:
|
171 |
-
unique_keywords.add(keyword)
|
172 |
-
monthly_pc = item.get('monthlyPcQcCnt', 0)
|
173 |
-
monthly_mobile = item.get('monthlyMobileQcCnt', 0)
|
174 |
-
|
175 |
-
if isinstance(monthly_pc, str):
|
176 |
-
monthly_pc = monthly_pc.replace(',', '').replace('< 10', '0')
|
177 |
-
try:
|
178 |
-
monthly_pc = int(monthly_pc)
|
179 |
-
except ValueError:
|
180 |
-
monthly_pc = 0
|
181 |
-
if isinstance(monthly_mobile, str):
|
182 |
-
monthly_mobile = monthly_mobile.replace(',', '').replace('< 10', '0')
|
183 |
-
try:
|
184 |
-
monthly_mobile = int(monthly_mobile)
|
185 |
-
except ValueError:
|
186 |
-
monthly_mobile = 0
|
187 |
-
|
188 |
-
total_searches = monthly_pc + monthly_mobile
|
189 |
-
results.append((keyword, monthly_pc, monthly_mobile, total_searches))
|
190 |
-
|
191 |
-
if len(results) >= 100:
|
192 |
-
break
|
193 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
194 |
else:
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
try:
|
215 |
-
keyword, blog_count = future.result()
|
216 |
-
results[i] = (results[i][0], results[i][1], results[i][2], results[i][3], blog_count)
|
217 |
-
except Exception as e:
|
218 |
-
print(f"Error fetching blog count for keyword '{results[i][0]}': {e}")
|
219 |
-
results[i] = (results[i][0], results[i][1], results[i][2], results[i][3], "Error")
|
220 |
-
else:
|
221 |
-
blog_futures = [executor.submit(get_blog_count_parallel, result[0]) for result in results]
|
222 |
-
temp_results = []
|
223 |
-
for future in blog_futures:
|
224 |
-
try:
|
225 |
-
keyword, blog_count = future.result()
|
226 |
-
temp_results.append((keyword, results[0][1], results[0][2], results[0][3], blog_count))
|
227 |
-
except Exception as e:
|
228 |
-
print(f"Error fetching blog count for keyword '{keyword}': {e}")
|
229 |
-
temp_results.append((keyword, results[0][1], results[0][2], results[0][3], "Error"))
|
230 |
-
results = temp_results
|
231 |
-
|
232 |
-
return results
|
233 |
-
|
234 |
-
def save_to_excel(results, keyword):
|
235 |
-
df = pd.DataFrame(results, columns=["ν€μλ", "PCμκ²μλ", "λͺ¨λ°μΌμκ²μλ", "ν νμκ²μλ", "λΈλ‘κ·Έλ¬Έμμ"])
|
236 |
-
now = datetime.now().strftime('%Y-%m-%d')
|
237 |
-
sanitized_keyword = keyword.replace(' ', '_')
|
238 |
-
filename = f"{now}_{sanitized_keyword}_μ°κ΄κ²μμ΄.xlsx"
|
239 |
-
file_path = os.path.join(tempfile.gettempdir(), filename)
|
240 |
-
df.to_excel(file_path, index=False)
|
241 |
-
return file_path
|
242 |
-
|
243 |
-
def display_search_volumes(keywords, include_related):
|
244 |
-
keyword_list = [keyword.strip() for keyword in keywords.split(',') if keyword.strip()]
|
245 |
-
if not keyword_list:
|
246 |
-
return [("Error", "μ
λ ₯λ ν€μλκ° μμ΅λλ€.", "", "", "")], None
|
247 |
-
results = get_monthly_search_volumes(keyword_list, include_related_keywords=include_related)
|
248 |
-
file_path = save_to_excel(results, keywords)
|
249 |
-
return results, file_path
|
250 |
|
|
|
251 |
iface = gr.Interface(
|
252 |
-
fn=
|
253 |
-
inputs=[
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
outputs=[
|
258 |
-
gr.Dataframe(headers=["ν€μλ", "PCμκ²μλ", "λͺ¨λ°μΌμκ²μλ", "ν νμκ²μλ", "λΈλ‘κ·Έλ¬Έμμ"]),
|
259 |
-
gr.File(label="λ€μ΄λ‘λ μμ
νμΌ")
|
260 |
-
],
|
261 |
-
title="λ€μ΄λ² μκ²μλ κ²μκΈ°",
|
262 |
-
description="ν€μλμ μ κ²μλκ³Ό λΈλ‘κ·Έ λ¬Έμ μλ₯Ό νμΈν μ μμ΅λλ€. μ°κ΄κ²μμ΄λ₯Ό ν¬ν¨ν μ§ μ ννμΈμ.",
|
263 |
)
|
264 |
|
265 |
-
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
import re
|
4 |
+
from collections import Counter
|
5 |
+
import os
|
6 |
+
from openpyxl import load_workbook
|
7 |
+
from openpyxl.drawing.image import Image
|
8 |
import time
|
9 |
import hashlib
|
10 |
import hmac
|
11 |
import base64
|
12 |
import requests
|
|
|
13 |
import urllib.request
|
14 |
import urllib.parse
|
15 |
import json
|
|
|
16 |
from concurrent.futures import ThreadPoolExecutor
|
|
|
17 |
import tempfile
|
18 |
from datetime import datetime
|
19 |
from dotenv import load_dotenv # dotenv μΆκ°
|
|
|
104 |
print(f"Error fetching blog count for keyword '{keyword}': {e}")
|
105 |
return 0
|
106 |
|
107 |
+
def get_search_volumes(keyword, api):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
"""
|
109 |
λ¨μΌ ν€μλμ μ κ²μλμ κ°μ Έμ€λ ν¨μ.
|
110 |
"""
|
|
|
111 |
try:
|
112 |
data = api.get_keywords_data([keyword])
|
113 |
if 'keywordList' in data and len(data['keywordList']) > 0:
|
|
|
131 |
monthly_mobile = 0
|
132 |
|
133 |
total_searches = monthly_pc + monthly_mobile
|
134 |
+
blog_count = get_blog_count(keyword)
|
135 |
+
return (monthly_pc, monthly_mobile, total_searches, blog_count)
|
136 |
# μ
λ ₯ν ν€μλμ μΌμΉνλ νλͺ©μ΄ μμ κ²½μ°
|
137 |
+
return (0, 0, 0, 0)
|
138 |
else:
|
139 |
+
return (0, 0, 0, 0)
|
140 |
except Exception as e:
|
141 |
print(f"Error fetching search volumes for keyword '{keyword}': {e}")
|
142 |
+
return (0, 0, 0, 0)
|
143 |
+
|
144 |
+
def process_excel(file):
|
145 |
+
# μμ
νμΌ μ½κΈ°
|
146 |
+
df = pd.read_excel(file.name)
|
147 |
+
|
148 |
+
# Dμ΄μ λ°μ΄ν° μΆμΆ
|
149 |
+
product_names = df.iloc[:, 3].dropna() # Dμ΄μ 0λΆν° μμνλ―λ‘ indexλ 3
|
150 |
+
|
151 |
+
# ν€μλ μΆμΆ λ° λΉλ κ³μ°
|
152 |
+
all_keywords = []
|
153 |
+
|
154 |
+
for name in product_names:
|
155 |
+
# νΉμλ¬Έμ μ κ±° λ° κ³΅λ°± κΈ°μ€μΌλ‘ λΆν
|
156 |
+
words = re.sub(r'[^\w\s]', '', name).split()
|
157 |
+
# μ€λ³΅ μ κ±°
|
158 |
+
unique_words = set(words)
|
159 |
+
all_keywords.extend(unique_words)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
|
161 |
+
# λΉλ κ³μ°
|
162 |
+
keyword_counts = Counter(all_keywords)
|
163 |
+
|
164 |
+
# κ²°κ³Όλ₯Ό λ°μ΄ν°νλ μμΌλ‘ μ 리
|
165 |
+
result_df = pd.DataFrame(keyword_counts.items(), columns=['Keyword', 'Frequency'])
|
166 |
+
result_df = result_df.sort_values(by='Frequency', ascending=False).reset_index(drop=True)
|
167 |
+
|
168 |
+
# λλ ν 리 μμ± νμΈ λ° νμΌ μ μ₯
|
169 |
+
output_dir = "output"
|
170 |
+
if not os.path.exists(output_dir):
|
171 |
+
os.makedirs(output_dir)
|
172 |
+
|
173 |
+
output_file = os.path.join(output_dir, "keyword_counts.xlsx")
|
174 |
+
|
175 |
+
# μμ
νμΌμ λ°μ΄ν°λ₯Ό A4, B4 μ
λΆν° μ°κΈ°
|
176 |
+
with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
|
177 |
+
result_df.to_excel(writer, index=False, startrow=3) # startrow=3μΌλ‘ μ€μ νμ¬ 4λ²μ§Έ ν(A4, B4)λΆν° μμ
|
178 |
+
|
179 |
+
# μ΄λ―Έμ§λ₯Ό μμ
νμΌμ A1 μ
μ μ½μ
|
180 |
+
wb = load_workbook(output_file)
|
181 |
+
ws = wb.active
|
182 |
+
|
183 |
+
# ssboost-logo.png νμΌμ A1 μ
μ μ½μ
|
184 |
+
if os.path.exists("ssboost-logo.png"):
|
185 |
+
img = Image("ssboost-logo.png")
|
186 |
+
|
187 |
+
# μ΄λ―Έμ§ ν¬κΈ° μ€μ (1.54cm λμ΄, 5.69cm λλΉ)
|
188 |
+
img.height = int(1.54 * 28.3465) # 1 cm = 28.3465 ν¬μΈνΈ
|
189 |
+
img.width = int(5.69 * 28.3465) # 1 cm = 28.3465 ν¬μΈνΈ
|
190 |
+
|
191 |
+
ws.add_image(img, "A1")
|
192 |
else:
|
193 |
+
print("ssboost-logo.png νμΌμ΄ μ‘΄μ¬νμ§ μμ΅λλ€. μ΄λ―Έμ§λ₯Ό μ½μ
νμ§ μμ΅λλ€.")
|
194 |
+
|
195 |
+
# Naver APIλ₯Ό μ¬μ©νμ¬ ν€μλ λΆμ μΆκ°
|
196 |
+
api = NaverAPI(BASE_URL, API_KEY, SECRET_KEY, CUSTOMER_ID)
|
197 |
+
|
198 |
+
# ν€μλ λΆμ κ²°κ³Όλ₯Ό Cμ΄λΆν° μΆκ°
|
199 |
+
for idx, row in result_df.iterrows():
|
200 |
+
keyword = row['Keyword']
|
201 |
+
monthly_pc, monthly_mobile, total_searches, blog_count = get_search_volumes(keyword, api)
|
202 |
+
excel_row = idx + 5 # A5λΆν° μμ
|
203 |
+
ws.cell(row=excel_row, column=3, value=monthly_pc) # Cμ΄: PCμκ²μλ
|
204 |
+
ws.cell(row=excel_row, column=4, value=monthly_mobile) # Dμ΄: λͺ¨λ°μΌμκ²μλ
|
205 |
+
ws.cell(row=excel_row, column=5, value=total_searches) # Eμ΄: ν νμκ²μλ
|
206 |
+
ws.cell(row=excel_row, column=6, value=blog_count) # Fμ΄: λΈλ‘κ·Έλ¬Έμμ
|
207 |
+
|
208 |
+
# μμ
νμΌ μ μ₯
|
209 |
+
wb.save(output_file)
|
210 |
+
|
211 |
+
return output_file
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
|
213 |
+
# Gradio μΈν°νμ΄μ€ μ μ
|
214 |
iface = gr.Interface(
|
215 |
+
fn=process_excel,
|
216 |
+
inputs=gr.File(file_types=[".xlsx"]), # μμ
νμΌλ§ μ
λ‘λν μ μκ² μ€μ
|
217 |
+
outputs="file",
|
218 |
+
title="Excel Keyword Extractor with Naver Analysis",
|
219 |
+
description="μμ
νμΌμ Dμ΄μμ ν€μλλ₯Ό μΆμΆνκ³ λΉλλ₯Ό κ³μ°ν ν, κ° ν€μλμ κ²μλ λ° λΈλ‘κ·Έ λ¬Έμ μλ₯Ό λΆμνμ¬ μλ‘μ΄ μμ
νμΌλ‘ μΆλ ₯ν©λλ€."
|
|
|
|
|
|
|
|
|
|
|
|
|
220 |
)
|
221 |
|
222 |
+
if __name__ == "__main__":
|
223 |
+
iface.launch()
|