Kims12 commited on
Commit
bfacc72
Β·
verified Β·
1 Parent(s): b43fad9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +37 -36
app.py CHANGED
@@ -8,13 +8,12 @@ import pandas as pd
8
  import tempfile
9
  import gradio as gr
10
 
11
- # 넀이버 κ΄‘κ³  API 호좜 μ‹œ μ‚¬μš©ν•  μ„œλͺ… 생성 ν•¨μˆ˜
12
  def generate_signature(timestamp, method, uri, secret_key):
13
  message = f"{timestamp}.{method}.{uri}"
14
  digest = hmac.new(secret_key.encode("utf-8"), message.encode("utf-8"), hashlib.sha256).digest()
15
  return base64.b64encode(digest).decode()
16
 
17
- # 넀이버 κ΄‘κ³  API 호좜 헀더 생성 ν•¨μˆ˜
18
  def get_header(method, uri, api_key, secret_key, customer_id):
19
  timestamp = str(round(time.time() * 1000))
20
  signature = generate_signature(timestamp, method, uri, secret_key)
@@ -26,9 +25,8 @@ def get_header(method, uri, api_key, secret_key, customer_id):
26
  "X-Signature": signature
27
  }
28
 
29
- # 넀이버 κ΄‘κ³  APIλ₯Ό 톡해 단일 ν‚€μ›Œλ“œμ˜ 연관검색어 및 κ²€μƒ‰λŸ‰ 정보λ₯Ό κ°€μ Έμ˜€λŠ” ν•¨μˆ˜
30
  def fetch_related_keywords(keyword):
31
- # ν™˜κ²½λ³€μˆ˜μ—μ„œ κ΄‘κ³  API 킀값듀을 λΆˆλŸ¬μ˜΅λ‹ˆλ‹€.
32
  API_KEY = os.environ["NAVER_API_KEY"]
33
  SECRET_KEY = os.environ["NAVER_SECRET_KEY"]
34
  CUSTOMER_ID = os.environ["NAVER_CUSTOMER_ID"]
@@ -51,8 +49,7 @@ def fetch_related_keywords(keyword):
51
 
52
  def parse_count(x):
53
  try:
54
- x_str = str(x).replace(",", "")
55
- return int(x_str)
56
  except:
57
  return 0
58
 
@@ -63,9 +60,8 @@ def fetch_related_keywords(keyword):
63
  result_df = df[["μ •λ³΄ν‚€μ›Œλ“œ", "PCμ›”κ²€μƒ‰λŸ‰", "λͺ¨λ°”μΌμ›”κ²€μƒ‰λŸ‰", "ν† νƒˆμ›”κ²€μƒ‰λŸ‰"]]
64
  return result_df
65
 
66
- # 넀이버 검색 개발 APIλ₯Ό ν™œμš©ν•˜μ—¬ λΈ”λ‘œκ·Έ λ¬Έμ„œμˆ˜λ₯Ό μ‘°νšŒν•˜λŠ” ν•¨μˆ˜
67
  def fetch_blog_count(keyword):
68
- # ν™˜κ²½λ³€μˆ˜μ—μ„œ 넀이버 검색 API 자격증λͺ…을 λΆˆλŸ¬μ˜΅λ‹ˆλ‹€.
69
  client_id = os.environ["NAVER_SEARCH_CLIENT_ID"]
70
  client_secret = os.environ["NAVER_SEARCH_CLIENT_SECRET"]
71
  url = "https://openapi.naver.com/v1/search/blog.json"
@@ -81,38 +77,35 @@ def fetch_blog_count(keyword):
81
  else:
82
  return 0
83
 
84
- # μž„μ‹œ μ—‘μ…€ 파일 생성 ν•¨μˆ˜
85
  def create_excel_file(df):
86
  with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as tmp:
87
  excel_path = tmp.name
88
  df.to_excel(excel_path, index=False)
89
  return excel_path
90
 
91
- # μž…λ ₯된 μ—¬λŸ¬ ν‚€μ›Œλ“œλ₯Ό μ²˜λ¦¬ν•˜λŠ” ν•¨μˆ˜
92
  def process_keyword(keywords: str, include_related: bool):
