이렇게 requests.get("https://www.youtube.com/...")로 페이지를 가져와 BeautifulSoup로 비디오 제목을 찾으려 해 본 적이 있다면, 결과는 이미 예상하셨을 겁니다. 비어 있는 <div> 태그만 잔뜩 돌아오고, 쓸 만한 데이터는 하나도 없죠.
YouTube를 처음 스크래핑하려는 개발자들이 가장 자주 겪는 답답함이 바로 이겁니다. YouTube는 싱글 페이지 애플리케이션(SPA)이라서, 대부분의 콘텐츠를 JavaScript로 클라이언트 측에서 렌더링합니다. 즉, Python 스크립트가 받는 HTML은 껍데기에 불과합니다. 실제 비디오 제목, 조회수, 메타데이터는 어디에 있냐고요? 페이지가 로드된 뒤 JS가 주입하는 거대한 JSON 블록 ytInitialData 안에 숨어 있습니다.
그래서 soup.find("div", class_="ytd-video-renderer")처럼 아주 그럴듯한 코드를 써도 None이 나오는 겁니다. 그 요소가 원본 HTTP 응답에는 아예 존재하지 않기 때문이죠. 이걸 이해하고 나서야 퍼즐이 맞춰졌고, 아래 4가지 방법은 수많은 테스트와 실패, 그리고 GitHub 이슈를 끝없이 뒤적인 결과물입니다. 각 접근법을 하나씩 짚어 드리고, 어떤 상황에 어떤 방법을 써야 하는지 정확히 알려드리겠습니다. 마지막에는 프로젝트 환경 설정 없이 그냥 데이터만 빠르게 뽑고 싶은 분들을 위한 노코드 지름길도 소개합니다.
왜 굳이 Python으로 YouTube를 스크래핑할까?
YouTube는 단순한 동영상 플랫폼이 아니라 에 달하는 거대한 데이터 소스입니다. 과 덕분에, 기업과 연구자, 크리에이터들은 이 공개 데이터를 프로그램으로 분석하고 싶어 합니다.
문제는 YouTube의 내장 분석 도구가 내 채널 데이터만 보여준다는 점입니다. 경쟁사의 업로드 주기, 내 업계의 트렌딩 주제, 다른 사람 영상 댓글의 반응을 분석하고 싶다면 직접 스크래핑해야 합니다.
실제로 가장 많이 쓰이는 사례는 다음과 같습니다.
| 사용 사례 | 필요한 사람 | 포함 데이터 |
|---|---|---|
| 경쟁사 분석 | 마케팅 팀, 콘텐츠 전략가 | 조회수, 업로드 빈도, 참여율 |
| 리드 생성 | 영업팀, B2B 아웃리치 | 채널 연락처, 설명란의 비즈니스 이메일 |
| 시장 조사 | 제품 관리자, 애널리스트 | 트렌딩 주제, 댓글 기반 사용자 반응 |
| 콘텐츠 전략 | 유튜버, 에이전시 | 성과가 좋은 포맷, 최적의 제목/태그 패턴 |
| SEO / 키워드 리서치 | SEO 전문가 | 영상 제목, 태그, 설명, 랭킹 신호 |
| 브랜드 모니터링 | PR 팀, 브랜드 관리자 | 영상 제목·댓글·설명에 등장한 브랜드 언급 |
| 학술 연구 | 연구자, 데이터 과학자 | 감성 분석을 위한 댓글 데이터셋 (2025년 한 연구에서는 4.5만 개의 YouTube 댓글로 BERT를 파인튜닝해 93.1% 정확도를 달성) |
예를 들어 DJI, GoPro, Insta360를 비교한 경쟁 분석에서는 는 결과가 나왔습니다. 이런 인사이트는 YouTube Studio 안에서는 절대 보이지 않습니다.
requests + BeautifulSoup만으로는 왜 YouTube를 못 긁을까?
실제로 통하는 방법을 보기 전에, 왜 흔한 접근이 실패하는지부터 이해해야 합니다. 이건 단순한 이론이 아니라, 몇 시간짜리 디버깅을 막아 주는 핵심입니다.
겉보기엔 이런 식으로 할 수 있을 것 같죠.
1import requests
2from bs4 import BeautifulSoup
3response = requests.get("https://www.youtube.com/@somechannel/videos")
4soup = BeautifulSoup(response.text, "html.parser")
5videos = soup.find_all("a", id="video-title-link")
6print(len(videos)) # 0 — 항상
결과는 늘 0입니다. 도 말하듯, “페이지가 동적으로 로드되었기 때문에 requests 라이브러리로는 지원되지 않는다”는 게 이유입니다. 는 더 직설적입니다. “requests와 BeautifulSoup만으로는 JavaScript를 실행할 수 없다.”
은 그 메커니즘을 설명합니다. YouTube는 SPA로 만들어져 있어서, 기본 HTTP 요청으로는 초기 HTML 껍데기만 받고 실제 콘텐츠는 아직 렌더링되지 않은 상태입니다. 비디오 데이터는 브라우저가 실행해 DOM에 주입하는 JavaScript 객체 안에 숨겨져 있습니다.
좋은 소식도 있습니다. YouTube는 필요한 데이터를 원본 HTML 안에 분명히 넣어 둡니다. 다만 DOM 요소가 아니라 <script> 태그 안의 두 JSON 블록에 들어 있을 뿐입니다.
ytInitialData— 페이지 구조, 영상 목록, 참여 지표, 댓글 continuation tokenytInitialPlayerResponse— 핵심 비디오 메타데이터(제목, 설명, 재생 시간, 포맷, 자막)
어떻게 추출하고 파싱하는지만 알면, 브라우저 없이도 requests.get() 한 번으로 접근할 수 있습니다. 아래 1번 방법이 바로 그 방식입니다.
Python으로 YouTube 스크래핑하는 4가지 방법: 한눈에 비교
각 방법을 자세히 보기 전에, 먼저 의사결정 표부터 보겠습니다. 실제 프로젝트에 맞는 도구를 고를 때 중요한 기준으로 네 가지 방법을 모두 테스트해 비교했습니다.
| 기준 | requests + BS4 (ytInitialData) | Selenium / Playwright | yt-dlp | YouTube Data API | 노코드(Thunderbit) |
|---|---|---|---|---|---|
| 설정 난이도 | 낮음 | 중간 | 낮음 | 중간(API 키 필요) | 없음 |
| JS 렌더링 처리 | 부분적(JSON 파싱) | 가능 | 가능 | 해당 없음(구조화 API) | 가능 |
| 속도 | 빠름 | 느림 | 빠름 | 빠름 | 빠름(클라우드) |
| 봇 탐지 위험 | 중간 | 높음 | 낮음 | 없음 | 처리됨 |
| 쿼터 / 속도 제한 | 없음(대신 IP 차단 가능) | 없음(대신 탐지 가능) | 없음 | 하루 10,000 유닛 | 크레딧 기반 |
| 댓글 추출 | 어려움 | 가능하지만 복잡 | 내장 | 내장 | 페이지에 따라 다름 |
| 자막/트랜스크립트 | 아니오 | 복잡 | 가능 | 아니오 | 아니오 |
| 가장 적합한 용도 | 빠른 메타데이터 | 검색 결과, 동적 페이지 | 대량 메타데이터 + 댓글 | 대규모 구조화 데이터 | 비개발자, 빠른 내보내기 |
간단 요약:

