이 튜토리얼에서는 다음을 배웁니다:
- 웹 스크래핑을 통해 빌리빌리에서 데이터를 추출하는 것이 합리적인 이유.
- Bilibili에서 스크래핑할 수 있는 데이터 유형.
- AI 훈련(및 기타 용도)을 위한 동영상 데이터 수집을 위한 빌리빌리 스크래핑 및 다운로드 파이프라인 구축 방법.
- – 왜 전용 Bilibili 스크레이퍼가 생산 환경에 적합한 엔터프라이즈급 애플리케이션에 더 나은 선택인지.
복잡한 과정은 건너뛰세요:Bright Data의 Bilibili 스크레이퍼는내장된 봇 우회 기능과 99.99% 가동 시간을 바탕으로 기업 규모에 맞는 즉시 사용 가능한 동영상 데이터를 제공합니다.
자, 시작해 보겠습니다!
Bilibili 스크래핑의 필요성: 가능한 사용 사례
Bilibili는 상하이에 기반을 둔 동영상 플랫폼으로, 흔히 “중국의 YouTube”로 불립니다. 2009년 출시된 이후 월간 활성 사용자 2억 9,400만 명, 일일 동영상 조회수 30억 회 이상을 기록하는 Z세대의 거대 플랫폼으로 성장했습니다.
원래 ACG(애니메이션, 만화, 게임)에 중점을 두었으나, 현재는 기술, 교육, 라이프스타일, 음악, e스포츠, 라이브 스트리밍까지 영역을 확장했습니다. 빌리빌리는 실시간 ‘탄무(탄막)’ 댓글과 높은 참여도를 자랑하는 커뮤니티로 유명합니다. 사용자 제작 콘텐츠, 인플루언서 문화, 게임, 광고를 하나의 디지털 생태계로 결합합니다.
빌리빌리의 급속한 성장을 고려할 때, 플랫폼 데이터 접근은 다음과 같은 다양한 활용 사례를 지원합니다:
- 비디오 AI 훈련: 대규모 빌리빌리 동영상 데이터셋은 컴퓨터 비전, 음성 인식, 다중 모달 대규모 언어 모델(LLM), 추천 시스템, 콘텐츠 관리 모델을 강화할 수 있습니다. 풍부한 메타데이터, 자막, 참여 신호, 원시 시청각 콘텐츠 덕분에 가능합니다.
- 트렌드 및 콘텐츠 인텔리전스: 카테고리, 태그, 조회수, 참여 지표를 분석하여 Z세대 시청자 및 ACG 중심 커뮤니티 내에서 부상하는 주제, 급성장하는 크리에이터, 바이럴 형식을 식별합니다.
- 크리에이터 및 인플루언서 분석: 업로더 성과, 팔로워 증가율, 참여율, 게시 빈도를 추적하여 KOL(Key Opinion Leader) 영향력을 벤치마킹하고 중국 내 인플루언서 마케팅 전략을 최적화합니다.
- 시청자 감정 분석: 단무(탄환 댓글) 및 일반 댓글을 분석하여 시청자 반응, 감정적 어조, 문화적 참조, 대규모 실시간 피드백 패턴을 파악합니다.
- 경쟁사 벤치마킹: 유사한 틈새 시장에서 조회수, 상호작용 및 콘텐츠 전략을 모니터링하여 브랜드 채널, 스폰서 캠페인 및 카테고리 리더를 비교합니다.
- 시장 진입 및 현지화 연구: 콘텐츠 선호도, 언어 사용, 유행하는 테마를 평가하여 중국의 디지털 네이티브 시청자를 위한 제품, 캠페인 및 메시지를 맞춤화합니다.
Bilibili에서 수집 가능한 데이터
Bilibili를 스크래핑할 때, 수집할 수 있는 여러 데이터 필드가 있습니다. 이는 수집하는 페이지의 특정 유형과 전반적인 목표에 따라 달라집니다. 따라서 탐구할 가치가 있는 여러 흥미로운 Bilibili 데이터 범주가 있습니다.
동영상 메타데이터
특정 빌리빌리 동영상을 대상으로 할 경우 다음 정보를 수집할 수 있습니다:
- 기본 정보: 제목, 설명, 커버 이미지 URL, 동영상 ID, 재생 시간 등
- 업로드 세부 정보: 게시 타임스탬프 및 카테고리/분류(예: “애니메이션”, “기술”, “음악”).
- 분류 정보: 태그, 키워드, 동영상이 원본 콘텐츠인지 재게시물인지 여부.
- 참여 통계: 총 조회수, 좋아요, 코인, 즐겨찾기, 공유 수.
- 댓글: 동영상에 직접 표시되는 댓글. 댓글 텍스트, 타임스탬프, 색상, 글꼴 크기, 표시 모드 포함.
- 자막: AI 생성 또는 업로더가 제공한 대본.
사용자 및 크리에이터 프로필
Bilibili 크리에이터 페이지에 집중할 때 다음 정보를 스크래핑할 수 있습니다:
- 신원 정보: 사용자명, 사용자 ID, 성별, 프로필 사진 등.
- 소셜 지표: 팔로워 수, 팔로잉 수, 전체 동영상 누적 좋아요 수.
- 개인 정보: 사용자 소개, 생일, 계정 등급.
- 계정 상태: 인증 배지(예: “공식 음악가”) 및 멤버십 등급(예: VIP/빅 멤버).
- 작품 목록: 특정 크리에이터가 공개 업로드한 모든 동영상.
검색 및 발견 데이터
Bilibili 검색 시스템을 활용하여 다음을 검색할 수도 있습니다:
- 검색 결과: 특정 키워드와 일치하는 동영상, 사용자 또는 라이브 스트림 목록.
- 트렌딩 데이터: 인기 검색 키워드 및 일간/주간 순위표.
- 라이브 스트림 정보: 방 ID, 스트림 제목, 라이브 상태, 동시 시청자 수(인기 지수).
Python으로 빌리빌리 스크레이퍼 및 동영상 다운로드 파이프라인 구축하기: 단계별 가이드
이 가이드 섹션에서는 “테크” 카테고리 페이지에서 빌리빌리 동영상 메타데이터를 스크래핑하는 방법을 배웁니다:
이것은 단지 예시임을 유의하십시오. 동일한 논리를 메인 홈페이지를 포함한 다른 모든 카테고리 페이지에도 적용할 수 있습니다.
해당 페이지에서 추출한 동영상 URL을 사용하여 두 번째 스크립트를 구축해 하나씩 다운로드합니다. 다운로드한 동영상 파일은 AI/ML 훈련 파이프라인에 직접 투입할 수 있습니다.
아래 지침을 따르세요!
필수 준비 사항
이 튜토리얼을 따라하려면 다음이 설치되어 있어야 합니다:
- 로컬에 설치된 Python 3.10
- 로컬에 FFmpeg 설치.
- 브라우저 자동화 작동 방식에 대한 이해.
-
yt-dlp의작동 방식에 대한 기본적인 이해.
다음 명령어로 FFmpeg이 설치되었는지 확인하세요:
ffmpeg -version
다음과 유사한 결과가 표시되어야 합니다:
오류가 발생할 경우, 운영 체제에 맞는 공식 설치 가이드를 따라 FFmpeg를 설치하세요.
단계 #0: 빌리빌리(Bilibili)에 익숙해지기
코드를 작성하기 전에 대상 사이트를 탐색하는 데 시간을 할애하세요. 웹 스크래핑 로드맵은 사이트가 정적인지 동적인지에 따라 달라지므로 이를 파악해야 합니다.
사이트가 정적이라면 간단한 HTTP 클라이언트와 HTML 파싱 접근법으로도 충분할 수 있습니다. 동적인 경우 브라우저 자동화 도구가 필요합니다. 웹 스크래핑을 위한 정적 vs 동적 콘텐츠 가이드에서 자세히 알아보세요.
브라우저에서 대상 페이지를 방문하여 상호작용을 시작하세요. 페이지가 무한 스크롤 UI 패턴을 사용하는 방식을 확인하세요:
스크롤을 내릴수록 새로운 비디오 카드가 자동으로 로드됩니다. 이러한 동작은 웹사이트가 동적임을 나타내는 지표입니다. 구체적으로, 사용자 상호작용에 따라 새로운 데이터를 가져와 렌더링하는 데 자바스크립트에 의존합니다.
따라서 단순한 HTTP 요청만으로는 부족합니다. 콘텐츠를 제대로 렌더링하고 스크래핑하려면 브라우저 자동화 도구가 필요합니다. 이 튜토리얼에서는 Playwright를 사용하겠지만, Selenium, SeleniumBase, NODRIVER 같은 도구도 사용할 수 있습니다.
1단계: Playwright 프로젝트 설정
터미널을 실행하고 Bilibili 스크래퍼용 새 디렉터리를 생성하세요:
mkdir bilibili-scraper
프로젝트 디렉터리로 이동하여 내부에서 Python 가상 환경을 생성합니다:
cd bilibili-scraper
python -m venv .venv
그런 다음 선호하는 Python IDE에서 프로젝트 폴더를 로드하세요. Python 확장 프로그램이 설치된 Visual Studio Code나 PyCharm Community Edition이 모두 좋은 선택입니다.
프로젝트 디렉토리 루트에 scraper.py라는 새 파일을 생성합니다. 내용은 다음과 같아야 합니다:
bilibili-scraper/
├── .venv/
└── scraper.py # <-----------
IDE 내장 터미널에서 가상 환경을 활성화하세요. Linux/macOS에서는 다음 명령을 실행합니다:
source .venv/bin/activate
Windows에서는 다음과 같이 실행하세요:
.venv/Scripts/activate
가상 환경이 활성화된 상태에서 playwright를 설치합니다:
pip install playwright
필요한 브라우저 바이너리를 다운로드하여 설치를 완료하세요:
python -m playwright install
이제 scraper.py에 다음과 같은 기본 Playwright 설정을 추가하세요:
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
# 헤드리스 모드가 아닌 제어된 크로미움 인스턴스 실행
browser = await p.chromium.launch(headless=False) # 프로덕션 환경에서는 True로 설정
context = await browser.new_context()
page = await context.new_page()
# 스크래핑 로직...
# 브라우저 닫기 및 리소스 해제
await browser.close()
if __name__ == "__main__":
asyncio.run(main())
이 코드 조각은 Chromium 브라우저 인스턴스를 초기화하고 Playwright가 이를 제어하도록 합니다.
개발 중에는 headless=False로 유지하여 브라우저의 동작을 시각적으로 추적하는 것이 유용합니다. 프로덕션 환경에서는 headless=True로 설정하여 헤드리스 모드를 활성화함으로써 리소스 사용량을 줄이고 실행 속도를 높일 수 있습니다.
잘하셨습니다! 이제 브라우저 자동화를 통한 빌리빌리 웹 스크래핑을 위한 파이썬 환경이 준비되었습니다.
2단계: 대상 사이트 연결
Playwright를 사용하여 대상 웹 페이지인 Bilibili “기술” 카테고리 페이지로 이동합니다:
# 대상 "기술" 빌리빌리 페이지
target_bilibili_page = "https://www.bilibili.com/c/tech/"
# 대상 페이지로 이동
await page.goto(target_bilibili_page)
goto() 함수는 제어되는 브라우저가 지정된 URL을 방문하고 페이지 로딩을 기다리도록 지시합니다.
이제 빌리빌리 대상 페이지에 연결되었습니다.
다음 단계는 스크롤 동작을 자동화하여 새로운 동영상 카드가 동적으로 로드되도록 하는 것입니다. 추가 콘텐츠가 나타나면 해당 HTML 요소에서 데이터를 추출할 준비가 된 것입니다.
단계 #3: 새로운 동영상 카드 로드
앞서 언급했듯이, 빌리빌리 홈페이지와 카테고리 페이지는 무한 스크롤 UI 패턴을 사용합니다. 처음에는 몇 개의 동영상 카드만 표시됩니다. 아래로 스크롤하면 자바스크립트를 통해 동적으로 더 많은 콘텐츠가 로드됩니다.
구체적으로, 페이지는 처음에 .head-cards HTML 요소 안에 고정된 수의 동영상 카드 요소로 로드됩니다:
스크롤을 내리면 .feed-cards 컨테이너가 페이지에 추가됩니다. 이 섹션은 계속 스크롤할 때 새로운 동영상 카드로 동적으로 채워집니다:
여기서 중요한 점은 모든 동영상 카드(초기 페이지 로드 시 정적으로 존재하는 카드든 스크롤 중 동적으로 로드되는 카드든)가 다음 CSS 선택자를 통해 선택 가능하다는 것입니다:
.feed-card
이 빌리빌리 스크래핑 튜토리얼에서는 최소 50개의 동영상을 가져오려는 경우를 가정합니다. 이를 달성하려면 여러 번의 스크롤 동작을 시뮬레이션해야 합니다. Playwright는 스크롤을 위한 특정 API를 제공하지 않으므로, 페이지 컨텍스트에서 직접 간단한 자바스크립트 스크립트를 실행합니다:
for _ in range(3):
# 지연 로딩 허용
await asyncio.sleep(1)
await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
# 지연 로딩 허용
await asyncio.sleep(2)
이 루프는 window.scrollTo() 를 세 번 실행하여 각 반복마다 페이지 상단에서 하단으로 스크롤합니다. asyncio.sleep() 호출이 중요한 이유는 다음과 같습니다:
- 스크롤 동작이 더 자연스럽게 보이도록 합니다.
- 봇 방지 메커니즘이 작동할 위험을 줄입니다.
- 다음 스크롤 전에 지연 로딩된 콘텐츠가 완전히 렌더링될 시간을 제공합니다.
비디오 카드는 동적으로 로드되므로 스크롤 직후 바로 존재한다고 가정할 수 없습니다. 대신 50번째 카드가 DOM에 연결될 때까지 명시적으로 대기해야 합니다. Playwright에서는 다음과 같이 수행합니다:
fiftieth_card = page.locator(".feed-card").nth(49)
await fiftieth_card.wait_for(state="attached")
이 코드는 50번째 .feed-card 요소에 대한 Playwright 로케이터를 생성합니다(인덱싱이 0부터 시작하므로nth(49) ). 그런 다음 wait_for()를 사용하여 해당 요소가 DOM에 연결될 때까지 대기합니다.
이제 헤드리스 모드(headless=False)로 스크립트를 실행하면 브라우저가 자동으로 세 번 스크롤하는 것을 확인할 수 있습니다:
의도한 대로, 스크롤 후마다 새로운 비디오 카드가 로드됩니다.
이 단계 이후에는 페이지에 최소 50개의 비디오 카드가 존재한다고 확신할 수 있습니다. 훌륭합니다!
단계 #4: 비디오 카드 구조 파악하기
올바른 데이터를 추출하려면 먼저 DOM 내에서 각 비디오 카드의 구조를 이해해야 합니다.
먼저 .head-cards 섹션 내 그래픽 카드 중 하나를 마우스 오른쪽 버튼으로 클릭하고 브라우저 개발자 도구에서 검사하세요:
그런 다음 로드된 .feed-cards 섹션 내의 비디오 카드에 대해 동일한 과정을 반복하세요:
다행히 모든 .feed-card 요소는 동일한 내부 구조를 공유합니다. 즉, 초기 페이지 렌더링 시 로드된 비디오 카드와 스크롤 후 동적으로 로드된 비디오 카드를 구분할 필요가 없습니다. 동일한 선택자로 모두 타겟팅할 수 있습니다!
각 비디오 카드에서 다음 정보를 수집할 수 있음을 확인하세요:
.bili-video-card__title a요소에서 동영상 제목을 수집합니다.- 동일한 제목
<a>노드의href속성에서 동영상 URL. .bili-video-card__subtitle span[title]에서 원본 자막(제작자 이름 + 게시일 포함)..bili-video-card__author요소에서 저자 프로필 URL을 수집합니다.
완벽합니다! 이제 DOM 구조를 이해했으니, 다음 단계는 이 지식을 프로그래밍 방식의 빌리빌리 데이터 스크래핑 로직으로 전환하는 것입니다.
5단계: 동영상 데이터 스크래핑
대상 페이지에는 여러 동영상 카드가 포함되어 있음을 기억하세요. 따라서 먼저 스크래핑 결과를 저장할 데이터 구조가 필요합니다. 리스트가 이에 적합합니다:
videos = []
다음으로, 모든 동영상 카드를 순회하며 앞서 설명한 추출 로직을 적용합니다:
for i in range(feed_card_count):
# 데이터를 추출할 현재 동영상 카드 가져오기
card = feed_cards.nth(i)
title_locator = card.locator(".bili-video-card__title a")
title = await title_locator.inner_text() if await title_locator.count() else None
video_url = await title_locator.get_attribute("href") if await title_locator.count() else None
subtitle_locator = card.locator(".bili-video-card__subtitle span[title]")
subtitle = await subtitle_locator.inner_text() if await subtitle_locator.count() else None
author_locator = card.locator(".bili-video-card__author")
author_url = await author_locator.get_attribute("href") if await author_locator.count() else None
author_name = None
date = None
if subtitle and "·" in subtitle:
parts = [p.strip() for p in subtitle.split("·")]
if len(parts) >= 2:
author_name = parts[0]
date = parts[1]
# 수집된 데이터 저장
video = {
"title": title,
"video_url": video_url,
"subtitle": subtitle,
"author": {
"name": author_name,
"url": author_url
},
"date": date
}
videos.append(video)
위의 코드 조각은 각 동영상 카드를 순회하며:
- 제목, 동영상 URL, 원본 자막, 작성자 프로필 URL을 추출합니다.
"<AUTHOR_NAME> · <DATE>"형식의 자막 문자열을 파싱하여 저자 이름과 동영상 날짜를 별도로 추출합니다.- 구조화된
동영상사전(dictionary)을 생성하고videos목록에 추가합니다.
for 루프가 끝날 때쯤이면 videos 목록에는 50개 이상의 구조화된 빌리빌리 동영상 객체가 포함될 것입니다. 훌륭합니다!
6단계: 스크랩된 데이터 내보내기
스크랩한 데이터를 처리하기 쉽게 하려면 videos.json 파일로 내보냅니다:
import json
with open("videos.json", "w", encoding="utf-8") as f:
json.dump(videos, f, ensure_ascii=False, indent=2)
이제 scraper.py를 실행하면 다음과 같이 구조화된 빌리빌리 동영상 데이터가 포함된 videos.json 파일이 생성됩니다:
미션 완료! 수많은 동영상 카드가 있는 페이지에서 시작하여, 이제 그 메타데이터를 구조화된 JSON 파일에 저장했습니다.
단순히 빌리빌리 스크래핑이 목표라면 튜토리얼은 여기서 끝낼 수 있습니다(완성된 스크립트는 마지막 단계를 꼭 확인하세요). 한 걸음 더 나아가 동영상 자체를 다운로드하고 싶다면 계속 읽어보세요…
7단계: 빌리빌리 동영상 다운로드 준비
앞서 스크래핑한 URL에서 Bilibili 동영상을 다운로드하는 가장 쉬운 방법은 yt-dlp를 활용하는 것입니다.
yt-dlp는 Bilibili를 포함한 수백 개의 웹사이트를 지원하는 기능이 풍부한 오디오/비디오 다운로더입니다. 명령줄과 프로그래밍 방식의 Python API를 통해 모두 사용할 수 있습니다. 여기서는 Python API를 통해 프로그래밍 방식으로 활용하겠습니다.
가상 환경을 활성화한 상태에서 yt-dlp를 설치하세요:
pip install yt-dlp
그런 다음 프로젝트 루트에 video-downloader. py라는 새 파일을 추가하세요:
bilibili-scraper/
├── .venv/
├── scraper.py
└── video-downloader.py # <-----------
이 파일에는 yt-dlp 기반의 빌리빌리 동영상 다운로드 로직이 포함됩니다.
video-downloader.py 스크립트는 다음 작업을 수행해야 합니다:
videos.json파일을 읽어야 합니다.- 각 동영상에 대한
video_url추출 yt_dlp의YoutubeDL클래스를 사용하여 동영상 파일 다운로드.
구현 내용은 다음과 같습니다:
import os
import json
from yt_dlp import YoutubeDL
INPUT_FILE = "videos.json"
OUTPUT_DIR = "./videos"
# 입력 JSON 파일에서 동영상 데이터 로드
with open(INPUT_FILE, "r", encoding="utf-8") as f:
videos = json.load(f)
print(f"{INPUT_FILE}에서 {len(videos)}개의 동영상 로드됨n")
# 출력 폴더가 존재하는지 확인
os.makedirs(OUTPUT_DIR, exist_ok=True)
ydl_opts = {
"format": "bestvideo+bestaudio/best",
"outtmpl": f"{OUTPUT_DIR}/%(title)s.%(ext)s",
"merge_output_format": "mp4",
}
with YoutubeDL(ydl_opts) as ydl:
for index, video in enumerate(videos, start=1):
video_url = video.get("video_url")
print(f"[{index}/{len(videos)}] 다운로드 중: {video.get('title')}")
try:
ydl.download([video_url])
print(f"동영상 #{index} 다운로드 완료n")
except Exception as e:
print(f"동영상 #{index} 다운로드 실패: {e}n")
와! 목표를 달성하는 데 35줄 미만의 코드만으로도 충분했습니다.
8단계: 동영상 파일 다운로드
ffmpeg가 로컬에 설치되어 있는지 확인한 후 video-downloader.py 스크립트를 실행하세요. 터미널에 다음과 유사한 내용이 표시됩니다:
이는 videos.json 입력 파일에서 59개의 동영상이 로드되었으며, 첫 번째 동영상이 로컬 경로에 성공적으로 다운로드되었음을 나타냅니다:
./videos/실탄 테스트! 방탄복은 얼마나 빠른 총알을 막을 수 있을까?.mp4
Visual Studio Code에서 해당 경로에 MP4 동영상 파일이 생성된 것을 확인할 수 있습니다:
대단합니다! 이제 새로운 동영상을 발견할 뿐만 아니라 다운로드까지 자동화한 완전한 Bilibili 시스템을 갖게 되었습니다. 이 파일들을 활용하면 다중 모달 머신러닝 파이프라인을 통해 AI 모델을 훈련시킬 수도 있습니다.
9단계: 최종 코드
scraper.py 파일에는 다음 코드가 포함됩니다:
# scraper.py
# pip install playwright
# python -m playwright install
import asyncio
from playwright.async_api import async_playwright
import json
async def main():
async with async_playwright() as p:
# 헤드풀 모드로 제어된 크로미움 인스턴스 실행
browser = await p.chromium.launch()
context = await browser.new_context()
page = await context.new_page()
# 대상 "Tech" 빌리빌리 페이지
target_bilibili_page = "https://www.bilibili.com/c/tech/"
# 대상 페이지로 이동
await page.goto(target_bilibili_page)
# 페이지 전체를 3번 스크롤 다운
for _ in range(3):
# 지연 로딩 허용
await asyncio.sleep(1)
await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
# 지연 로딩 허용
await asyncio.sleep(2)
# DOM에 50번째 동영상 카드 요소가 추가될 때까지 대기
fiftieth_card = page.locator(".feed-card").nth(49)
await fiftieth_card.wait_for(state="visible")
# 로케이터를 통해 모든 피드 카드 선택
feed_cards = page.locator(".feed-card")
feed_card_count = await feed_cards.count()
print(f"{feed_card_count}개의 피드 카드 로드됨.")
# 스크랩된 데이터 저장 위치
videos = []
# 각 동영상 카드에 Bilili 데이터 스크래핑 로직 적용
for i in range(feed_card_count):
# 데이터 추출 대상 현재 동영상 카드 가져오기
card = feed_cards.nth(i)
title_locator = card.locator(".bili-video-card__title a")
title = await title_locator.inner_text() if await title_locator.count() else None
video_url = await title_locator.get_attribute("href") if await title_locator.count() else None
subtitle_locator = card.locator(".bili-video-card__subtitle span[title]")
subtitle = await subtitle_locator.inner_text() if await subtitle_locator.count() else None
author_locator = card.locator(".bili-video-card__author")
author_url = await author_locator.get_attribute("href") if await author_locator.count() else None
author_name = None
date = None
if subtitle and "·" in subtitle:
parts = [p.strip() for p in subtitle.split("·")]
if len(parts) >= 2:
author_name = parts[0]
date = parts[1]
# 스크랩한 데이터 저장
video = {
"title": title,
"video_url": video_url,
"subtitle": subtitle,
"author": {
"name": author_name,
"url": author_url
},
"date": date
}
videos.append(video)
# 브라우저 닫기 및 리소스 해제
await browser.close()
# 수집된 데이터를 JSON 파일로 내보내기
with open("videos.json", "w", encoding="utf-8") as f:
json.dump(videos, f, ensure_ascii=False, indent=2)
print(f"{len(videos)} 개의 빌리빌리 동영상 스크랩하여 videos.json에 내보냄")
if __name__ == "__main__":
asyncio.run(main())
다음 명령어로 실행:
python scraper.py
이렇게 하면 스크랩된 빌리빌리 동영상 데이터가 포함된 videos.json 파일이 생성됩니다. 이후 video-downloader.py 스크립트를 사용하여 해당 동영상들을 다운로드할 수 있습니다:
# video-downloader.py
# pip install yt-dlp
import os
import json
from yt_dlp import YoutubeDL
INPUT_FILE = "videos.json"
OUTPUT_DIR = "./videos"
# 입력 JSON 파일에서 동영상 데이터 로드
with open(INPUT_FILE, "r", encoding="utf-8") as f:
videos = json.load(f)
print(f"{INPUT_FILE}에서 {len(videos)}개의 동영상 로드됨n")
# 출력 폴더가 존재하는지 확인
os.makedirs(OUTPUT_DIR, exist_ok=True)
ydl_opts = {
"format": "bestvideo+bestaudio/best",
"outtmpl": f"{OUTPUT_DIR}/%(title)s.%(ext)s",
"merge_output_format": "mp4",
}
with YoutubeDL(ydl_opts) as ydl:
for index, video in enumerate(videos, start=1):
video_url = video.get("video_url")
print(f"[{index}/{len(videos)}] 다운로드 중: {video.get('title')}")
try:
ydl.download([video_url])
print(f"동영상 #{index} 다운로드 완료n")
except Exception as e:
print(f"동영상 #{index} 다운로드 실패: {e}n")
다음 명령어로 실행하세요:
python video-downloader.py
결과는 발견된 각 빌리빌리 동영상의 MP4 파일이 담긴 ./videos 폴더가 생성됩니다.
자, 이제 빌리빌리 스크레이퍼를 구축하고 스크레이핑된 동영상 데이터를 다운로더에 공급하는 방법을 배웠습니다. 이 과정을 통해 AI 훈련이나 기타 용도로 실제 동영상 파일을 가져올 수 있습니다.
다음 단계
이제 구조화된 메타데이터와 실제 동영상 파일을 모두 확보했으므로, 해당 데이터를 AI 훈련 파이프라인에 전달할 수 있습니다. 예를 들어, 컴퓨터 비전 작업을 위한 프레임 추출, NLP 모델 미세 조정을 위한 트랜스크립트 생성, 오디오 신호 분석, 동영상 콘텐츠 및 메타데이터 기반 추천 시스템 구축 등이 가능합니다. 제목, 제작자, 날짜, 원본 동영상 파일의 조합은 실험에 바로 활용 가능한 풍부한 다중 모달 데이터셋을 제공합니다.
또한 다운로드 속도를 높이기 위해 프로세스를 병렬화하여 여러 동영상을 동시에 다운로드하는 방안을 고려하세요. 이 접근법은 사용 가능한 대역폭을 최대한 활용하여 다운로드 시간을 단축하는 데 도움이 됩니다.
빌리빌리 스크래핑을 위한 생산 환경용 솔루션: AI용 동영상 데이터 확보
대량의 동영상에 다운로드 스크립트를 실행하면 다음과 같은 오류가 발생할 수 있습니다:
웹페이지 다운로드 실패: HTTP 오류 412: 사전 조건 실패 (<HTTPError 412: Precondition Failed>로 인해 발생)
이는 빌리빌리가 봇 방지 보호 기능을 적용했기 때문입니다. 플랫폼이 의심스러운 트래픽(동일 IP에서 발생하는 과도한 자동 요청 등)을 감지하면 412 사전조건 실패 응답을 반환하기 시작합니다.
오류 페이지는 다음과 같습니다:
이는 빌리빌리 스크래핑 시 직면하는 수많은 과제 중 하나에 불과합니다. 대상 페이지 구조 변경, 지문 기반 탐지 등 다른 일반적인 문제들도 존재합니다. 소규모 프로젝트에는 커스텀 Playwright + yt-dlp 설정이 효과적일 수 있으나, 장기적으로 유지 관리하기에는 복잡하고 취약해질 수 있습니다.
빌리빌리를 대규모로 안정적으로 스크래핑하려면 IP 로테이션, 브라우저 지문 인식, CAPTCHA 해결, 자동 재시도 등을 처리하는 더 강력한 인프라가 필요합니다. 바로 이것이 Bright Data의 빌리빌리 스크래퍼가 제공하는 기능입니다.
이 웹 스크래핑 API(노코드 스크래퍼로도 이용 가능)는 동영상 제목, 업로드 날짜, 조회수, 좋아요, 댓글, 즐겨찾기, 재생 시간, 업로더 이름, 설명, URL 등을 추출합니다. 이 모든 작업을 수행하면서 자동으로 반봇(anti-bot) 메커니즘을 우회합니다.
빌리빌리 스크레이퍼의 차별점은 195개국에 걸쳐 1억 5천만 개 이상의 IP를 보유한 프록시 인프라를 기반으로 운영된다는 점입니다. 이를 통해 99.99% 가동률, 99.95% 성공률, 무제한 동시 접속을 지원합니다. 이는 대규모 기업 수준의 스크레이핑 시나리오를 가능케 하며, 다중 모달 AI 훈련에 방대한 양의 동영상 데이터가 필요하다는 점을 고려할 때 핵심적입니다.
동영상 URL을 추출한 후, Bright Data의 Web Unlocker API를 자동화된 yt-dlp 워크플로우에 통합하면 412 에러를 피하고 차단 없이 동영상을 다운로드할 수 있습니다. Bright Data 덕분에 속도 제한, 차단 또는 yt-dlp 실패에 대한 걱정을 덜고 AI/ML 모델 훈련용 동영상을 더 많이 확보할 수 있습니다.
결론
이 블로그 글에서는 빌리빌리에서 어떤 데이터를 스크래핑할 수 있는지, 그리고 이를 지원하는 주요 사용 사례를 살펴보았습니다. 가장 흥미로운 시나리오 중 하나는 동영상 데이터를 활용한 AI 훈련입니다. 플랫폼에 수억 개의 동영상이 존재하는 빌리빌리는 공개적으로 접근 가능한 멀티미디어 콘텐츠의 방대한 원천입니다.
이 과정은 단계별로 구축하는 법을 배운 빌리빌리 스크레이퍼로 시작됩니다. 이 스크레이퍼는 동영상 URL을 포함한 구조화된 동영상 메타데이터를 수집합니다. 그런 다음 이 가이드에서 시연한 대로 해당 URL을 yt-dlp 기반 워크플로에 전달하여 실제 동영상 파일을 다운로드할 수 있습니다.
Bright Data는 전용 스크레이퍼와 직접적인 yt-dlp 통합 옵션을 통해 Bilibili 스크레이핑을 지원하여 안정적이고 중단 없는 다운로드를 보장합니다. 자세한 내용은 대규모 비디오 데이터 접근을 통한 LLM 훈련 솔루션을 확인해 보세요.
지금 바로 Bright Data에 가입하여 당사의 동영상 데이터 수집 솔루션을 경험해 보세요!