93
  """
94
- 1. ν…μŠ€νŠΈλ°•μŠ€μ— μ—”ν„°λ‘œ κ΅¬λΆ„λœ μ—¬λŸ¬ ν‚€μ›Œλ“œλ₯Ό λ°›μ•„ 각 ν‚€μ›Œλ“œμ— λŒ€ν•΄ 넀이버 κ΄‘κ³  APIλ₯Ό 톡해 κ²€μƒ‰λŸ‰ 정보λ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€.
95
- 2. 각 ν‚€μ›Œλ“œμ— λŒ€ν•΄ μž…λ ₯ν•œ ν‚€μ›Œλ“œ 자체의 κ²°κ³Όλ₯Ό ν¬ν•¨ν•©λ‹ˆλ‹€.
96
- 3. μ²΄ν¬λ°•μŠ€(True)인 경우, 첫 번째 ν‚€μ›Œλ“œμ— λŒ€ν•΄μ„œλ§Œ 연관검색어(μž…λ ₯ ν‚€μ›Œλ“œλ₯Ό μ œμ™Έν•œ κ²°κ³Ό)λ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.
97
- 4. λ§ˆμ§€λ§‰μœΌλ‘œ, 각 "μ •λ³΄ν‚€μ›Œλ“œ"에 λŒ€ν•΄ 넀이버 검색 APIλ₯Ό ν˜ΈμΆœν•˜μ—¬ λΈ”λ‘œκ·Έ λ¬Έμ„œμˆ˜λ₯Ό μ‘°νšŒν•˜κ³  "λΈ”λ‘œκ·Έλ¬Έμ„œμˆ˜" μ»¬λŸΌμ— μΆ”κ°€ν•©λ‹ˆλ‹€.
98
  """
99
- # μ€„λ°”κΏˆμœΌλ‘œ λΆ„λ¦¬ν•˜μ—¬ μž…λ ₯ ν‚€μ›Œλ“œ 리슀트 생성 (빈 쀄 μ œμ™Έ)
100
- input_keywords = [k.strip() for k in keywords.splitlines() if k.strip() != ""]
101
  result_dfs = []
102
 
103
  for idx, kw in enumerate(input_keywords):
104
  df_kw = fetch_related_keywords(kw)
105
  if df_kw.empty:
106
  continue
107
- # μž…λ ₯ ν‚€μ›Œλ“œ 자체의 κ²°κ³Όλ₯Ό μš°μ„  포함
108
  row_kw = df_kw[df_kw["μ •λ³΄ν‚€μ›Œλ“œ"] == kw]
109
  if not row_kw.empty:
110
  result_dfs.append(row_kw)
111
  else:
112
- # μž…λ ₯ ν‚€μ›Œλ“œμ— ν•΄λ‹Ήν•˜λŠ” 행이 μ—†μœΌλ©΄ 첫 번째 행을 λŒ€μ²΄λ‘œ μΆ”κ°€
113
  result_dfs.append(df_kw.head(1))
114
-
115
- # μ²΄ν¬λ°•μŠ€κ°€ True이고, 첫 번째 ν‚€μ›Œλ“œμ— λŒ€ν•΄μ„œλ§Œ 연관검색어 μΆ”κ°€ (μž…λ ₯ ν‚€μ›Œλ“œ μ œμ™Έ)
116
  if include_related and idx == 0:
117
  df_related = df_kw[df_kw["μ •λ³΄ν‚€μ›Œλ“œ"] != kw]
118
  if not df_related.empty:
@@ -124,32 +117,40 @@ def process_keyword(keywords: str, include_related: bool):
124
  else:
125
  result_df = pd.DataFrame(columns=["μ •λ³΄ν‚€μ›Œλ“œ", "PCμ›”κ²€μƒ‰λŸ‰", "λͺ¨λ°”μΌμ›”κ²€μƒ‰λŸ‰", "ν† νƒˆμ›”κ²€μƒ‰λŸ‰"])
126
 
