# 네이버 데이터랩 검색어 트렌드로 성별×연령 추정 비중을 산출하는 모듈 # 주의: 세그먼트별 독립 정규화를 앵커 키워드 비율로 상쇄한 '추정' 상대값. 절대 검색량 아님. import os from datetime import date, timedelta import requests URL = "https://openapi.naver.com/v1/datalab/search" ANCHOR = "날씨" # 정규화 상쇄용 광범위 키워드 (가정: 연령·성별 분포가 비교적 고름) AGE_BANDS = { # 표시 구간 → 데이터랩 연령 코드 "20대 이하": ["1", "2", "3", "4"], # ~29세 "30대": ["5", "6"], "40대": ["7", "8"], "50대 이상": ["9", "10", "11"], # 50세~ } GENDERS = {"m": "남성", "f": "여성"} def _headers() -> dict: # .strip(): 시크릿 등록 시 딸려온 개행/공백이 HTTP 헤더 검증을 깨는 것 방지(HF Secrets 붙여넣기 이슈) return { "X-Naver-Client-Id": os.environ["NAVER_CLIENT_ID"].strip(), "X-Naver-Client-Secret": os.environ["NAVER_CLIENT_SECRET"].strip(), "Content-Type": "application/json", } def _segment_score(keyword: str, gender: str, ages: list, start: str, end: str) -> float: # 한 세그먼트에서 [키워드, 앵커]를 같은 요청으로 정규화 → 키워드/앵커 평균비율 반환 body = { "startDate": start, "endDate": end, "timeUnit": "month", "keywordGroups": [ {"groupName": "kw", "keywords": [keyword]}, {"groupName": "anchor", "keywords": [ANCHOR]}, ], "gender": gender, "ages": ages, } res = requests.post(URL, json=body, headers=_headers(), timeout=10) res.raise_for_status() groups = {g["title"]: g["data"] for g in res.json()["results"]} def avg(rows): return sum(d["ratio"] for d in rows) / len(rows) if rows else 0.0 kw, anchor = avg(groups.get("kw", [])), avg(groups.get("anchor", [])) return kw / anchor if anchor else 0.0 def demographics(keyword: str) -> dict: # 성별×연령 8개 세그먼트의 추정 비중(%) 반환 end = date.today().replace(day=1) - timedelta(days=1) # 지난달 말일 start = end.replace(year=end.year - 1, day=1) # 약 13개월 전 1일 cells, total = [], 0.0 for g, glabel in GENDERS.items(): for alabel, ages in AGE_BANDS.items(): score = _segment_score(keyword, g, ages, start.isoformat(), end.isoformat()) cells.append({"gender": glabel, "age": alabel, "score": round(score, 4)}) total += score for c in cells: c["pct"] = round(c["score"] / total * 100, 1) if total else 0.0 return {"cells": cells, "anchor": ANCHOR}