docs YouTube Analytics API 수정 및 오디언스 인사이트 단위 수정

subtitle
김성경 2026-02-27 15:26:38 +09:00
parent a1192193e5
commit 8fe0512608
4 changed files with 84 additions and 42 deletions

View File

@ -267,7 +267,7 @@ async def get_dashboard_stats(
Dashboard.platform == "youtube", Dashboard.platform == "youtube",
Dashboard.platform_user_id == social_account.platform_user_id, Dashboard.platform_user_id == social_account.platform_user_id,
Dashboard.uploaded_at >= start_dt, Dashboard.uploaded_at >= start_dt,
Dashboard.uploaded_at <= kpi_end_dt, Dashboard.uploaded_at <= end_dt,
) )
) )
period_video_count = count_result.scalar() or 0 period_video_count = count_result.scalar() or 0

View File

@ -418,8 +418,8 @@ class DataProcessor:
# === 연령/성별 데이터 처리 === # === 연령/성별 데이터 처리 ===
demo_rows = demographics_data.get("rows", []) demo_rows = demographics_data.get("rows", [])
age_map: dict[str, int] = {} age_map: dict[str, float] = {}
gender_map = {"male": 0, "female": 0} gender_map_f: dict[str, float] = {"male": 0.0, "female": 0.0}
for row in demo_rows: for row in demo_rows:
if len(row) < 3: if len(row) < 3:
@ -427,27 +427,21 @@ class DataProcessor:
age_group = row[0] # "age18-24" age_group = row[0] # "age18-24"
gender = row[1] # "male" or "female" gender = row[1] # "male" or "female"
views = row[2] viewer_pct = row[2] # viewerPercentage (이미 % 값, 예: 45.5)
# 연령대별 집계 (age18-24 → 18-24) # 연령대별 집계: 남녀 비율 합산 (age18-24 → 18-24)
age_label = age_group.replace("age", "") age_label = age_group.replace("age", "")
age_map[age_label] = age_map.get(age_label, 0) + views age_map[age_label] = age_map.get(age_label, 0.0) + viewer_pct
# 성별 집계 # 성별 집계
if gender in gender_map: if gender in gender_map_f:
gender_map[gender] += views gender_map_f[gender] += viewer_pct
# 연령대별 비율 계산
total_demo_views = sum(age_map.values())
age_groups = [ age_groups = [
{ {"label": age, "percentage": int(round(pct))}
"label": age, for age, pct in sorted(age_map.items())
"percentage": int(
(count / total_demo_views * 100) if total_demo_views > 0 else 0
),
}
for age, count in sorted(age_map.items())
] ]
gender_map = {k: int(round(v)) for k, v in gender_map_f.items()}
# === 지역 데이터 처리 === # === 지역 데이터 처리 ===
geo_rows = geography_data.get("rows", []) geo_rows = geography_data.get("rows", [])

View File

@ -124,7 +124,7 @@ class YouTubeAnalyticsService:
self._fetch_monthly_data(video_ids, previous_start, previous_end, access_token), self._fetch_monthly_data(video_ids, previous_start, previous_end, access_token),
self._fetch_top_videos(video_ids, start_date, _kpi_end, access_token), self._fetch_top_videos(video_ids, start_date, _kpi_end, access_token),
self._fetch_demographics(start_date, _kpi_end, access_token), self._fetch_demographics(start_date, _kpi_end, access_token),
self._fetch_region(video_ids, start_date, _kpi_end, access_token), self._fetch_region(start_date, _kpi_end, access_token),
] ]
else: # mode == "day" else: # mode == "day"
tasks = [ tasks = [
@ -134,7 +134,7 @@ class YouTubeAnalyticsService:
self._fetch_daily_data(video_ids, day_previous_start, day_previous_end, access_token), self._fetch_daily_data(video_ids, day_previous_start, day_previous_end, access_token),
self._fetch_top_videos(video_ids, start_date, end_date, access_token), self._fetch_top_videos(video_ids, start_date, end_date, access_token),
self._fetch_demographics(start_date, end_date, access_token), self._fetch_demographics(start_date, end_date, access_token),
self._fetch_region(video_ids, start_date, end_date, access_token), self._fetch_region(start_date, end_date, access_token),
] ]
# 병렬 실행 # 병렬 실행
@ -206,8 +206,6 @@ class YouTubeAnalyticsService:
estimatedMinutesWatched, averageViewDuration, estimatedMinutesWatched, averageViewDuration,
subscribersGained] subscribersGained]
Note:
annotationClickThroughRate는 2019 annotations 기능 제거로 deprecated.
""" """
logger.debug( logger.debug(
f"[YouTubeAnalyticsService._fetch_kpi] START - video_count={len(video_ids)}" f"[YouTubeAnalyticsService._fetch_kpi] START - video_count={len(video_ids)}"
@ -218,7 +216,7 @@ class YouTubeAnalyticsService:
"startDate": start_date, "startDate": start_date,
"endDate": end_date, "endDate": end_date,
"metrics": "views,likes,comments,shares,estimatedMinutesWatched,averageViewDuration,subscribersGained", "metrics": "views,likes,comments,shares,estimatedMinutesWatched,averageViewDuration,subscribersGained",
# "filters": f"video=={','.join(video_ids)}", "filters": f"video=={','.join(video_ids)}",
} }
result = await self._call_api(params, access_token) result = await self._call_api(params, access_token)
@ -393,7 +391,6 @@ class YouTubeAnalyticsService:
async def _fetch_region( async def _fetch_region(
self, self,
video_ids: list[str],
start_date: str, start_date: str,
end_date: str, end_date: str,
access_token: str, access_token: str,
@ -403,7 +400,6 @@ class YouTubeAnalyticsService:
지역별 조회수 분포를 조회합니다 (상위 5). 지역별 조회수 분포를 조회합니다 (상위 5).
Args: Args:
video_ids: YouTube 영상 ID 리스트
start_date: 조회 시작일 (YYYY-MM-DD) start_date: 조회 시작일 (YYYY-MM-DD)
end_date: 조회 종료일 (YYYY-MM-DD) end_date: 조회 종료일 (YYYY-MM-DD)
access_token: OAuth 2.0 액세스 토큰 access_token: OAuth 2.0 액세스 토큰
@ -421,9 +417,7 @@ class YouTubeAnalyticsService:
"endDate": end_date, "endDate": end_date,
"dimensions": "country", "dimensions": "country",
"metrics": "views", "metrics": "views",
"filters": f"video=={','.join(video_ids)}",
"sort": "-views", "sort": "-views",
"maxResults": "5",
} }
result = await self._call_api(params, access_token) result = await self._call_api(params, access_token)

