|
|
""" |
|
|
ํธ๋ ๋ ๋ถ์ ๋ชจ๋ - ๋ค์ด๋ฒ ๋ฐ์ดํฐ๋ฉ API๋ฅผ ํตํ ๊ฒ์ ํธ๋ ๋ ๋ถ์ |
|
|
- ์ฑ์ฅ๋ฅ ์ 3๋
๊ธฐ์ค์ผ๋ก ๋ณ๊ฒฝ |
|
|
- ๋๋น 100% ์ ์ฉ |
|
|
""" |
|
|
|
|
|
import requests |
|
|
import json |
|
|
import pandas as pd |
|
|
import plotly.graph_objects as go |
|
|
import plotly.express as px |
|
|
from datetime import datetime, timedelta |
|
|
import api_utils |
|
|
import keyword_search |
|
|
import logging |
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
logger.setLevel(logging.INFO) |
|
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') |
|
|
handler = logging.StreamHandler() |
|
|
handler.setFormatter(formatter) |
|
|
logger.addHandler(handler) |
|
|
|
|
|
def get_trend_data(keywords, period="1year"): |
|
|
""" |
|
|
๋ค์ด๋ฒ ๋ฐ์ดํฐ๋ฉ API๋ฅผ ํตํด ๊ฒ์ ํธ๋ ๋ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ (์์ ๋ ๋ฒ์ ) |
|
|
|
|
|
Args: |
|
|
keywords (list): ๋ถ์ํ ํค์๋ ๋ชฉ๋ก (์ต๋ 5๊ฐ) |
|
|
period (str): ๋ถ์ ๊ธฐ๊ฐ ("1year" ๋๋ "3year") |
|
|
|
|
|
Returns: |
|
|
dict: ํธ๋ ๋ ๋ฐ์ดํฐ ๋ฐ ๊ทธ๋ํ HTML |
|
|
""" |
|
|
logger.info(f"ํธ๋ ๋ ๋ถ์ ์์: {len(keywords)}๊ฐ ํค์๋, ๊ธฐ๊ฐ: {period}") |
|
|
|
|
|
|
|
|
yesterday = datetime.now() - timedelta(days=1) |
|
|
end_year = yesterday.year |
|
|
end_month = yesterday.month |
|
|
|
|
|
if period == "1year": |
|
|
|
|
|
start_year = end_year - 1 |
|
|
start_month = end_month |
|
|
else: |
|
|
|
|
|
start_year = end_year - 3 |
|
|
start_month = end_month |
|
|
|
|
|
|
|
|
start_date_str = f"{start_year:04d}-{start_month:02d}-01" |
|
|
end_date_str = f"{end_year:04d}-{end_month:02d}-01" |
|
|
|
|
|
logger.info(f"๋ถ์ ๊ธฐ๊ฐ: {start_date_str} ~ {end_date_str} (์๊ฐ ๋ฐ์ดํฐ)") |
|
|
|
|
|
|
|
|
keywords = keywords[:5] |
|
|
|
|
|
|
|
|
api_config = api_utils.get_next_datalab_api_config() |
|
|
if not api_config: |
|
|
logger.error("๋ฐ์ดํฐ๋ฉ API ์ค์ ์ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.") |
|
|
return { |
|
|
"status": "error", |
|
|
"message": "๋ฐ์ดํฐ๋ฉ API ์ค์ ์์" |
|
|
} |
|
|
|
|
|
|
|
|
keyword_groups = [] |
|
|
for keyword in keywords: |
|
|
keyword_groups.append({ |
|
|
'groupName': keyword, |
|
|
'keywords': [keyword] |
|
|
}) |
|
|
|
|
|
|
|
|
body_dict = { |
|
|
'startDate': start_date_str, |
|
|
'endDate': end_date_str, |
|
|
'timeUnit': 'month', |
|
|
'keywordGroups': keyword_groups |
|
|
|
|
|
} |
|
|
|
|
|
body = json.dumps(body_dict) |
|
|
|
|
|
|
|
|
url = "https://openapi.naver.com/v1/datalab/search" |
|
|
headers = { |
|
|
'X-Naver-Client-Id': api_config["CLIENT_ID"], |
|
|
'X-Naver-Client-Secret': api_config["CLIENT_SECRET"], |
|
|
'Content-Type': 'application/json' |
|
|
} |
|
|
|
|
|
try: |
|
|
response = requests.post(url, data=body, headers=headers, timeout=10) |
|
|
|
|
|
if response.status_code == 200: |
|
|
response_json = response.json() |
|
|
|
|
|
|
|
|
trend_data = [] |
|
|
for result in response_json['results']: |
|
|
for data_point in result['data']: |
|
|
trend_data.append({ |
|
|
'keyword': result['title'], |
|
|
'period': data_point['period'], |
|
|
'ratio': data_point['ratio'] |
|
|
}) |
|
|
|
|
|
df_trend = pd.DataFrame(trend_data) |
|
|
|
|
|
|
|
|
search_volumes = keyword_search.fetch_all_search_volumes(keywords) |
|
|
|
|
|
|
|
|
df_trend_with_volume = convert_to_absolute_volume(df_trend, search_volumes) |
|
|
|
|
|
|
|
|
graph_html = create_trend_graph(df_trend_with_volume, period) |
|
|
|
|
|
logger.info(f"ํธ๋ ๋ ๋ถ์ ์๋ฃ: {len(trend_data)}๊ฐ ๋ฐ์ดํฐ ํฌ์ธํธ") |
|
|
|
|
|
return { |
|
|
"status": "success", |
|
|
"trend_data": df_trend_with_volume, |
|
|
"graph_html": graph_html, |
|
|
"period": period, |
|
|
"keywords": keywords |
|
|
} |
|
|
|
|
|
else: |
|
|
logger.error(f"๋ฐ์ดํฐ๋ฉ API ์ค๋ฅ: {response.status_code} - {response.text}") |
|
|
return { |
|
|
"status": "error", |
|
|
"message": f"API ์ค๋ฅ: {response.status_code}" |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"ํธ๋ ๋ ๋ถ์ ์ค ์ค๋ฅ ๋ฐ์: {e}") |
|
|
return { |
|
|
"status": "error", |
|
|
"message": f"๋ถ์ ์ค ์ค๋ฅ: {str(e)}" |
|
|
} |
|
|
|
|
|
def convert_to_absolute_volume(df_trend, search_volumes): |
|
|
""" |
|
|
์๋ ๊ฒ์๋์ ์ ๋ ๊ฒ์๋์ผ๋ก ๋ณํ |
|
|
|
|
|
Args: |
|
|
df_trend (DataFrame): ํธ๋ ๋ ๋ฐ์ดํฐ (์๋๊ฐ) |
|
|
search_volumes (dict): ํ์ฌ ์๋ณ ๊ฒ์๋ ๋ฐ์ดํฐ |
|
|
|
|
|
Returns: |
|
|
DataFrame: ์ ๋ ๊ฒ์๋์ด ์ถ๊ฐ๋ ํธ๋ ๋ ๋ฐ์ดํฐ |
|
|
""" |
|
|
df_result = df_trend.copy() |
|
|
df_result['absolute_volume'] = 0 |
|
|
|
|
|
|
|
|
for keyword in df_trend['keyword'].unique(): |
|
|
keyword_data = df_trend[df_trend['keyword'] == keyword] |
|
|
|
|
|
|
|
|
current_ratio = keyword_data['ratio'].iloc[-1] |
|
|
|
|
|
|
|
|
volume_data = search_volumes.get(keyword.replace(" ", ""), {"์ด๊ฒ์๋": 0}) |
|
|
current_volume = volume_data.get("์ด๊ฒ์๋", 0) |
|
|
|
|
|
if current_ratio > 0 and current_volume > 0: |
|
|
|
|
|
volume_per_percent = current_volume / current_ratio |
|
|
|
|
|
|
|
|
mask = df_result['keyword'] == keyword |
|
|
df_result.loc[mask, 'absolute_volume'] = ( |
|
|
df_result.loc[mask, 'ratio'] * volume_per_percent |
|
|
).astype(int) |
|
|
|
|
|
logger.info(f"'{keyword}': ํ์ฌ ๋น์จ {current_ratio}%, ๊ฒ์๋ {current_volume:,}, 1%๋น {volume_per_percent:.0f}") |
|
|
else: |
|
|
logger.warning(f"'{keyword}': ๊ฒ์๋ ๋ณํ ๋ถ๊ฐ (๋น์จ: {current_ratio}, ๊ฒ์๋: {current_volume})") |
|
|
|
|
|
return df_result |
|
|
|
|
|
def create_trend_graph(df_trend, period): |
|
|
""" |
|
|
ํธ๋ ๋ ๊ทธ๋ํ ์์ฑ (๋๋น 100% ์ ์ฉ) |
|
|
|
|
|
Args: |
|
|
df_trend (DataFrame): ํธ๋ ๋ ๋ฐ์ดํฐ |
|
|
period (str): ๋ถ์ ๊ธฐ๊ฐ |
|
|
|
|
|
Returns: |
|
|
str: HTML ํํ์ ๊ทธ๋ํ |
|
|
""" |
|
|
|
|
|
fig = go.Figure() |
|
|
|
|
|
|
|
|
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'] |
|
|
|
|
|
for i, keyword in enumerate(df_trend['keyword'].unique()): |
|
|
keyword_data = df_trend[df_trend['keyword'] == keyword] |
|
|
|
|
|
fig.add_trace(go.Scatter( |
|
|
x=keyword_data['period'], |
|
|
y=keyword_data['absolute_volume'], |
|
|
mode='lines+markers', |
|
|
name=keyword, |
|
|
line=dict(color=colors[i % len(colors)], width=3), |
|
|
marker=dict(size=6), |
|
|
hovertemplate='<b>%{fullData.name}</b><br>' + |
|
|
'๊ธฐ๊ฐ: %{x}<br>' + |
|
|
'๊ฒ์๋: %{y:,}<br>' + |
|
|
'<extra></extra>' |
|
|
)) |
|
|
|
|
|
|
|
|
period_text = "์ต๊ทผ 1๋
" if period == "1year" else "์ต๊ทผ 3๋
" |
|
|
|
|
|
fig.update_layout( |
|
|
title=f'ํค์๋๋ณ ์๋ณ ๊ฒ์๋ ํธ๋ ๋ ({period_text})', |
|
|
xaxis_title='๊ธฐ๊ฐ', |
|
|
yaxis_title='์๋ณ ๊ฒ์๋', |
|
|
hovermode='x unified', |
|
|
template='plotly_white', |
|
|
height=500, |
|
|
showlegend=True, |
|
|
legend=dict( |
|
|
orientation="h", |
|
|
yanchor="bottom", |
|
|
y=1.02, |
|
|
xanchor="right", |
|
|
x=1 |
|
|
), |
|
|
width=None, |
|
|
autosize=True |
|
|
) |
|
|
|
|
|
|
|
|
fig.update_yaxis(tickformat=',') |
|
|
|
|
|
|
|
|
graph_html = fig.to_html( |
|
|
include_plotlyjs='cdn', |
|
|
div_id="trend-graph", |
|
|
config={'responsive': True} |
|
|
) |
|
|
|
|
|
return graph_html |
|
|
|
|
|
def calculate_3year_growth_rate(volumes): |
|
|
""" |
|
|
3๋
๊ธฐ์ค ์ฑ์ฅ๋ฅ ๊ณ์ฐ (์ ์ฒด ๊ธฐ๊ฐ ๊ธฐ์ค) |
|
|
|
|
|
Args: |
|
|
volumes (list): ์๋ณ ๊ฒ์๋ ๋ฐ์ดํฐ |
|
|
|
|
|
Returns: |
|
|
float: 3๋
๊ธฐ์ค ์ฑ์ฅ๋ฅ |
|
|
""" |
|
|
if len(volumes) < 6: |
|
|
return 0 |
|
|
|
|
|
|
|
|
total_months = len(volumes) |
|
|
period_size = max(1, total_months // 3) |
|
|
|
|
|
|
|
|
early_period = volumes[:period_size] |
|
|
early_avg = sum(early_period) / len(early_period) |
|
|
|
|
|
|
|
|
recent_period = volumes[-period_size:] |
|
|
recent_avg = sum(recent_period) / len(recent_period) |
|
|
|
|
|
if early_avg > 0: |
|
|
return round(((recent_avg - early_avg) / early_avg) * 100, 1) |
|
|
return 0 |
|
|
|
|
|
def analyze_trend_insights(df_trend): |
|
|
""" |
|
|
ํธ๋ ๋ ๋ฐ์ดํฐ์์ ์ธ์ฌ์ดํธ ์ถ์ถ (3๋
๊ธฐ์ค ์ฑ์ฅ๋ฅ ๋ก ๋ณ๊ฒฝ) |
|
|
|
|
|
Args: |
|
|
df_trend (DataFrame): ํธ๋ ๋ ๋ฐ์ดํฐ |
|
|
|
|
|
Returns: |
|
|
dict: ํธ๋ ๋ ์ธ์ฌ์ดํธ |
|
|
""" |
|
|
insights = {} |
|
|
|
|
|
for keyword in df_trend['keyword'].unique(): |
|
|
keyword_data = df_trend[df_trend['keyword'] == keyword].sort_values('period') |
|
|
|
|
|
|
|
|
max_volume = keyword_data['absolute_volume'].max() |
|
|
min_volume = keyword_data['absolute_volume'].min() |
|
|
max_period = keyword_data[keyword_data['absolute_volume'] == max_volume]['period'].iloc[0] |
|
|
min_period = keyword_data[keyword_data['absolute_volume'] == min_volume]['period'].iloc[0] |
|
|
|
|
|
|
|
|
total_avg = keyword_data['absolute_volume'].mean() |
|
|
|
|
|
|
|
|
volumes = keyword_data['absolute_volume'].tolist() |
|
|
growth_rate = calculate_3year_growth_rate(volumes) |
|
|
|
|
|
insights[keyword] = { |
|
|
'max_volume': int(max_volume), |
|
|
'max_period': max_period, |
|
|
'min_volume': int(min_volume), |
|
|
'min_period': min_period, |
|
|
'total_avg': int(total_avg), |
|
|
'growth_rate': growth_rate, |
|
|
'total_months': len(volumes) |
|
|
} |
|
|
|
|
|
return insights |