- 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>
7.8 KiB
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 로그인
- VNC로 접속 (
vnc://공인IP:5901) - Chrome에서 YouTube가 열려있는지 확인
- YouTube에 Google 계정으로 로그인
- 로그인 상태 유지 — 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 자동 실행 |