실제로 어떤 YouTube 데이터를 뽑을 수 있고, 어떤 방법이 가능한가?
제가 처음 시작할 때 이런 표가 있었으면 좋겠다고 생각했던 비교표입니다. 어떤 한 방법도 모든 필드를 다 커버하지는 못합니다. 그래서 이 글에서 네 가지 방법을 다 다루는 이유이기도 하죠.
| 데이터 항목 | BS4 (ytInitialData) | Selenium/Playwright | yt-dlp | YouTube API | Thunderbit |
|---|---|---|---|---|---|
| 비디오 제목 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 조회수 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 좋아요 수 | ⚠️ 불안정 | ✅ | ✅ | ✅ | ✅ |
| 댓글(텍스트) | ❌ | ⚠️ 복잡 | ✅ | ✅ | ⚠️ |
| 트랜스크립트/자막 | ❌ | ⚠️ | ✅ | ❌ | ❌ |
| 태그 | ✅ | ✅ | ✅ | ✅ | ⚠️ |
| 썸네일 URL | ✅ | ✅ | ✅ | ✅ | ✅ |
| 채널 구독자 수 | ⚠️ | ✅ | ✅ | ✅ | ✅ |
| 업로드 날짜 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 비디오 길이 | ✅ | ✅ | ✅ | ✅ | ✅ |
| Shorts 전용 데이터 | ❌ | ⚠️ | ✅ | ⚠️ | ⚠️ |
프로젝트에서 가장 중요한 행이 무엇인지에 따라 방법을 고르세요. 댓글과 자막이 필요하면 yt-dlp가 확실한 승자입니다. 구조화된 통계를 중간 규모로 가져오려면 API가 가장 좋습니다. 2분 안에 데이터를 뽑고 싶다면 아래 Thunderbit 섹션을 보시면 됩니다.

