Files
sundol/docs/youtube-transcript-guide.md
joungmin 4cde775809 Switch to user Chrome CDP for YouTube transcript, fix auth and ads
- Replace Playwright standalone browser with CDP connection to user Chrome
  (bypasses YouTube bot detection by using logged-in Chrome session)
- Add video playback, ad detection/skip, and play confirmation before transcript extraction
- Extract transcript JS to separate resource files (fix SyntaxError in evaluate)
- Add ytInitialPlayerResponse-based transcript extraction as primary method
- Fix token refresh: retry on network error during backend restart
- Fix null userId logout, CLOB type hint for structured_content
- Disable XFCE screen lock/screensaver
- Add troubleshooting entries (#10-12) and YouTube transcript guide

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 21:01:49 +00:00

7.8 KiB

YouTube 트랜스크립트 추출 가이드

아키텍처

┌──────────────────────────────────────────────────┐
│              사용자 Chrome 브라우저                │
│  (VNC에서 직접 YouTube 로그인된 상태)              │
│  --remote-debugging-port=9222                     │
│  --remote-allow-origins=*                         │
└──────────────┬───────────────────────────────────┘
               │ CDP (Chrome DevTools Protocol)
┌──────────────▼───────────────────────────────────┐
│         PlaywrightBrowserService                  │
│  playwright.chromium().connectOverCDP()            │
│  → 사용자 Chrome의 세션/쿠키를 그대로 사용          │
└──────────────┬───────────────────────────────────┘
               │
┌──────────────▼───────────────────────────────────┐
│        YouTubeTranscriptService                   │
│  1차: youtube-transcript-api 라이브러리            │
│  2차: Playwright → 사용자 Chrome 탭으로 추출       │
└──────────────────────────────────────────────────┘

핵심: 왜 사용자 Chrome인가?

Playwright 자체 브라우저의 한계

  • Playwright가 실행하는 Chromium에는 --enable-automation 플래그가 붙음
  • YouTube가 이를 감지하여 봇으로 판정 → 자막 API 차단
  • 쿠키를 로드해도, 직접 로그인을 시도해도 차단됨
  • Google 로그인 자체가 "안전하지 않은 브라우저"로 거부됨

사용자 Chrome + CDP 방식

  • VNC에서 일반 Chrome으로 YouTube에 로그인
  • 백엔드가 CDP로 해당 Chrome에 연결하여 새 탭을 열어 작업
  • 로그인 세션이 그대로 유지되므로 봇 판정 우회
  • Chrome 자체는 종료하지 않고 연결만 관리

Chrome 실행 방법

필수 옵션

DISPLAY=:1 google-chrome \
  --remote-debugging-port=9222 \
  --remote-allow-origins=* \
  --user-data-dir=/tmp/chrome-debug-profile \
  --no-default-browser-check \
  "https://www.youtube.com"
옵션 설명
--remote-debugging-port=9222 CDP 접속 포트
--remote-allow-origins=* CDP WebSocket 연결 허용
--user-data-dir 프로필 디렉토리 (기본과 다른 경로 필요)

Chrome 프로필 복사 (기존 로그인 유지)

cp -r /home/opc/.config/google-chrome /tmp/chrome-debug-profile

CDP 연결 확인

curl -s http://localhost:9222/json/version | python3 -m json.tool

YouTube 로그인

  1. VNC로 접속 (vnc://공인IP:5901)
  2. Chrome에서 YouTube가 열려있는지 확인
  3. YouTube에 Google 계정으로 로그인
  4. 로그인 상태 유지 — Chrome을 닫지 않음

Chrome을 닫으면 CDP 연결이 끊기고 백엔드가 재연결을 시도함. Chrome을 다시 시작하면 다시 로그인 필요.

트랜스크립트 추출 흐름

1차: youtube-transcript-api 라이브러리

videoId → TranscriptApi.listTranscripts()
  → 수동 자막(ko, en) 우선
  → 자동 생성 자막 fallback
  • 서버 IP가 봇으로 판정되면 실패 (대부분 실패)

2차: Playwright + 사용자 Chrome

1. 사용자 Chrome에 새 탭 열기 (DOMCONTENTLOADED, 60초 타임아웃)
2. DOM 렌더링 대기 (3초)
3. 동영상 재생 시작 (playVideo)
   - 쿠키 동의 팝업 닫기
   - 마우스 호버 → 재생 버튼 클릭
4. 광고 대기 (waitForAdsToFinish, 최대 60초)
   - 스킵 버튼 자동 클릭
   - 광고 끝날 때까지 반복 확인
5. 실제 영상 재생 확인 (waitForVideoPlaying, 최대 15초)
   - video.currentTime > 0.5 확인
   - 일시정지면 재시도
6. ytInitialPlayerResponse에서 자막 URL 추출 + fetch (fetchTranscriptFromPageJs)
7. 실패 시 자막 패널 열기 (extractTranscriptFromPanel)
   - 'Show transcript' 버튼 클릭
   - 자막 세그먼트 DOM에서 텍스트 추출
   - 최대 30초 반복 확인
8. 완료 후 about:blank로 이동 (탭 유지)

광고 처리

감지 방법

  • #movie_player.ad-showing 클래스 확인 (가장 신뢰할 수 있음)
  • .ytp-ad-player-overlay 오버레이 확인
  • 광고 텍스트 배지 확인 (.ytp-ad-text)

스킵 버튼 셀렉터

.ytp-skip-ad-button,
.ytp-ad-skip-button,
.ytp-ad-skip-button-modern,
.ytp-ad-skip-button-container button

주의사항

  • 광고 스킵 후 두 번째 광고가 나올 수 있음 → no_ad 상태 확인까지 반복
  • 광고 중에는 NETWORKIDLE에 도달하지 못함 → DOMCONTENTLOADED 사용

쿠키 관리

CDP 방식에서는 쿠키 파일 불필요

  • 사용자 Chrome에 직접 연결하므로 cookies.txt 불필요
  • Chrome 세션의 쿠키가 자동으로 사용됨

Chrome 쿠키 수동 export (참고용)

Chrome을 --remote-debugging-port=9222로 실행 후:

import json, websocket, urllib.request

tabs = json.loads(urllib.request.urlopen("http://localhost:9222/json").read())
ws_url = tabs[0]["webSocketDebuggerUrl"]
ws = websocket.create_connection(ws_url)
ws.send(json.dumps({"id": 1, "method": "Network.getAllCookies"}))
result = json.loads(ws.recv())
cookies = result["result"]["cookies"]
ws.close()

Chrome의 쿠키 DB(~/.config/google-chrome/Default/Cookies)는 암호화되어 있어 직접 읽기 어려움. CDP를 통해 복호화된 쿠키를 가져오는 것이 가장 확실한 방법.

PM2 + Chrome 자동 시작

Chrome이 서버 재부팅 시에도 자동 시작되도록:

# pm2로 Chrome 관리하지 않음 (GUI 앱이라 적합하지 않음)
# 대신 VNC 시작 시 자동 실행하도록 ~/.vnc/xstartup에 추가:

~/.vnc/xstartup:

#!/bin/bash
exec startxfce4 &

# Chrome을 CDP 모드로 자동 시작
sleep 5
google-chrome --remote-debugging-port=9222 \
  --remote-allow-origins=* \
  --user-data-dir=/tmp/chrome-debug-profile \
  --no-default-browser-check \
  "https://www.youtube.com" &

트러블슈팅

Chrome CDP 연결 실패

Chrome CDP 연결 실패: Chrome이 --remote-debugging-port=9222로 실행 중인지 확인하세요.

→ Chrome 재시작:

DISPLAY=:1 google-chrome --remote-debugging-port=9222 --remote-allow-origins=* \
  --user-data-dir=/tmp/chrome-debug-profile --no-default-browser-check "https://www.youtube.com" &

자막 패널이 ghost-cards만 표시

  • YouTube가 자막 데이터 로드를 차단한 상태
  • Chrome에서 YouTube 로그인이 풀렸는지 VNC에서 확인
  • 로그인 상태라면 Chrome 재시작 후 다시 로그인

"안전하지 않은 브라우저" 로그인 거부

  • Playwright 자체 브라우저에서 발생 (정상)
  • 사용자 Chrome에서만 로그인 가능
  • CDP 방식으로 전환하면 해결

광고 중에 페이지가 닫힘

  • YouTube 페이지 로드 시 NETWORKIDLE 사용하면 광고 때문에 타임아웃
  • DOMCONTENTLOADED + 60초 타임아웃으로 변경하여 해결

관련 파일

파일 역할
PlaywrightBrowserService.java 사용자 Chrome에 CDP 연결, 탭 관리
YouTubeTranscriptService.java 자막 추출 로직 (API → Playwright fallback)
youtube-transcript-extract.js 자막 패널 DOM 추출 JS
youtube-transcript-from-page.js ytInitialPlayerResponse 기반 자막 추출 JS
~/.vnc/xstartup VNC 시작 시 Chrome 자동 실행