View File

@ -70,7 +70,7 @@ API 요청 시 `dimensions` 파라미터에 들어갈 수 있는 값들입니다
| -------------- | ------- | --------------------------- | -------------- | | -------------- | ------- | --------------------------- | -------------- |
| `**ageGroup`** | **연령대** | 시청자 연령 분포 (18-24, 25-34...) | `video`와 혼용 불가 | | `**ageGroup`** | **연령대** | 시청자 연령 분포 (18-24, 25-34...) | `video`와 혼용 불가 |
| `**gender`** | **성별** | 남녀 성비 (male, female) | `video`와 혼용 불가 | | `**gender`** | **성별** | 남녀 성비 (male, female) | `video`와 혼용 불가 |
| `**country`** | **국가** | 국가별 시청자 수 (KR, US...) | 지도 차트용 | | `**country`** | **국가** | 국가별 시청자 수 (KR, US...) | 지도 차트용, 채널 전체 기준 |
### C. 유입 및 기기 (Traffic Device) ### C. 유입 및 기기 (Traffic Device)
@ -84,22 +84,76 @@ API 요청 시 `dimensions` 파라미터에 들어갈 수 있는 값들입니다
--- ---
## 3. 자주 쓰는 조합 (Best Practice) ## 3. 현재 사용 중인 API 호출 조합
대시보드에서 실제로 사용하는 7가지 호출 조합입니다. 모두 `ids=channel==MINE`으로 고정합니다.
### 1. KPI 요약 (`_fetch_kpi`) — 현재/이전 기간 각 1회
| 파라미터 | 값 |
| ---------- | ------------------------------------------------------------------------------------- |
| dimensions | (없음) |
| metrics | `views, likes, comments, shares, estimatedMinutesWatched, averageViewDuration, subscribersGained` |
| filters | `video==ID1,ID2,...` (업로드된 영상 ID 최대 30개) |
> 현재/이전 기간을 각각 호출하여 trend(증감률) 계산에 사용.
---
### 2. 월별 추이 차트 (`_fetch_monthly_data`) — 최근 12개월 / 이전 12개월 각 1회
| 파라미터 | 값 |
| ---------- | -------------------- |
| dimensions | `month` |
| metrics | `views` |
| filters | `video==ID1,ID2,...` |
| sort | `month` |
---
### 3. 일별 추이 차트 (`_fetch_daily_data`) — 최근 30일 / 이전 30일 각 1회
| 파라미터 | 값 |
| ---------- | -------------------- |
| dimensions | `day` |
| metrics | `views` |
| filters | `video==ID1,ID2,...` |
| sort | `day` |
---
### 4. 인기 영상 TOP 4 (`_fetch_top_videos`)
| 파라미터 | 값 |
| ---------- | ------------------------ |
| dimensions | `video` |
| metrics | `views, likes, comments` |
| filters | `video==ID1,ID2,...` |
| sort | `-views` |
| maxResults | `4` |
---
### 5. 시청자 연령/성별 분포 (`_fetch_demographics`) — 채널 전체 기준
| 파라미터 | 값 |
| ---------- | ----------------------- |
| dimensions | `ageGroup, gender` |
| metrics | `viewerPercentage` |
> `ageGroup`, `gender` 차원은 `video` 필터와 혼용 불가 → 채널 전체 시청자 기준.
---
### 6. 지역별 조회수 TOP 5 (`_fetch_region`) — 채널 전체 기준
| 파라미터 | 값 |
| ---------- | -------------------- |
| dimensions | `country` |
| metrics | `views` |
| sort | `-views` |
| maxResults | `5` |
1. **프로젝트 전체 요약 (KPI)**
- `dimensions`: (없음)
- `metrics`: `views,likes,estimatedRevenue`
- `filters`: `video==ID1,ID2...`
2. **일별 성장 그래프 (Line Chart)**
- `dimensions`: `day`
- `metrics`: `views`
- `filters`: `video==ID1,ID2...`
- `sort`: `day`
3. **인기 영상 랭킹 (Table)**
- `dimensions`: `video`
- `metrics`: `views,averageViewDuration`
- `filters`: `video==ID1,ID2...`
- `sort`: `-views`
--- ---