방법 1: requests + BeautifulSoup로 YouTube 스크래핑하기 (ytInitialData 파싱)
이 방법은 YouTube가 페이지 데이터를 원본 HTML 안의 JSON으로 모두 넣어 둔다는 점을 이용합니다. 브라우저는 필요 없고, 어디를 봐야 하는지만 알면 됩니다.
- 난이도: 초급
- 소요 시간: 약 15분
- 준비물: Python 3.10+,
requests,beautifulsoup4
1단계: YouTube 페이지에 GET 요청 보내기
현실적인 User-Agent 헤더와 함께 요청을 보내세요. 기본 python-requests/2.x 헤더는 바로 차단되는 경우가 많습니다. 도 이것이 초보자가 가장 많이 밟는 함정이라고 설명합니다.
1import requests
2HEADERS = {
3 "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
4 "AppleWebKit/537.36 (KHTML, like Gecko) "
5 "Chrome/114.0.0.0 Safari/537.36",
6 "Accept-Language": "en-US,en;q=0.9",
7 "Cookie": "CONSENT=YES+cb", # EU 동의 화면 우회
8}
9url = "https://www.youtube.com/@mkbhd/videos"
10response = requests.get(url, headers=HEADERS)
11print(response.status_code) # 200이어야 함
CONSENT 쿠키는 매우 중요합니다. 이게 없으면 EU 지역 요청은 consent.youtube.com으로 리다이렉트되는데, 그 HTML에는 ytInitialData가 아예 없습니다.
2단계: HTML을 파싱하고 ytInitialData 스크립트 찾기
BeautifulSoup나 정규식을 사용해 var ytInitialData =가 들어 있는 <script> 태그를 찾습니다.
1import re
2import json
3# ytInitialData JSON 추출
4match = re.search(
5 r"var ytInitialData\s*=\s*({.*?});</script>",
6 response.text,
7 re.DOTALL
8)
9if match:
10 data = json.loads(match.group(1))
11 print("ytInitialData를 성공적으로 추출했습니다")
12else:
13 print("ytInitialData를 찾지 못했습니다 — 헤더/쿠키를 확인하세요")
흔한 실수는 끝 구분자로 };만 사용하는 비탐욕적 .*? 패턴입니다. JSON 내부에는 중괄호 닫는 패턴이 계속 나오기 때문에 캡처가 너무 일찍 잘립니다. 처럼 };</script>를 끝 지점으로 써야 합니다. 그 블록 안에서 마지막 할당이기 때문입니다.
3단계: JSON 구조를 따라가며 비디오 데이터 추출하기
JSON은 중첩이 깊습니다. YouTube가 구조를 조금만 바꿔도 바로 깨지는 하드코딩 경로 대신, 재귀적으로 키를 찾는 방식이 훨씬 안전합니다. 이런 변경은 자주 발생하는데, 에도 2023년 이후 포맷 변경 사례가 여러 번 기록돼 있습니다.
1def search_dict(partial, search_key):
2 stack = [partial]
3 while stack:
4 cur = stack.pop()
5 if isinstance(cur, dict):
6 for k, v in cur.items():
7 if k == search_key:
8 yield v
9 else:
10 stack.append(v)
11 elif isinstance(cur, list):
12 stack.extend(cur)
13# 채널 페이지에서 비디오 정보 추출
14videos = []
15for vr in search_dict(data, "videoRenderer"):
16 videos.append({
17 "video_id": vr.get("videoId"),
18 "title": vr["title"]["runs"][0]["text"],
19 "views": vr.get("viewCountText", {}).get("simpleText", "N/A"),
20 "published": vr.get("publishedTimeText", {}).get("simpleText", "N/A"),
21 })
22print(f"{len(videos)}개의 비디오를 찾았습니다")
23for v in videos[:5]:
24 print(f" {v['title']} — {v['views']}")
이 재귀 방식은 , yt-dlp, Scrapfly가 모두 수렴한 방식입니다. YouTube의 잦은 JSON 구조 변경에도 비교적 잘 버팁니다.
4단계: 스크래핑한 데이터를 CSV 또는 Excel로 내보내기
1import csv
2with open("youtube_videos.csv", "w", newline="", encoding="utf-8") as f:
3 writer = csv.DictWriter(f, fieldnames=["video_id", "title", "views", "published"])
4 writer.writeheader()
5 writer.writerows(videos)
6print("데이터를 youtube_videos.csv로 내보냈습니다")
이 방법은 언제 쓰고, 언제 피해야 할까?
추천 대상: 몇 개 안 되는 채널 또는 영상 페이지에서 빠르게 메타데이터를 뽑고 싶을 때, 가벼운 SEO 도구, 제목·조회수·업로드 날짜 정도만 필요한 단발성 분석.
한계: JSON 구조가 자주 바뀝니다. 실제로 깨진 사례로는 좋아요 버튼 리팩터링(2023: toggleButtonRenderer → segmentedLikeDislikeButtonViewModel), 설명란 리팩터링(2023: description.runs[] → attributedDescription.content), 채널 Videos 탭 재설계(2022–2023: gridRenderer → richGridRenderer)가 있습니다. 데이터센터 IP는 보통 50~200회 요청 이후 소프트 차단됩니다. 댓글도 없고, 자막도 없습니다.
방법 2: Selenium 또는 Playwright로 YouTube 스크래핑하기
페이지와 직접 상호작용해야 할 때, 예를 들어 검색 결과를 스크롤하거나 탭을 클릭하거나 설명을 펼쳐야 할 때는 브라우저 자동화가 정답입니다.
- 난이도: 중급
- 소요 시간: 약 30분
- 준비물: Python 3.10+, Playwright (
pip install playwright && playwright install) 또는 Selenium + ChromeDriver
새 프로젝트라면 Selenium보다 Playwright를 추천합니다. 에 따르면 Playwright는 Selenium보다 작업당 약 높습니다. Playwright는 Chrome DevTools Protocol 위에 지속적인 WebSocket 연결을 사용하고, Selenium은 HTTP 기반 WebDriver를 사용해 명령마다 변환 계층이 하나 더 생깁니다.
1단계: Playwright 설치하기
1pip install playwright
2playwright install chromium
1from playwright.sync_api import sync_playwright
2pw = sync_playwright().start()
3browser = pw.chromium.launch(headless=False) # 화면 표시 모드가 일부 탐지를 피하는 데 도움
4context = browser.new_context()
5# EU 동의 화면 우회를 위해 쿠키 미리 설정
6context.add_cookies([{
7 "name": "SOCS",
8 "value": "CAISNQgDEitib3FfaWRlbnRpdHlmcm9udGVuZHVpc2VydmVyXzIwMjMwODI5LjA3X3AxGgJlbiACGgYIgJnPpwY",
9 "domain": ".youtube.com",
10 "path": "/",
11}])
12page = context.new_page()
2단계: YouTube 페이지로 이동하고 콘텐츠가 로드될 때까지 대기하기
1page.goto("https://www.youtube.com/@mkbhd/videos")
2page.wait_for_selector("a#video-title-link", timeout=15000)
3print("페이지 로드 완료 — 비디오 요소가 보입니다")
검색 결과를 긁는다면 https://www.youtube.com/results?search_query=your+query를 사용하세요.
3단계: 무한 스크롤로 더 많은 비디오 로드하기
YouTube의 채널 페이지와 검색 결과는 무한 스크롤을 사용합니다. 아래는 를 바탕으로 정리한 표준 scrollHeight 루프입니다.
1prev_height = -1
2max_scrolls = 20 # 꼭 제한하세요 — 1만 개 영상 채널은 끝없이 스크롤됩니다
3scroll_count = 0
4while scroll_count < max_scrolls:
5 page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
6 page.wait_for_timeout(1500) # 새 콘텐츠 로딩 대기
7 new_height = page.evaluate("document.body.scrollHeight")
8 if new_height == prev_height:
9 break # 더 이상 새 콘텐츠 없음
10 prev_height = new_height
11 scroll_count += 1
12print(f"{scroll_count}번 스크롤했습니다")
4단계: 렌더링된 페이지에서 비디오 데이터 추출하기
1video_elements = page.query_selector_all("a#video-title-link")
2videos = []
3for el in video_elements:
4 title = el.inner_text()
5 href = el.get_attribute("href")
6 video_id = href.split("v=")[-1] if href else None
7 videos.append({"title": title, "video_id": video_id, "url": f"https://www.youtube.com{href}"})
8print(f"{len(videos)}개의 비디오를 추출했습니다")
조회수와 업로드 날짜를 얻으려면 형제 요소를 같이 가져와야 합니다. 는 id="video-title-link"가 모든 페이지에서 동일하게 쓰이지 않으며, YouTube가 여러 페이지 변형을 제공한다고 경고합니다. 더 튼튼한 대안은 a[href*="watch"]입니다.
5단계: CSV 또는 Google Sheets로 내보내기
1import csv
2with open("youtube_playwright.csv", "w", newline="", encoding="utf-8") as f:
3 writer = csv.DictWriter(f, fieldnames=["title", "video_id", "url"])
4 writer.writeheader()
5 writer.writerows(videos)
6browser.close()
7pw.stop()
이 방법은 언제 쓰고, 언제 피해야 할까?
추천 대상: 검색 결과 스크래핑, 탭 클릭이나 설명 펼치기 같은 동적 요소 조작, 완전히 렌더링된 DOM이 필요한 작업.
한계: 느립니다. 스크롤 후 추출 흐름에서는 비디오당 약 1.5~3초가 걸립니다. 봇 탐지 위험도 높습니다. 기본 Selenium은 navigator.webdriver === true를 노출하는데, 할 수 있습니다. 리소스도 많이 먹습니다(브라우저 인스턴스 하나당 RAM 200~500MB). 100개 영상이면 yt-dlp보다 몇 분이 걸릴 수 있습니다.
방법 3: yt-dlp로 YouTube 스크래핑하기
yt-dlp는 YouTube 스크래핑의 스위스 아미 나이프입니다. youtube-dl의 커뮤니티 포크로, 을 받았고, 야간 빌드와 함께 메타데이터, 댓글, 자막, 일괄 스크래핑까지 지원합니다. 브라우저나 API 키도 필요 없습니다.
- 난이도: 초급~중급
- 소요 시간: 약 10분
- 준비물: Python 3.10+,
pip install yt-dlp
1단계: yt-dlp 설치하기
1pip install yt-dlp
브라우저 드라이버도 없고, API 키도 없고, 설정 파일도 없습니다.
2단계: 다운로드 없이 비디오 메타데이터 추출하기
1import yt_dlp
2opts = {
3 "quiet": True,
4 "skip_download": True, # 영상 파일은 받지 않고 메타데이터만
5 "no_warnings": True,
6}
7with yt_dlp.YoutubeDL(opts) as ydl:
8 info = ydl.extract_info(
9 "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
10 download=False
11 )
12print(f"제목: {info['title']}")
13print(f"조회수: {info['view_count']:,}")
14print(f"좋아요 수: {info.get('like_count', 'N/A')}")
15print(f"길이: {info['duration']}초")
16print(f"업로드 날짜: {info['upload_date']}")
17print(f"채널: {info['channel']} ({info.get('channel_follower_count', 'N/A')}명 구독자)")
18print(f"태그: {info.get('tags', [])[:5]}")
일반적인 extract_info 호출은 영상 상태에 따라 id, title, channel, channel_id, channel_follower_count, view_count, like_count, comment_count, upload_date, duration, tags, categories, description, thumbnails, is_live, availability, automatic_captions, subtitles, chapters, heatmap 등 80~120개 필드를 반환합니다.
3단계: YouTube 영상의 댓글 추출하기
1opts = {
2 "quiet": True,
3 "skip_download": True,
4 "getcomments": True,
5 "extractor_args": {
6 "youtube": {
7 "max_comments": ["200", "50", "50", "10"], # 전체, 상위 댓글, 대댓글/개, 대댓글 총합
8 "comment_sort": ["top"],
9 }
10 },
11}
12with yt_dlp.YoutubeDL(opts) as ydl:
13 info = ydl.extract_info(
14 "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
15 download=False
16 )
17comments = info.get("comments", [])
18print(f"댓글 {len(comments)}개를 가져왔습니다")
19for c in comments[:3]:
20 print(f" [{c.get('like_count', 0)} likes] {c['author']}: {c['text'][:80]}...")
댓글 추출은 느립니다. 에 따르면 댓글 가져오기 속도는 약 30KB/s 수준입니다. 댓글이 10만 개 달린 영상은 몇 시간이 걸릴 수 있습니다. 는 댓글 페이지를 다 읽기 전에 포맷 URL이 약 6시간 만에 만료되는 사례도 보여 줍니다. 대형 영상은 max_comments를 공격적으로 제한하는 것이 좋습니다.
4단계: 트랜스크립트와 자막 추출하기
YouTube Data API도, BS4 파싱도 전체 트랜스크립트를 주지 못합니다. 이건 yt-dlp의 독보적인 장점입니다.
1opts = {
2 "quiet": True,
3 "skip_download": True,
4 "writesubtitles": True,
5 "writeautomaticsub": True,
6 "subtitleslangs": ["en", "en-orig"],
7 "subtitlesformat": "json3", # 기계 파싱에 적합: start/dur(ms) + text
8 "outtmpl": "%(id)s.%(ext)s",
9}
10with yt_dlp.YoutubeDL(opts) as ydl:
11 info = ydl.extract_info(
12 "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
13 download=False
14 )
15# info 딕셔너리에서 자막 데이터 직접 접근
16auto_captions = info.get("automatic_captions", {})
17manual_subs = info.get("subtitles", {})
18print(f"자동 자막 언어: {list(auto_captions.keys())[:10]}")
19print(f"수동 자막 언어: {list(manual_subs.keys())}")
기계 파싱에는 json3 형식이 좋습니다. 각 세그먼트에 밀리초 단위의 start/dur와 텍스트가 들어 있습니다. 언어 코드는 BCP-47 형식(en, en-US, zh-Hans, ja, es)입니다.
5단계: 여러 영상 또는 전체 채널을 일괄 스크래핑하기
1opts = {
2 "quiet": True,
3 "skip_download": True,
4 "extract_flat": "in_playlist", # 빠르게 — 비디오 ID와 제목만
5 "sleep_interval": 2,
6 "max_sleep_interval": 6,
7}
8with yt_dlp.YoutubeDL(opts) as ydl:
9 info = ydl.extract_info(
10 "https://www.youtube.com/@mkbhd/videos",
11 download=False
12 )
13entries = info.get("entries", [])
14print(f"채널에서 {len(entries)}개의 비디오를 찾았습니다")
15for e in entries[:5]:
16 print(f" {e.get('title', 'N/A')} — {e.get('id')}")
채널 URL, 재생목록 URL, 심지어 검색 쿼리(ytsearch10:python scraping)도 넣을 수 있으며, yt-dlp가 내부적으로 페이징을 처리합니다.
이 방법은 언제 쓰고, 언제 피해야 할까?
추천 대상: 대량 메타데이터 추출, 댓글, 자막, 영상 다운로드, 채널 전체 스크래핑처럼 필드가 많이 필요한 작업.
한계: 검색 결과 페이지를 스크래핑하는 데는 최적이 아닙니다(그럴 땐 Selenium/Playwright가 더 적합). 2024~2026년의 봇 차단 경쟁 때문에 대규모 운영은 더 복잡해졌습니다. YouTube가 일부 클라이언트에 을 적용하고 있기 때문입니다. 운영 환경에서는 플러그인을 설치하고 --cookies-from-browser chrome을 사용하세요. 단, 테스트용 계정을 쓰는 것이 좋습니다. yt-dlp 팀은 실제 Google 계정 쿠키를 쓰면 계정이 차단될 수 있다고 경고합니다.
방법 4: YouTube Data API로 YouTube 스크래핑하기
공식 YouTube Data API v3는 YouTube 데이터를 가장 안정적이고 구조화된 형태로 가져오는 방법입니다. 응답은 깔끔한 JSON이고, 필드도 문서화되어 있으며, 봇 탐지를 피하기 위한 수 싸움을 할 필요도 없습니다. 다만 대부분의 튜토리얼이 넘어가는 중요한 함정이 하나 있습니다. 바로 쿼터 제도입니다.
- 난이도: 중급
- 소요 시간: 약 20분(API 키 설정 포함)
- 준비물: Python 3.10+, Google Cloud 프로젝트,
pip install google-api-python-client
1단계: YouTube Data API 키 받기
- 로 이동
- 새 프로젝트 생성(또는 기존 프로젝트 선택)
- APIs & Services → Library로 가서 "YouTube Data API v3" 검색 후 Enable
- APIs & Services → Credentials → Create Credentials → API Key
- 키를 복사해 아래 코드에서 사용
2단계: 첫 API 호출하기
1from googleapiclient.discovery import build
2API_KEY = "YOUR_API_KEY_HERE"
3youtube = build("youtube", "v3", developerKey=API_KEY)
4# 특정 비디오 상세 정보 가져오기
5response = youtube.videos().list(
6 part="snippet,statistics,contentDetails",
7 id="dQw4w9WgXcQ"
8).execute()
9video = response["items"][0]
10print(f"제목: {video['snippet']['title']}")
11print(f"조회수: {video['statistics']['viewCount']}")
12print(f"좋아요 수: {video['statistics'].get('likeCount', '숨김')}")
13print(f"댓글 수: {video['statistics'].get('commentCount', '비활성화')}")
14print(f"길이: {video['contentDetails']['duration']}")
15print(f"태그: {video['snippet'].get('tags', [])[:5]}")
응답은 깔끔하고, 타입이 분명하고, 문서화도 잘 되어 있습니다. JSON 구조를 파고들 필요가 없습니다.
3단계: 비디오 상세 정보, 채널 정보, 댓글 추출하기
1# 비디오 검색
2search_response = youtube.search().list(
3 part="snippet",
4 q="python web scraping tutorial",
5 type="video",
6 maxResults=10,
7 order="viewCount"
8).execute()
9for item in search_response["items"]:
10 print(f" {item['snippet']['title']} — {item['id']['videoId']}")
11# 댓글 가져오기
12comments_response = youtube.commentThreads().list(
13 part="snippet",
14 videoId="dQw4w9WgXcQ",
15 maxResults=20,
16 order="relevance"
17).execute()
18for item in comments_response["items"]:
19 comment = item["snippet"]["topLevelComment"]["snippet"]
20 print(f" [{comment['likeCount']} likes] {comment['authorDisplayName']}: {comment['textDisplay'][:80]}")
YouTube API 쿼터의 현실: 아무도 잘 말해 주지 않는 부분
이 부분이 그냥 복붙 튜토리얼과 진짜 쓸 만한 가이드를 가르는 지점입니다. 기본 할당량은 이며, 미국 태평양 시간 자정에 초기화됩니다. 각 호출 비용은 다음과 같습니다.
| API 엔드포인트 | 호출당 쿼터 비용 | 호출당 최대 결과 수 |
|---|---|---|
search.list | 100 유닛 | 50개 결과 |
videos.list | 1 유닛 | 비디오 ID 50개(배치) |
channels.list | 1 유닛 | 채널 ID 50개 |
commentThreads.list | 1 유닛 | 댓글 100개 |
captions.list | 50 유닛 | 해당 없음 |
이제 계산을 해보겠습니다. YouTube 검색 결과 1,000개를 스크래핑하고 싶다고 합시다.
- 검색 호출: 1,000개 ÷ 페이지당 50개 = 20회 호출 × 100 유닛 = 2,000 유닛(하루 예산의 20%가 사라짐)
- 그 1,000개 비디오의 상세 정보: 1,000개 ID ÷ 배치당 50개 = 20회 호출 × 1 유닛 = 20 유닛(저렴함.
videos.list배치가 핵심) - 그 1,000개 비디오의 댓글(페이지당 1회라고 가정): 1,000회 호출 × 1 유닛 = 1,000 유닛
합계는 비교적 소규모 스크래핑에도 약 3,020 유닛입니다. 하지만 영상마다 댓글이 깊게 달려 있으면(영상당 50페이지 이상) 남은 7,000 유닛도 금방 소진됩니다. 댓글 5만 개짜리 영상 하나는 약 500페이지 = 500 유닛입니다. 이런 영상을 20개만 긁어도 그날 예산은 끝입니다.
에는 상당히 엄격한 컴플라이언스 심사가 필요합니다. 개인정보 처리방침 URL, 약관 URL, 앱 시연 영상, 쿼터 산정 근거까지 제출해야 합니다. 커뮤니티 보고에 따르면 Google의 초기 응답은 보통 3~5영업일이지만, 최종 승인까지는 몇 주에서 몇 달이 걸릴 수 있고, 특히 “분석용으로 더 많은 데이터가 필요하다”는 용도는 거절되는 경우가 많습니다.
API를 써야 할 때: 소~중간 규모, 구조화되고 신뢰할 수 있는 데이터가 필요할 때, 댓글과 채널 통계가 중요할 때, 쿼터 한도를 감수할 수 있을 때.
스크래핑이 더 나은 경우: 대규모 프로젝트(하루 1만 개 이상의 영상), API가 공개하지 않는 필드(전체 트랜스크립트 — captions.download는 OAuth와 영상 소유자 권한이 필요), 또는 쿼리당 500개를 넘는 검색 결과가 필요할 때(API가 totalResults를 어떻게 말하든 실제 한도는 고정).
노코드 지름길: Thunderbit로 YouTube 스크래핑하기(Python 불필요)
데이터 파이프라인에 Python이 꼭 필요하다면 위의 1~4번 방법을 쓰면 됩니다. 하지만 2분 안에 YouTube 데이터가 필요하다면 — 예를 들어 경쟁사 통계를 뽑는 마케터이거나, 프로젝트 환경 설정 없이 빠르게 데이터만 가져오고 싶은 개발자라면 — 더 빠른 방법이 있습니다.
은 코드 작성이 과한 경우를 위해 만든 AI 웹 스크래퍼 Chrome 확장 프로그램입니다. 브라우저에서 YouTube 페이지에 바로 작동합니다.
Thunderbit로 YouTube를 스크래핑하는 3단계
1단계: 을 설치한 뒤 YouTube 채널 페이지, 검색 결과 페이지, 또는 비디오 페이지를 엽니다.
2단계: Thunderbit 사이드바에서 **"AI Suggest Fields"**를 클릭합니다. AI가 페이지를 읽고 비디오 제목, 조회수, 업로드 날짜, 길이, 채널 이름, 썸네일 URL 같은 열을 제안합니다. 필요에 따라 열을 추가, 삭제, 이름 변경할 수 있습니다.
3단계: **"Scrape"**를 클릭한 뒤 Google Sheets, Excel, CSV, Airtable, Notion으로 내보냅니다. 깔끔한 표 형태로 바로 사용할 수 있습니다.
이런 분들에게 적합합니다
- 코드를 쓰지 않는 마케터지만 경쟁 채널 데이터가 필요한 분
- 가상환경 설정이나 의존성 설치 없이 빠르게 데이터를 뽑고 싶은 개발자
- 봇 차단에 막힌 모든 사람 — Thunderbit은 사용자의 로그인된 브라우저 세션 안에서 스크래핑하므로 쿠키와 PO 토큰을 그대로 활용합니다. 서버 측 스크래퍼를 괴롭히는 차단 문제를 상당 부분 피할 수 있습니다
- Thunderbit은 도 활용할 수 있어, 각 비디오 페이지를 방문해 좋아요 수, 설명, 태그 같은 추가 정보로 표를 더 풍부하게 만들 수 있습니다
Thunderbit가 YouTube를 어떻게 다루는지 더 자세히 보려면 과 을 확인해 보세요.
Python으로 YouTube를 스크래핑할 때 차단을 피하는 팁
이 팁들은 위 4가지 Python 방법 모두에 적용됩니다. YouTube의 봇 차단은 수준이며, 핵심 신호는 IP 행동 분석, JS 실행 요구, 자주 바뀌는 HTML 구조입니다.
모든 방법 공통:
- User-Agent 문자열뿐 아니라 전체 헤더 세트를 바꾸세요.
Accept,Accept-Language,Sec-CH-UA같은 client hints도 선언한 UA와 일치해야 합니다. 에 최신 목록이 있습니다. - 요청 사이에 2~8초의 랜덤 지연을 넣으세요. 고정 간격은 탐지 신호가 됩니다.
- 몇 페이지 이상이라면 주거용 프록시를 쓰세요. AWS, GCP, Hetzner 같은 데이터센터 IP는 .
- 세션과 IP를 함께 회전하세요. YouTube는 세션을 IP와 묶어 보고, 같은 세션 쿠키가 두 IP에서 나타나면 위험 신호로 봅니다.
requests + BS4용: CONSENT=YES+cb 쿠키를 설정하세요. 없으면 EU 요청이 동의 페이지로 리다이렉트되고 데이터가 없습니다.
Selenium/Playwright용: Linux 서버에서는 --headless=new보다 xvfb를 쓰고 headful로 실행하세요. 최신 headless Chrome도 정교한 탐지기에는 충분한 지문 정보를 남깁니다. 약 17개의 우회 기법을 적용하는 도 고려해 볼 만합니다.
yt-dlp용: sleep_interval과 max_sleep_interval 옵션을 사용하세요. PO Token 생성을 위해 플러그인을 설치하세요. --cookies-from-browser chrome은 테스트용 계정으로만 쓰는 것이 좋습니다.
API용: 에서 쿼터 사용량을 모니터링하고 요청을 효율적으로 배치하세요. 비디오 ID 50개를 쉼표로 묶은 videos.list 한 번은 1 유닛만 듭니다.
Thunderbit용: 스크래핑이 브라우저 세션 안에서 이뤄지므로 봇 차단은 자동으로 처리됩니다. 사실상 수작업을 자동화하는 것과 같습니다.
Python으로 YouTube를 스크래핑하는 것이 합법일까?
무엇을, 어떻게 긁는지, 그리고 그 데이터를 무엇에 쓰는지에 따라 다릅니다.
2024년에는 법적 환경이 바뀌었습니다. Meta Platforms v. Bright Data(N.D. Cal., 2024년 1월) 사건에서 했습니다. 이 판결 이후 공개 데이터 스크래핑은 “상당히 덜 위험한” 것으로 여겨지게 되었습니다. 반면 hiQ v. LinkedIn은 로 끝났고, 약관 위반, CFAA 위반(가짜 계정), 동산침해(trespass to chattels)까지 인정되며 영구 금지명령도 내려졌습니다.
YouTube 자체 도 분명합니다. 사전 서면 허가가 있거나 관련 법에서 허용하는 경우를 제외하고는 “robots, botnets or scrapers 같은 자동화 수단으로 서비스에 접근해서는 안 된다”고 되어 있습니다. 공식적으로 허용된 접근 방식은 YouTube Data API입니다.
실무적으로는 다음 정도를 지키면 됩니다.
- 공개적으로 보이는 데이터를 개인 연구나 비상업적 분석 목적으로 스크래핑하는 것은 일반적으로 위험이 낮습니다
- 가장 안전한 경로는 API입니다. 명시적으로 허가된 방식이니까요
- 비공개/로그인 전용 콘텐츠 스크래핑, 저작권 영상 재배포 목적 다운로드, 댓글의 개인 데이터를 이용한 GDPR 위반은 피하세요
- YouTube 댓글에는 GDPR 제4조 1항 기준의 개인정보가 포함될 수 있습니다. EU 데이터 주체의 정보를 다룰 때는 특히 조심해야 합니다
- 상업적 스크래핑 프로젝트라면 법률 자문을 받으세요
이 내용은 법률 자문이 아닙니다. 이 분야는 빠르게 변하고 있습니다. 2025~2026년에는 AI 기업들이 학습 데이터로 YouTube를 스크래핑한 것과 관련해 도 생기고 있습니다.
Python으로 YouTube를 스크래핑할 때 어떤 방법을 써야 할까?
의사결정 가이드는 이렇습니다.
- 몇 개 페이지에서 빠르게 메타데이터만 필요하다면? → 1번 방법(requests + BS4). 빠르고 가볍고,
requests와beautifulsoup4외에 거의 필요 없습니다. - 검색 결과를 긁거나 동적 페이지와 상호작용해야 한다면? → 2번 방법(Selenium/Playwright). 완전한 브라우저 렌더링과 무한 스크롤을 지원하지만 느리고 탐지되기 쉽습니다.
- 대량 메타데이터, 댓글, 자막이 필요하다면? → 3번 방법(yt-dlp). 단일 도구 중 가장 다재다능합니다. 가 괜히 있는 게 아닙니다.
- 중간 규모에서 구조적이고 신뢰할 수 있는 데이터가 필요하다면? → 4번 방법(YouTube Data API). 공식적이고 깔끔하지만 이라는 쿼터 한계가 있습니다.
- 코드 없이 2분 만에 데이터가 필요하다면? → . 브라우저 기반, AI 기반, 클릭 몇 번으로 Google Sheets로 내보낼 수 있습니다.
모든 사용 사례를 하나의 방법이 다 해결해 주지는 않습니다. 위의 비교표와 추출 가능한 데이터 항목 표를 북마크해 두세요. 다음 프로젝트에서 시간을 많이 아껴 줄 겁니다. 더 많은 을 보고 싶다면 Thunderbit 블로그에 부터 까지 다양한 가이드가 준비되어 있습니다.
FAQ
API 키 없이 YouTube를 스크래핑할 수 있나요?
네. 1번 방법(requests + BS4), 2번 방법(Selenium/Playwright), 3번 방법(yt-dlp)은 API 키가 필요 없습니다. 4번 방법(YouTube Data API)만 키가 필요합니다. Thunderbit도 API 키 없이 사용할 수 있으며, 브라우저에서 직접 스크래핑합니다.
Python으로 YouTube를 가장 빠르게 스크래핑하는 방법은 무엇인가요?
Python 기준으로는 yt-dlp와 requests + BS4가 가장 빠릅니다. 둘 다 브라우저 오버헤드가 없고, 영상당 몇 초 안에 메타데이터를 가져올 수 있습니다. 특히 yt-dlp는 페이징을 내부에서 처리해 배치 작업에 매우 빠릅니다. Python을 쓰지 않는다면 Thunderbit가 설정 시간이 전혀 없어서 전체적으로 가장 빠릅니다.
Python으로 YouTube 댓글은 어떻게 스크래핑하나요?
yt-dlp는 getcomments 옵션으로 댓글 추출을 기본 지원하므로 가장 간단합니다. YouTube Data API도 commentThreads.list를 통해 댓글을 가져올 수 있습니다(호출당 1 유닛, 페이지당 최대 100개). Selenium/Playwright로도 스크롤해서 렌더링된 댓글 요소를 뽑을 수는 있지만, 느리고 깨지기 쉽습니다.
Python으로 YouTube Shorts도 스크래핑할 수 있나요?
네. yt-dlp는 Shorts 메타데이터를 잘 처리하며, 일반 영상에 Shorts 전용 필드를 추가로 붙여 다룹니다. YouTube Data API도 부분 지원합니다(Shorts 조회수 집계는 에 바뀌어 이제 재생 시작/재생 반복도 조회수에 포함). BS4와 Selenium/Playwright는 Shorts shelf가 다른 DOM 구조를 쓰기 때문에 지원이 제한적입니다.
하루에 YouTube 영상을 몇 개까지 스크래핑할 수 있나요?
YouTube Data API는 하루 약 10,000 쿼터 유닛으로 제한됩니다. 배치 videos.list 호출(한 번에 50개 ID, 1 유닛)을 쓰면 하루 최대 50만 건의 비디오 통계 조회가 가능하지만, search.list는 호출당 100유닛이라 예산을 금방 소모합니다. 스크래핑 방식(BS4, Selenium, yt-dlp)은 고정 상한보다 실무적 한계가 더 중요합니다. 프록시 설정과 요청 패턴에 따라 IP 차단은 보통 IP당 하루 수백~수천 요청 이후 발생합니다. Thunderbit는 에 연동된 크레딧 시스템을 사용합니다.
더 알아보기
