""" 트렌드 분석 모듈 - 네이버 데이터랩 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