127
- # λΈ”λ‘œκ·Έ λ¬Έμ„œμˆ˜ 컬럼 μΆ”κ°€: 각 μ •λ³΄ν‚€μ›Œλ“œλ§ˆλ‹€ 넀이버 λΈ”λ‘œκ·Έ 검색 API둜 총 λ¬Έμ„œμˆ˜λ₯Ό 쑰회
128
  result_df["λΈ”λ‘œκ·Έλ¬Έμ„œμˆ˜"] = result_df["μ •λ³΄ν‚€μ›Œλ“œ"].apply(fetch_blog_count)
129
-
130
  result_df.sort_values(by="ν† νƒˆμ›”κ²€μƒ‰λŸ‰", ascending=False, inplace=True)
 
131
  return result_df, create_excel_file(result_df)
132
 
133
- # Gradio UI ꡬ성
134
- with gr.Blocks() as demo:
135
- gr.Markdown("### 넀이버 연관검색어 및 κ²€μƒ‰λŸ‰, λΈ”λ‘œκ·Έ λ¬Έμ„œμˆ˜ 쑰회 μ•±")
136
  gr.Markdown(
137
- "μ—¬λŸ¬ ν‚€μ›Œλ“œλ₯Ό μ—”ν„°λ‘œ κ΅¬λΆ„ν•˜μ—¬ μž…λ ₯ν•˜λ©΄ 각 ν‚€μ›Œλ“œμ˜ κ²€μƒ‰λŸ‰ 정보λ₯Ό μ‘°νšŒν•˜κ³ , "
138
- "첫 번째 ν‚€μ›Œλ“œμ˜ 경우 '연관검색어 포함' 체크 μ‹œ 연관검색어도 ν•¨κ»˜ μ‘°νšŒν•©λ‹ˆλ‹€. "
139
  "λ˜ν•œ, 각 μ •λ³΄ν‚€μ›Œλ“œμ— λŒ€ν•œ 넀이버 λΈ”λ‘œκ·Έ λ¬Έμ„œμˆ˜λ„ ν•¨κ»˜ 좜λ ₯λ©λ‹ˆλ‹€."
140
  )
141
 
142
  with gr.Row():
143
- keyword_input = gr.Textbox(label="ν‚€μ›Œλ“œ μž…λ ₯ (μ—¬λŸ¬ 개일 경우 μ—”ν„°λ‘œ ꡬ뢄)", lines=5, placeholder="예:\nκ°•μ›λ„ν’€λΉŒλΌ\nμžλ°”μŠ€ν¬λ¦½νŠΈ")
144
- include_checkbox = gr.Checkbox(label="연관검색어 포함 (첫번째 ν‚€μ›Œλ“œμ— ν•œν•¨)", value=False)
145
- search_button = gr.Button("검색")
 
 
 
 
 
 
 
 
 
146
 
147
- with gr.Row():
148
- df_output = gr.Dataframe(label="검색 κ²°κ³Ό")
149
- excel_output = gr.File(label="μ—‘μ…€ λ‹€μš΄λ‘œλ“œ")
150
-
151
- # λ²„νŠΌ 클릭 μ‹œ process_keyword ν•¨μˆ˜ μ‹€ν–‰
152
- search_button.click(fn=process_keyword, inputs=[keyword_input, include_checkbox], outputs=[df_output, excel_output])
153
 
154
  # μ•± μ‹€ν–‰ (Hugging Face Spaces 배포 κ°€λŠ₯)
155
  demo.launch()
 
8
  import tempfile
9
  import gradio as gr
10
 
11
+ # --- 넀이버 κ΄‘κ³  API: μ„œλͺ… 생성 및 헀더 ꡬ성 ---
12
  def generate_signature(timestamp, method, uri, secret_key):
13
  message = f"{timestamp}.{method}.{uri}"
14
  digest = hmac.new(secret_key.encode("utf-8"), message.encode("utf-8"), hashlib.sha256).digest()
15
  return base64.b64encode(digest).decode()
16
 
 
17
  def get_header(method, uri, api_key, secret_key, customer_id):
18
  timestamp = str(round(time.time() * 1000))
19
  signature = generate_signature(timestamp, method, uri, secret_key)
 
25
  "X-Signature": signature
26
  }
27
 
