jiji262/douyin-downloader
요약
Douyin의 동영상, 이미지, 음악, 프로필 등을 일괄 다운로드할 수 있는 실용적인 도구입니다. 데스크톱 GUI와 REST API 서버 모드를 지원하며, SQLite를 통한 중복 제거 및 OpenAI API를 활용한 비디오 전사 기능을 제공합니다.
핵심 포인트
- 동영상, 댓글, 라이브 스트리밍 녹화 등 다양한 데이터 수집 지원
- 데스크톱 GUI 및 FastAPI 기반 REST API 서버 모드 제공
- SQLite를 활용한 히스토리 관리 및 중복 제거 기능
- OpenAI Transcriptions API를 통한 비디오 전사 기능 지원
- Playwright를 이용한 브라우저 폴백 및 쿠키 자동 캡처
동영상, 이미지 노트, 컬렉션, 음악, 즐겨찾기 컬렉션 및 프로필 일괄 다운로드를 지원하며, 진행 상황 표시, 재시도, SQLite 중복 제거, 다운로드 무결성 검사 및 브라우저 폴백 (Browser fallback) 지원을 갖춘 실용적인 Douyin 다운로더입니다.
동일한 백엔드를 기반으로 구축된 데스크톱 GUI — 링크를 붙여넣어 시작하고, 팔로잉 목록을 동기화하며, 다운로드를 시각적으로 추적할 수 있습니다.
Beta: 데스크톱 앱은 현재 클로즈 베타 (closed beta) 상태입니다. 사용해 보려면 Releases 페이지에서 빌드를 다운로드하세요.
| 기능 | 설명 |
|---|---|
| 단일 동영상 다운로드 | /video/{aweme_id} |
| ... | 라이브 스트리밍 녹화 |
live.douyin.com/{room_id} → FLV/HLS, 스트림 종료 시 부분 데이터를 보존 | |
| 댓글 컬렉션 | 각 aweme의 댓글(+ 선택적 답글)이 *_comments.json으로 저장됨 |
| 핫 서치 + 키워드 검색 | --hot-board [N] / --search "keyword"를 통해 JSONL로 덤프 |
| REST API 서버 모드 | --serve --serve-port 8000 (선택 사항 fastapi + uvicorn) |
| 알림 푸시 | 다운로드 완료 시 Bark / Telegram / Webhook 전송 |
| 추가 에셋 | 커버, 음악, 아바타, JSON 메타데이터 |
| 비디오 전사 (Transcription) | 선택 사항, OpenAI Transcriptions API 사용 |
| 동시 다운로드 | 설정 가능한 동시성, 기본값 5 |
| ... |
- 브라우저 폴백 (Browser fallback)은
post에 대해 완전히 검증되었습니다.
;like/mix/music은 현재 API 페이지네이션 (pagination) number.allmix에 의존합니다.
/increase.allmix는 호환성을 위한 별칭으로 유지되며 mix로 정규화됩니다.
collect / collectmix는 현재 로그인된 쿠키로 표현되는 계정에 대해서만 작동합니다.
collect / collectmix는 단독으로 사용해야 하며 post / like / mix / music / increase와 결합할 수 없습니다.
increase는 현재 post / like / mix / music에 적용됩니다.
; 즐겨찾기 (favorites) 수집 모드는 증분 중단 (incremental stop)을 지원하지 않습니다. - 라이브 스트림 (Live stream) 녹화는 FLV 형식으로 네이티브하게 저장됩니다; HLS 소스는 플레이리스트만 저장합니다 (재생 가능한 출력을 위해 ffmpeg를 사용하세요)
-
웹캐스트 룸 (webcast room) 엔드포인트는 모든 라이브 시나리오에 대해 검증되지 않았습니다 — 실험적인 기능으로 간주하십시오
-
Python 3.8+
-
macOS / Linux / Windows
pip install -r requirements.txt
브라우저 폴백 (fallback) 및 자동 쿠키 캡처를 위해:
pip install playwright
python -m playwright install chromium
cp config.example.yml config.yml
python -m tools.cookie_fetcher --config config.yml
Douyin에 로그인한 후, 터미널로 돌아와 Enter를 누르세요. 쿠키가 설정 파일에 자동으로 기록됩니다.
docker build -t douyin-downloader .
docker run -v $(pwd)/config.yml:/app/config.yml -v $(pwd)/Downloaded:/app/Downloaded douyin-downloader
link:
- https://www.douyin.com/user/MS4wLjABAAAAxxxx
path: ./Downloaded/
...
python run.py -c config.yml
python run.py -c config.yml \
-u "https://www.douyin.com/video/7604129988555574538" \
-t 8 \
...
| 인자 (Argument) | 설명 (Description) |
|---|---|
-u, --url | 다운로드 링크를 추가합니다. 반복 사용 가능 |
-c, --config | 설정 파일을 지정합니다 (기본값: config.yml) |
-p, --path | 다운로드 디렉토리를 지정합니다 |
-t, --thread | 동시성 (concurrency)을 지정합니다 |
--show-warnings | 경고/에러 로그를 표시합니다 |
-v, --verbose | 정보/경고/에러 로그를 표시합니다 |
--hot-board [N] | Douyin 핫 검색 보드를 가져와 JSONL로 작성합니다; 선택 사항인 상위 N개 |
--search KEYWORD | 키워드로 영상을 검색하여 JSONL로 작성합니다 |
--search-max N | --search의 최대 항목 수 (기본값 50) |
--serve | REST API 서버로 실행합니다 (pip install fastapi uvicorn 필요) |
--serve-host HOST | REST 서버가 수신 대기할 호스트 (기본값 127.0.0.1) |
--serve-port PORT | REST 서버가 수신 대기할 포트 (기본값 8000) |
--version | 버전 번호를 표시합니다 |
link:
- https://www.douyin.com/video/7604129988555574538
link:
- https://www.douyin.com/note/7341234567890123456
link:
link:
link:
link:
link:
Cross-mode deduplication: 다른 모드에서 동일한 aweme_id가 두 번 다운로드되지 않습니다.
link:
link:
link:
- https://live.douyin.com/123456789 # 또는 /follow/live/{room_id}
live:
...
녹화기는 Downloaded/{author}/live/ 경로에 FLV 파일을 저장하고, 추가로 *_room.json 메타데이터 스냅샷을 저장합니다. 방송자가 스트림을 종료하거나 네트워크가 유휴 상태가 되거나 Ctrl+C를 누르면 이미 기록된 바이트는 보존됩니다 (.tmp 파일이 최종 파일로 승격됨).
comments:
enabled: true
include_replies: false # true로 설정하면 각 댓글의 2차 답글을 가져옵니다 (추가 API 호출)
...
미디어 파일 옆에 {날짜}_{제목}_{aweme_id}_comments.json 파일을 생성합니다.
run.py --hot-board 30 -p ./Downloaded
# 출력: ./Downloaded/hot_board/20260424_221530.jsonl
run.py --search
/ `server.job_ttl_seconds`
.
```yaml
notifications:
enabled: true
on_success: true
...
활성화된 모든 프로바이더 (provider)에게 병렬로 알림이 전송되며, 실패한 프로바이더가 다운로드 흐름을 차단하지 않습니다.
increase:
post: true
database: true # 증분 모드 (incremental mode)는 데이터베이스가 필요합니다
number:
post: 0
현재 동작은 비디오 아이템 (video items)에만 적용됩니다 (이미지 노트 아이템은 전사 (transcript)를 생성하지 않습니다).
transcript:
enabled: true
model: gpt-4o-mini-transcribe
...
환경 변수를 통해 키를 제공하는 것을 권장합니다:
export OPENAI_API_KEY="sk-xxxx"
활성화 시, 다음 파일들이 생성됩니다:
xxx.transcript.txt
xxx.transcript.json
database: true인 경우, 작업 상태가 SQLite 테이블 transcript_job에도 기록됩니다 (success/failed/skipped).
권장 사항:
python3 -m pytest -q
이제 일반 pytest도 지원됩니다:
pytest -q
| 필드 (Field) | 설명 (Description) |
|---|---|
mode | post / like / mix / music 지원; 로그인된 즐겨찾기 모드는 단독 collect / collectmix를 추가로 지원 |
number.post/like/mix/music/collect/collectmix | 모드별 다운로드 제한, 0 = 무제한 |
increase.post/like/mix/music | 모드별 증분 (incremental) 토글 |
start_time / end_time | 시간 필터 (형식: YYYY-MM-DD) |
folderstyle | 아이템별 하위 디렉토리 생성 |
browser_fallback.* | 페이지네이션 (pagination)이 제한될 때 post에 대한 브라우저 폴백 (fallback) |
progress.quiet_logs | 진행 단계 중 로그 출력 억제 |
transcript.* | 비디오 다운로드 후 선택적 전사 (transcription) |
comments.* | Aweme별 댓글 수집 (선택 사항) |
live.* | 라이브 스트림 녹화 옵션 (max_duration_seconds / chunk_size / idle_timeout_seconds) |
notifications.* | 완료 시 Bark/Telegram/Webhook 푸시 알림 |
server.* | REST API 서버 튜닝 (max_jobs, job_ttl_seconds) |
proxy | 선택적 HTTP/HTTPS 프록시 (proxy) 설정 |
database | SQLite 중복 제거 및 히스토리 활성화 |
database_path | SQLite 경로, 기본값은 현재 작업 디렉토리의 dy_downloader.db |
thread |
동시 다운로드 횟수 |
retry_times |
실패 시 재시도 횟수 |
folderstyle: true 설정 시 기본값
및 database_path: dy_downloader.db 기본값
:
workspace/
├── config.yml
├── dy_downloader.db # database: true 일 때 기본 위치
...
프로그램은 이미 다운로드된 콘텐츠를 건너뛸지 결정하기 위해 데이터베이스 기록 + 로컬 파일 이중 확인 방식을 사용합니다. 강제로 재다운로드하려면 그에 맞춰 정리해야 합니다:
# 로컬 파일 삭제 (폴더 이름에 aweme_id 포함)
rm -rf Downloaded/AuthorName/post/*_<aweme_id>/
# 데이터베이스 기록 삭제
...
rm -rf Downloaded/AuthorName/
sqlite3 dy_downloader.db "DELETE FROM aweme WHERE author_name = 'AuthorName';"
rm -rf Downloaded/
rm dy_downloader.db
참고: 데이터베이스만 삭제하고 파일을 유지하면 재다운로드가 트리거되지 않습니다. 프로그램이 기존 다운로드를 감지하기 위해 로컬 파일명에서 aweme_id를 스캔하기 때문입니다. 파일을 삭제하고 데이터베이스를 유지하면 재다운로드가 트리거됩니다 (프로그램은 "DB에는 있지만 로컬에는 없는 상태"를 재시도가 필요한 상태로 취급합니다).
이는 일반적인 페이지네이션 (Pagination) 리스크 관리 동작입니다. 다음 사항을 확인하세요:
browser_fallback.enabled: true
browser_fallback.headless: false
- 브라우저 팝업에서 수동으로 완전한 검증을 수행하고, 너무 일찍 닫지 마세요.
기본적으로 progress.quiet_logs: true는 진행 단계 중 로그를 억제합니다.
디버깅 시에는 일시적으로 --show-warnings 또는 -v를 사용하세요.
실행:
python -m tools.cookie_fetcher --config config.yml
다음 순서로 확인하세요:
transcript.enabled가true인지- 다운로드된 항목이 비디오인지 (이미지 노트는 전사(transcription)되지 않음)
OPENAI_API_KEY(또는transcript.api_key)가 유효한지response_formats에txt또는json이 포함되어 있는지
sqlite3 dy_downloader.db "SELECT aweme_id, title, author_name, datetime(download_time, 'unixepoch', 'localtime') FROM aweme ORDER BY download_time DESC LIMIT 20;"
링크를 클릭하여 그룹 채팅에 참여하세요 【QQ 그룹】: https://qm.qq.com/q/9xoNt8Wzv4
이 프로젝트는 기술 연구, 학습 및 개인 데이터 관리 목적으로만 제공됩니다. 법적이고 책임감 있게 사용해 주세요:
- 타인의 개인정보, 저작권 또는 기타 법적 권리를 침해하는 용도로 사용하지 마십시오.
- 어떠한 불법적인 목적으로도 사용하지 마십시오.
- 사용으로 인해 발생하는 모든 위험과 책임은 사용자 본인에게 있습니다.
- 플랫폼 정책이나 인터페이스가 변경되어 기능이 작동하지 않는 것은 정상적인 기술적 리스크입니다.
이 프로젝트를 계속 사용함으로써 귀하는 위의 사항을 인지하고 수락하는 것으로 간주됩니다.
이 프로젝트는 MIT License 하에 라이선스가 부여됩니다. 자세한 내용은 LICENSE를 참조하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 GitHub Trending Python (daily)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기