Update app.py
Browse files
app.py
CHANGED
@@ -75,6 +75,13 @@ def get_blog_count(keyword):
|
|
75 |
# ν΄λΌμ΄μΈνΈ IDμ μν¬λ¦Ώμ νκ²½ λ³μμμ λΆλ¬μ΅λλ€.
|
76 |
client_id = CLIENT_ID
|
77 |
client_secret = CLIENT_SECRET
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
encText = urllib.parse.quote(keyword)
|
79 |
url = "https://openapi.naver.com/v1/search/blog?query=" + encText
|
80 |
request = urllib.request.Request(url)
|
@@ -86,7 +93,7 @@ def get_blog_count(keyword):
|
|
86 |
if rescode == 200:
|
87 |
response_body = response.read()
|
88 |
data = json.loads(response_body.decode('utf-8'))
|
89 |
-
return data
|
90 |
else:
|
91 |
return 0
|
92 |
except Exception as e:
|
@@ -100,54 +107,127 @@ def get_keywords_data_chunk(chunk):
|
|
100 |
def get_blog_count_parallel(keyword):
|
101 |
return (keyword, get_blog_count(keyword))
|
102 |
|
103 |
-
def
|
104 |
-
|
105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
|
|
|
|
|
121 |
results = []
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
|
|
137 |
|
138 |
-
|
139 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
# λΈλ‘κ·Έ λ¬Έμ μ λ³λ ¬ μμ²
|
142 |
with ThreadPoolExecutor(max_workers=5) as executor:
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
|
152 |
return results
|
153 |
|
@@ -160,20 +240,26 @@ def save_to_excel(results, keyword):
|
|
160 |
df.to_excel(file_path, index=False)
|
161 |
return file_path
|
162 |
|
163 |
-
def display_search_volumes(keywords):
|
164 |
-
keyword_list = [keyword.strip() for keyword in keywords.split(',')]
|
165 |
-
|
|
|
|
|
166 |
file_path = save_to_excel(results, keywords)
|
167 |
return results, file_path
|
168 |
|
169 |
iface = gr.Interface(
|
170 |
fn=display_search_volumes,
|
171 |
-
inputs=
|
|
|
|
|
|
|
172 |
outputs=[
|
173 |
gr.Dataframe(headers=["ν€μλ", "PCμκ²μλ", "λͺ¨λ°μΌμκ²μλ", "ν νμκ²μλ", "λΈλ‘κ·Έλ¬Έμμ"]),
|
174 |
gr.File(label="λ€μ΄λ‘λ μμ
νμΌ")
|
175 |
],
|
176 |
title="λ€μ΄λ² μκ²μλ κ²μκΈ°",
|
|
|
177 |
)
|
178 |
|
179 |
-
iface.launch(share=True) # share=Trueλ₯Ό μΆκ°νμ¬ κ³΅κ° λ§ν¬ μμ±
|
|
|
75 |
# ν΄λΌμ΄μΈνΈ IDμ μν¬λ¦Ώμ νκ²½ λ³μμμ λΆλ¬μ΅λλ€.
|
76 |
client_id = CLIENT_ID
|
77 |
client_secret = CLIENT_SECRET
|
78 |
+
|
79 |
+
# keywordκ° λ°μ΄νΈ νμ
μΌ κ²½μ° λμ½λ©
|
80 |
+
if isinstance(keyword, bytes):
|
81 |
+
keyword = keyword.decode('utf-8')
|
82 |
+
elif not isinstance(keyword, str):
|
83 |
+
keyword = str(keyword)
|
84 |
+
|
85 |
encText = urllib.parse.quote(keyword)
|
86 |
url = "https://openapi.naver.com/v1/search/blog?query=" + encText
|
87 |
request = urllib.request.Request(url)
|
|
|
93 |
if rescode == 200:
|
94 |
response_body = response.read()
|
95 |
data = json.loads(response_body.decode('utf-8'))
|
96 |
+
return data.get('total', 0)
|
97 |
else:
|
98 |
return 0
|
99 |
except Exception as e:
|
|
|
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:
|
118 |
+
# keywordListμμ μ
λ ₯ν ν€μλμ μΌμΉνλ νλͺ©μ μ°Ύμ΅λλ€.
|
119 |
+
for item in data['keywordList']:
|
120 |
+
if item['relKeyword'].strip().lower() == keyword.strip().lower():
|
121 |
+
monthly_pc = item.get('monthlyPcQcCnt', 0)
|
122 |
+
monthly_mobile = item.get('monthlyMobileQcCnt', 0)
|
123 |
|
124 |
+
if isinstance(monthly_pc, str):
|
125 |
+
monthly_pc = monthly_pc.replace(',', '').replace('< 10', '0')
|
126 |
+
try:
|
127 |
+
monthly_pc = int(monthly_pc)
|
128 |
+
except ValueError:
|
129 |
+
monthly_pc = 0
|
130 |
+
if isinstance(monthly_mobile, str):
|
131 |
+
monthly_mobile = monthly_mobile.replace(',', '').replace('< 10', '0')
|
132 |
+
try:
|
133 |
+
monthly_mobile = int(monthly_mobile)
|
134 |
+
except ValueError:
|
135 |
+
monthly_mobile = 0
|
136 |
+
|
137 |
+
total_searches = monthly_pc + monthly_mobile
|
138 |
+
return (keyword, monthly_pc, monthly_mobile, total_searches)
|
139 |
+
# μ
λ ₯ν ν€μλμ μΌμΉνλ νλͺ©μ΄ μμ κ²½μ°
|
140 |
+
return (keyword, 0, 0, 0)
|
141 |
+
else:
|
142 |
+
return (keyword, 0, 0, 0)
|
143 |
+
except Exception as e:
|
144 |
+
print(f"Error fetching search volumes for keyword '{keyword}': {e}")
|
145 |
+
return (keyword, 0, 0, 0)
|
146 |
|
147 |
+
def get_monthly_search_volumes(keywords, include_related_keywords=True):
|
148 |
+
all_data = []
|
149 |
results = []
|
150 |
+
|
151 |
+
if include_related_keywords:
|
152 |
+
chunk_size = 10 # ν€μλλ₯Ό 10κ°μ© λλμ΄ μμ²
|
153 |
+
# API λ³λ ¬ μμ²
|
154 |
+
with ThreadPoolExecutor(max_workers=5) as executor:
|
155 |
+
futures = [executor.submit(get_keywords_data_chunk, keywords[i:i+chunk_size]) for i in range(0, len(keywords), chunk_size)]
|
156 |
+
for future in futures:
|
157 |
+
try:
|
158 |
+
data = future.result()
|
159 |
+
if 'keywordList' in data:
|
160 |
+
all_data.extend(data['keywordList'])
|
161 |
+
except Exception as e:
|
162 |
+
print(f"Error fetching keywords data chunk: {e}")
|
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 |
+
with ThreadPoolExecutor(max_workers=5) as executor:
|
197 |
+
futures = [executor.submit(get_search_volumes, keyword) for keyword in keywords]
|
198 |
+
for future in futures:
|
199 |
+
try:
|
200 |
+
result = future.result()
|
201 |
+
results.append(result)
|
202 |
+
except Exception as e:
|
203 |
+
print(f"Error fetching search volumes for keyword '{keyword}': {e}")
|
204 |
+
results.append((keyword, 0, 0, 0))
|
205 |
+
|
206 |
+
if not results:
|
207 |
+
return [("Error", "λ°μ΄ν°κ° λ°νλμ§ μμκ±°λ API μλ΅μ΄ μ ν¨νμ§ μμ΅λλ€.", "", "", "")]
|
208 |
+
|
209 |
# λΈλ‘κ·Έ λ¬Έμ μ λ³λ ¬ μμ²
|
210 |
with ThreadPoolExecutor(max_workers=5) as executor:
|
211 |
+
if include_related_keywords:
|
212 |
+
blog_futures = [executor.submit(get_blog_count_parallel, result[0]) for result in results]
|
213 |
+
for i, future in enumerate(blog_futures):
|
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 |
|
|
|
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=display_search_volumes,
|
253 |
+
inputs=[
|
254 |
+
gr.Textbox(placeholder="ν€μλλ₯Ό μ
λ ₯νμΈμ (μΌνλ‘ κ΅¬λΆ)", lines=2),
|
255 |
+
gr.Checkbox(label="μ°κ΄κ²μμ΄ ν¬ν¨", value=True) # μ°κ΄κ²μμ΄ ν κΈ μΆκ°
|
256 |
+
],
|
257 |
outputs=[
|
258 |
gr.Dataframe(headers=["ν€μλ", "PCμκ²μλ", "λͺ¨λ°μΌμκ²μλ", "ν νμκ²μλ", "λΈλ‘κ·Έλ¬Έμμ"]),
|
259 |
gr.File(label="λ€μ΄λ‘λ μμ
νμΌ")
|
260 |
],
|
261 |
title="λ€μ΄λ² μκ²μλ κ²μκΈ°",
|
262 |
+
description="ν€μλμ μ κ²μλκ³Ό λΈλ‘κ·Έ λ¬Έμ μλ₯Ό νμΈν μ μμ΅λλ€. μ°κ΄κ²μμ΄λ₯Ό ν¬ν¨ν μ§ μ ννμΈμ.",
|
263 |
)
|
264 |
|
265 |
+
iface.launch(share=True) # share=Trueλ₯Ό μΆκ°νμ¬ κ³΅κ° λ§ν¬ μμ±
|