28
+ # --- 넀이버 κ΄‘κ³  API: 연관검색어 및 κ²€μƒ‰λŸ‰ 쑰회 ---
29
  def fetch_related_keywords(keyword):
 
30
  API_KEY = os.environ["NAVER_API_KEY"]
31
  SECRET_KEY = os.environ["NAVER_SECRET_KEY"]
32
  CUSTOMER_ID = os.environ["NAVER_CUSTOMER_ID"]
 
49
 
50
  def parse_count(x):
51
  try:
52
+ return int(str(x).replace(",", ""))
 
53
  except:
54
  return 0
55
 
 
60
  result_df = df[["μ •λ³΄ν‚€μ›Œλ“œ", "PCμ›”κ²€μƒ‰λŸ‰", "λͺ¨λ°”μΌμ›”κ²€μƒ‰λŸ‰", "ν† νƒˆμ›”κ²€μƒ‰λŸ‰"]]
61
  return result_df
62
 
63
+ # --- 넀이버 검색 API: λΈ”λ‘œκ·Έ λ¬Έμ„œμˆ˜ 쑰회 ---
64
  def fetch_blog_count(keyword):
 
65
  client_id = os.environ["NAVER_SEARCH_CLIENT_ID"]
66
  client_secret = os.environ["NAVER_SEARCH_CLIENT_SECRET"]
67
  url = "https://openapi.naver.com/v1/search/blog.json"
 
77
  else:
78
  return 0
79
 
80
+ # --- μž„μ‹œ μ—‘μ…€ 파일 생성 ---
81
  def create_excel_file(df):
82
  with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as tmp:
83
  excel_path = tmp.name
84
  df.to_excel(excel_path, index=False)
85
  return excel_path
86
 
87
+ # --- μž…λ ₯ ν‚€μ›Œλ“œ 처리 ν•¨μˆ˜ ---
88
  def process_keyword(keywords: str, include_related: bool):
89
  """
90
+ 1. μ—¬λŸ¬ ν‚€μ›Œλ“œλ₯Ό μ—”ν„°λ‘œ κ΅¬λΆ„ν•˜μ—¬ 리슀트둜 λ§Œλ“­λ‹ˆλ‹€.
91
+ 2. 각 ν‚€μ›Œλ“œμ— λŒ€ν•΄ 넀이버 κ΄‘κ³  API둜 κ²€μƒ‰λŸ‰ 정보λ₯Ό μ‘°νšŒν•˜κ³ ,
92
+ 첫 번째 ν‚€μ›Œλ“œμ— λŒ€ν•΄ μ˜΅μ…˜(연관검색어 포함)이 True인 경우 연관검색어도 μΆ”κ°€ν•©λ‹ˆλ‹€.
93
+ 3. μ΅œμ’… κ²°κ³Ό DataFrame에 각 "μ •λ³΄ν‚€μ›Œλ“œ"λ§ˆλ‹€ 넀이버 검색 API둜 λΈ”λ‘œκ·Έ λ¬Έμ„œμˆ˜λ₯Ό μ‘°νšŒν•˜μ—¬ "λΈ”λ‘œκ·Έλ¬Έμ„œμˆ˜" μ»¬λŸΌμ„ μΆ”κ°€ν•©λ‹ˆλ‹€.
94
  """
95
+ input_keywords = [k.strip() for k in keywords.splitlines() if k.strip()]
 
96
  result_dfs = []
97
 
98
  for idx, kw in enumerate(input_keywords):
99
  df_kw = fetch_related_keywords(kw)
100
  if df_kw.empty:
101
  continue
102
+ # μž…λ ₯ ν‚€μ›Œλ“œμ— ν•΄λ‹Ήν•˜λŠ” κ²°κ³Ό 포함
103
  row_kw = df_kw[df_kw["μ •λ³΄ν‚€μ›Œλ“œ"] == kw]
104
  if not row_kw.empty:
105
  result_dfs.append(row_kw)
106
  else:
 
107
  result_dfs.append(df_kw.head(1))
108
+ # 첫 번째 ν‚€μ›Œλ“œμ˜ 연관검색어 μΆ”κ°€ (μž…λ ₯ ν‚€μ›Œλ“œ μ œμ™Έ)
 
