"""
트렌드 분석 모듈 - 네이버 데이터랩 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":
# 1년 전 같은 달
start_year = end_year - 1
start_month = end_month
else: # 3year
# 3년 전 같은 달
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} (월간 데이터)")
# 키워드는 최대 5개까지만 처리
keywords = keywords[:5]
# API 설정 가져오기
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]
})
# API 요청 데이터 (device 파라미터 제거)
body_dict = {
'startDate': start_date_str,
'endDate': end_date_str,
'timeUnit': 'month',
'keywordGroups': keyword_groups
# device 파라미터 제거 → 전체 환경(PC+모바일) 조회
}
body = json.dumps(body_dict)
# API 호출
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)
# 현재 월별 검색량 조회 (검색광고 API)
search_volumes = keyword_search.fetch_all_search_volumes(keywords)
# 절대 검색량으로 변환
df_trend_with_volume = convert_to_absolute_volume(df_trend, search_volumes)
# 그래프 생성 (너비 100% 적용)
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]
# 현재 월의 검색량 (PC + 모바일)
volume_data = search_volumes.get(keyword.replace(" ", ""), {"총검색량": 0})
current_volume = volume_data.get("총검색량", 0)
if current_ratio > 0 and current_volume > 0:
# 1%당 검색량 계산
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 형태의 그래프
"""
# Plotly 그래프 생성
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='%{fullData.name}
' +
'기간: %{x}
' +
'검색량: %{y:,}
' +
''
))
# 레이아웃 설정 (너비 100% 적용)
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 # 자동 크기 조정
)
# y축 포맷 설정 (천 단위 구분)
fig.update_yaxis(tickformat=',')
# HTML로 변환 (너비 100% 적용)
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: # 최소 6개월 데이터 필요
return 0
# 전체 기간을 3등분하여 성장률 계산
total_months = len(volumes)
period_size = max(1, total_months // 3) # 최소 1개월
# 초기 기간 평균 (첫 1/3)
early_period = volumes[:period_size]
early_avg = sum(early_period) / len(early_period)
# 최근 기간 평균 (마지막 1/3)
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()
# 3년 기준 성장률 계산 (전체 기간 기준)
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