109
  if include_related and idx == 0:
110
  df_related = df_kw[df_kw["μ •λ³΄ν‚€μ›Œλ“œ"] != kw]
111
  if not df_related.empty:
 
117
  else:
118
  result_df = pd.DataFrame(columns=["μ •λ³΄ν‚€μ›Œλ“œ", "PCμ›”κ²€μƒ‰λŸ‰", "λͺ¨λ°”μΌμ›”κ²€μƒ‰λŸ‰", "ν† νƒˆμ›”κ²€μƒ‰λŸ‰"])
119
 
120
+ # 각 μ •λ³΄ν‚€μ›Œλ“œμ— λŒ€ν•΄ λΈ”λ‘œκ·Έ λ¬Έμ„œμˆ˜ 쑰회
121
  result_df["λΈ”λ‘œκ·Έλ¬Έμ„œμˆ˜"] = result_df["μ •λ³΄ν‚€μ›Œλ“œ"].apply(fetch_blog_count)
 
122
  result_df.sort_values(by="ν† νƒˆμ›”κ²€μƒ‰λŸ‰", ascending=False, inplace=True)
123
+
124
  return result_df, create_excel_file(result_df)
125
 
126
+ # --- Gradio UI ꡬ성 ---
127
+ with gr.Blocks(css=".gradio-container { max-width: 960px; margin: auto; }") as demo:
128
+ gr.Markdown("# 넀이버 연관검색어, κ²€μƒ‰λŸ‰ 및 λΈ”λ‘œκ·Έ λ¬Έμ„œμˆ˜ 쑰회")
129
  gr.Markdown(
130
+ "μ—¬λŸ¬ ν‚€μ›Œλ“œλ₯Ό **μ—”ν„°**둜 κ΅¬λΆ„ν•˜μ—¬ μž…λ ₯ν•˜μ„Έμš”. 각 ν‚€μ›Œλ“œμ— λŒ€ν•œ κ²€μƒ‰λŸ‰ 정보λ₯Ό μ‘°νšŒν•˜λ©°, "
131
+ "첫 번째 ν‚€μ›Œλ“œμ— λŒ€ν•΄ '연관검색어 포함' μ˜΅μ…˜μ„ μ„ νƒν•˜λ©΄ 연관검색어 결과도 ν•¨κ»˜ μ‘°νšŒλ©λ‹ˆλ‹€. \n\n"
132
  "λ˜ν•œ, 각 μ •λ³΄ν‚€μ›Œλ“œμ— λŒ€ν•œ 넀이버 λΈ”λ‘œκ·Έ λ¬Έμ„œμˆ˜λ„ ν•¨κ»˜ 좜λ ₯λ©λ‹ˆλ‹€."
133
  )
134
 
135
  with gr.Row():
136
+ with gr.Column(scale=1):
137
+ keyword_input = gr.Textbox(
138
+ label="ν‚€μ›Œλ“œ μž…λ ₯ (μ—¬λŸ¬ 개일 경우 μ—”ν„°λ‘œ ꡬ뢄)",
139
+ lines=6,
140
+ placeholder="예:\nκ°•μ›λ„ν’€λΉŒλΌ\nμžλ°”μŠ€ν¬λ¦½νŠΈ"
141
+ )
142
+ include_checkbox = gr.Checkbox(label="연관검색어 포함 (첫번째 ν‚€μ›Œλ“œμ— ν•œν•¨)", value=False)
143
+ search_button = gr.Button("검색", variant="primary")
144
+ with gr.Column(scale=1):
145
+ gr.Markdown("### 검색 κ²°κ³Ό")
146
+ df_output = gr.Dataframe(label="κ²°κ³Ό ν…Œμ΄λΈ”")
147
+ excel_output = gr.File(label="μ—‘μ…€ λ‹€μš΄λ‘œλ“œ")
148
 
149
+ search_button.click(
150
+ fn=process_keyword,
151
+ inputs=[keyword_input, include_checkbox],
152
+ outputs=[df_output, excel_output]
153
+ )
 
154
 
155
  # μ•± μ‹€ν–‰ (Hugging Face Spaces 배포 κ°€λŠ₯)
156
  demo.launch()