Leitfaden zum Herunterladen von Videolinks auf mehreren Plattformen
Dieses Dokument erläutert detailliert, wie Videodateien nach dem Abrufen von Videolinks von Plattformen wie Douyin, TikTok, Bilibili, Xiaohongshu, Weibo usw. korrekt heruntergeladen werden. Der Schwerpunkt liegt auf der Lösung häufiger Probleme wie Cross-Domain, Hotlink-Schutz und Header-Validierung.
Inhaltsverzeichnis
- Allgemeine Problemübersicht
- Douyin (Douyin)
- TikTok
- Bilibili (Bilibili)
- Xiaohongshu (Xiaohongshu)
- Weibo (Weibo)
- YouTube
- Frontend-Download-Lösung
- Backend-Proxy-Download-Lösung
- Kontakt
Allgemeine Problemübersicht
Nach dem Abrufen von Videolinks von verschiedenen Plattformen treten beim direkten Herunterladen in der Regel die folgenden Probleme auf:
| Problemtyp | Beschreibung | Lösung |
|---|---|---|
| Hotlink-Schutz (Referer) | Der Server prüft die Herkunft der Anfrage und lehnt unzulässige Quellen ab | Den korrekten Referer-Header setzen |
| Cross-Domain (CORS) | Die Browsersicherheitsrichtlinie blockiert Cross-Domain-Anfragen | Backend-Proxy / Serverseitiger Download |
| User-Agent-Validierung | Der Server prüft die Client-Kennung | Browser-UA simulieren |
| Cookie-Validierung | Einige Links erfordern einen Login-Status | Gültige Cookies mitsenden |
| Ablaufzeit des Links | Videolinks haben ein Ablaufdatum | Rechtzeitig verwenden, nach Ablauf neu abrufen |
| IP-Beschränkung | Einige Links beschränken den Zugriff nach Region | Einen Proxy aus der entsprechenden Region verwenden |
Douyin (Douyin)
Eigenschaften der Videolinks
Douyin-Videolinks liegen typischerweise in den folgenden Formen vor:
1# 播放地址 (无水印)
2https://www.douyin.com/aweme/v1/play/?video_id=xxx&line=0&file_id=xxx
3
4# CDN 地址
5https://v26-web.douyinvod.com/xxx/xxx.mp4
6
7# 带水印下载地址
8https://aweme.snssdk.com/aweme/v1/playwm/?video_id=xxxDownload-Anforderungen
| Request-Header | Erforderlich? | Wert |
|---|---|---|
Referer | Erforderlich | https://www.douyin.com/ |
User-Agent | Empfohlen | Browser-UA |
Cookie | Optional | Für einige HD-Links erforderlich |
Beispiel für serverseitigen Download
1import httpx
2
3async def download_douyin_video(video_url: str, save_path: str) -> bool:
4 """
5 下载抖音视频
6
7 Args:
8 video_url: 视频播放地址
9 save_path: 保存路径
10
11 Returns:
12 是否下载成功
13 """
14 headers = {
15 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
16 "Referer": "https://www.douyin.com/", # 必须
17 "Accept": "*/*",
18 "Accept-Encoding": "identity", # 避免压缩,方便流式下载
19 "Range": "bytes=0-", # 支持断点续传
20 }
21
22 async with httpx.AsyncClient(follow_redirects=True, timeout=60) as client:
23 async with client.stream("GET", video_url, headers=headers) as response:
24 if response.status_code in [200, 206]:
25 with open(save_path, "wb") as f:
26 async for chunk in response.aiter_bytes(chunk_size=8192):
27 f.write(chunk)
28 return True
29 return FalseFrontend-Cross-Domain-Problem
Douyin-Videolinks unterstützen kein CORS; direkte Anfragen vom Frontend werden vom Browser blockiert:
1// ❌ 错误方式 - 会被 CORS 拦截
2fetch('https://v26-web.douyinvod.com/xxx.mp4')
3 .then(res => res.blob()) // CORS error!
4
5// ✅ 正确方式 - 使用后端代理
6fetch('/api/proxy/video?url=' + encodeURIComponent(videoUrl))
7 .then(res => res.blob())
8 .then(blob => {
9 const url = URL.createObjectURL(blob);
10 const a = document.createElement('a');
11 a.href = url;
12 a.download = 'video.mp4';
13 a.click();
14 });Hinweise
- Ablaufzeit des Links: Douyin-Videolinks sind in der Regel 24 Stunden gültig
- Wasserzeichenfreie Links: Verwenden Sie
play_addrstattdownload_addr - HD-Links: Einige 4K-/Originalqualitätsvideos sind sehr groß; es wird empfohlen, segmentiert herunterzuladen, um einen einmaligen Downloadfehler zu vermeiden
TikTok
Eigenschaften der Videolinks
1# 播放地址
2https://v16-webapp-prime.tiktok.com/video/tos/xxx.mp4
3
4# 无水印地址
5https://www.tiktok.com/aweme/v1/play/?video_id=xxx
6
7# 带水印地址
8https://v16-webapp.tiktok.com/video/tos/xxx.mp4Download-Anforderungen
Videolinks der Web-Version
| Request-Header | Erforderlich? | Wert |
|---|---|---|
Referer | Erforderlich | https://www.tiktok.com/ |
User-Agent | Erforderlich | Browser-UA |
Cookie | Erforderlich | Erfordert das Feld tt_chain_token |
Wichtig: Von der TikTok-Webversion zurückgegebene Videolinks können nur mit dem Cookie
tt_chain_tokenheruntergeladen werden, andernfalls wird ein 403-Fehler zurückgegeben.
Videolinks der App-Version
| Request-Header | Erforderlich? | Wert |
|---|---|---|
User-Agent | Empfohlen | Beliebiger UA |
Cookie | Nicht erforderlich | Für App-Links gilt diese Einschränkung nicht |
Empfehlung: Wenn Sie App-Links abrufen können, verwenden Sie bevorzugt App-Links, da dann kein Cookie-Problem behandelt werden muss.
Beispiel für serverseitigen Download
Download der Web-Version (erfordert tt_chain_token)
1import httpx
2
3async def download_tiktok_video_web(
4 video_url: str,
5 save_path: str,
6 tt_chain_token: str # 必须提供
7) -> bool:
8 """
9 下载 TikTok Web 端视频
10
11 注意:
12 - Web 端链接必须携带 tt_chain_token Cookie
13 - tt_chain_token 可从浏览器或 API 响应中获取
14 """
15 headers = {
16 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
17 "Referer": "https://www.tiktok.com/",
18 "Accept": "video/webm,video/ogg,video/*;q=0.9,*/*;q=0.8",
19 "Accept-Language": "en-US,en;q=0.5",
20 "Cookie": f"tt_chain_token={tt_chain_token}", # 必须
21 "Sec-Fetch-Dest": "video",
22 "Sec-Fetch-Mode": "no-cors",
23 "Sec-Fetch-Site": "cross-site",
24 }
25
26 async with httpx.AsyncClient(follow_redirects=True, timeout=60) as client:
27 async with client.stream("GET", video_url, headers=headers) as response:
28 if response.status_code in [200, 206]:
29 with open(save_path, "wb") as f:
30 async for chunk in response.aiter_bytes(chunk_size=8192):
31 f.write(chunk)
32 return True
33 elif response.status_code == 403:
34 print("下载失败: tt_chain_token 无效或缺失")
35 return FalseDownload der App-Version (ohne Cookie-Beschränkung)
1async def download_tiktok_video_app(video_url: str, save_path: str) -> bool:
2 """
3 下载 TikTok App 端视频
4
5 App 端链接无需 Cookie,直接下载即可
6 """
7 headers = {
8 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
9 "Accept": "*/*",
10 }
11
12 async with httpx.AsyncClient(follow_redirects=True, timeout=60) as client:
13 async with client.stream("GET", video_url, headers=headers) as response:
14 if response.status_code in [200, 206]:
15 with open(save_path, "wb") as f:
16 async for chunk in response.aiter_bytes(chunk_size=8192):
17 f.write(chunk)
18 return True
19 return FalseWie man tt_chain_token erhält
tt_chain_token ist das von TikTok für die Authentifizierung beim Video-Download verwendete Cookie; die Beschaffungsmethoden sind:
-
Aus dem Browser abrufen: TikTok-Website öffnen, F12-Entwicklertools -> Application -> Cookies ->
tt_chain_tokenfinden -
Aus der API-Antwort abrufen: In den
Set-Cookie-Headern einiger TikTok-API-Antworten kann dieser Wert enthalten sein -
TikHub API verwenden: Die API-Rückgabedaten können bereits ein verfügbares Token enthalten
1# 示例:从响应头提取 tt_chain_token
2def extract_tt_chain_token(response_headers: dict) -> str:
3 """从响应头中提取 tt_chain_token"""
4 cookies = response_headers.get("set-cookie", "")
5 for cookie in cookies.split(";"):
6 if "tt_chain_token=" in cookie:
7 return cookie.split("tt_chain_token=")[1].split(";")[0]
8 return ""Regionseinschränkungen
TikTok-Videos können Regionseinschränkungen haben; einige Videos sind nur in bestimmten Ländern zugänglich:
1# 使用代理下载特定地区视频
2proxies = {
3 "http://": "http://us-proxy:8080",
4 "https://": "http://us-proxy:8080",
5}
6
7async with httpx.AsyncClient(proxies=proxies) as client:
8 # ...Hinweise
- Regionseinschränkung: Einige Videos erfordern eine US-/Europa-IP, um zugänglich zu sein
- Ablaufzeit des Links: Die Gültigkeit des Links beträgt etwa 24 Stunden
- Wasserzeichenfrei: Bevorzugt das Feld
downloadAddrverwenden
Bilibili (Bilibili)
Eigenschaften der Videolinks
Bilibili-Videolinks sind etwas Besonderes, Audio und Video sind getrennt:
1# 视频流 (DASH)
2https://upos-sz-mirrorali.bilivideo.com/xxx/xxx.m4s
3
4# 音频流 (DASH)
5https://upos-sz-mirrorali.bilivideo.com/xxx/xxx.m4s
6
7# FLV 格式 (旧版)
8https://upos-sz-mirrorali.bilivideo.com/xxx/xxx.flvDownload-Anforderungen
| Request-Header | Erforderlich? | Wert |
|---|---|---|
Referer | Erforderlich | https://www.bilibili.com/ |
User-Agent | Empfohlen | Browser-UA |
Cookie | HD, 4K erforderlich | SESSDATA usw. |
Range | Empfohlen | Segmentierter Download |
Beispiel für serverseitigen Download
1import httpx
2import subprocess
3from pathlib import Path
4
5async def download_bilibili_video(
6 video_url: str,
7 audio_url: str,
8 save_path: str,
9 cookie: str = None
10) -> bool:
11 """
12 下载 B站 视频 (需要合并音视频)
13
14 Args:
15 video_url: 视频流地址
16 audio_url: 音频流地址
17 save_path: 最终保存路径
18 cookie: 可选,高清视频需要 SESSDATA
19 """
20 headers = {
21 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
22 "Referer": "https://www.bilibili.com/", # 必须,否则 403
23 "Accept": "*/*",
24 "Accept-Encoding": "identity",
25 "Origin": "https://www.bilibili.com",
26 }
27
28 if cookie:
29 headers["Cookie"] = cookie
30
31 # 临时文件路径
32 video_temp = save_path + ".video.m4s"
33 audio_temp = save_path + ".audio.m4s"
34
35 async with httpx.AsyncClient(follow_redirects=True, timeout=120) as client:
36 # 下载视频流
37 async with client.stream("GET", video_url, headers=headers) as response:
38 if response.status_code not in [200, 206]:
39 return False
40 with open(video_temp, "wb") as f:
41 async for chunk in response.aiter_bytes(chunk_size=8192):
42 f.write(chunk)
43
44 # 下载音频流
45 async with client.stream("GET", audio_url, headers=headers) as response:
46 if response.status_code not in [200, 206]:
47 return False
48 with open(audio_temp, "wb") as f:
49 async for chunk in response.aiter_bytes(chunk_size=8192):
50 f.write(chunk)
51
52 # 使用 FFmpeg 合并音视频
53 try:
54 subprocess.run([
55 "ffmpeg", "-y",
56 "-i", video_temp,
57 "-i", audio_temp,
58 "-c:v", "copy",
59 "-c:a", "copy",
60 save_path
61 ], check=True, capture_output=True)
62
63 # 删除临时文件
64 Path(video_temp).unlink()
65 Path(audio_temp).unlink()
66 return True
67 except subprocess.CalledProcessError:
68 return FalseZusammenhang zwischen Qualität und Cookie
| Qualität | Anmeldung erforderlich? | Cookie-Anforderung |
|---|---|---|
| 360P/480P | Nein | Keine |
| 720P | Ja | SESSDATA |
| 1080P | Ja | SESSDATA + Premium-Mitgliedschaft |
| 4K/HDR | Ja | SESSDATA + Premium-Mitgliedschaft |
Hinweise
- Muss zusammengeführt werden: Im DASH-Format sind Audio und Video getrennt; sie müssen mit FFmpeg zusammengeführt werden
- Referer erforderlich: Ohne korrekten Referer wird 403 zurückgegeben
- Ablaufzeit des Links: Etwa 2 Stunden gültig
- Segmentierter Download: Für große Dateien wird empfohlen, mit dem Range-Header segmentiert herunterzuladen
Xiaohongshu (Xiaohongshu)
Eigenschaften der Videolinks
1# 视频地址
2https://sns-video-bd.xhscdn.com/xxx.mp4
3https://sns-video-hw.xhscdn.com/xxx.mp4
4
5# 图片地址
6https://sns-img-bd.xhscdn.com/xxx.jpgDownload-Anforderungen
| Request-Header | Erforderlich? | Wert |
|---|---|---|
Referer | Erforderlich | https://www.xiaohongshu.com/ |
User-Agent | Empfohlen | Browser-UA |
Cookie | Optional | Für einige Inhalte erforderlich |
Beispiel für serverseitigen Download
1import httpx
2
3async def download_xiaohongshu_video(video_url: str, save_path: str) -> bool:
4 """
5 下载小红书视频
6
7 小红书对 Referer 检查较严格
8 """
9 headers = {
10 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
11 "Referer": "https://www.xiaohongshu.com/", # 必须
12 "Accept": "*/*",
13 "Origin": "https://www.xiaohongshu.com",
14 "Sec-Fetch-Dest": "video",
15 "Sec-Fetch-Mode": "cors",
16 "Sec-Fetch-Site": "cross-site",
17 }
18
19 async with httpx.AsyncClient(follow_redirects=True, timeout=60) as client:
20 async with client.stream("GET", video_url, headers=headers) as response:
21 if response.status_code in [200, 206]:
22 with open(save_path, "wb") as f:
23 async for chunk in response.aiter_bytes(chunk_size=8192):
24 f.write(chunk)
25 return True
26 return FalseBild-Download
Auch Xiaohongshu-Bilder haben Hotlink-Schutz:
1async def download_xiaohongshu_image(image_url: str, save_path: str) -> bool:
2 """下载小红书图片"""
3 headers = {
4 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
5 "Referer": "https://www.xiaohongshu.com/",
6 "Accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8",
7 }
8
9 async with httpx.AsyncClient(follow_redirects=True) as client:
10 response = await client.get(image_url, headers=headers)
11 if response.status_code == 200:
12 with open(save_path, "wb") as f:
13 f.write(response.content)
14 return True
15 return FalseHinweise
- Strenger Referer: Der Referer muss
xiaohongshu.comenthalten - CDN-Knoten: Unterschiedliche Knoten wie
bd(Baidu Cloud),hw(Huawei Cloud) usw. - Wasserzeichenfrei: Die von der API zurückgegebenen Links sind in der Regel wasserzeichenfrei
Weibo (Weibo)
Eigenschaften der Videolinks
Weibo-Videolinks gibt es in vielen Formaten:
1# 标准视频
2https://f.video.weibocdn.com/xxx.mp4
3
4# 直播回放
5https://live.video.weibocdn.com/xxx.flv
6
7# 高清视频
8https://video.weibo.com/media/play?fid=xxxDownload-Anforderungen
| Request-Header | Erforderlich? | Wert |
|---|---|---|
Referer | Erforderlich | https://weibo.com/ oder https://m.weibo.cn/ |
User-Agent | Empfohlen | Browser-UA |
Cookie | Teilweise erforderlich | Für private Videos erforderlich |
Beispiel für serverseitigen Download
1import httpx
2
3async def download_weibo_video(video_url: str, save_path: str) -> bool:
4 """
5 下载微博视频
6
7 微博支持多个 Referer 域名
8 """
9 headers = {
10 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
11 "Referer": "https://weibo.com/", # 或 https://m.weibo.cn/
12 "Accept": "*/*",
13 "Accept-Encoding": "identity",
14 }
15
16 async with httpx.AsyncClient(follow_redirects=True, timeout=60) as client:
17 async with client.stream("GET", video_url, headers=headers) as response:
18 if response.status_code in [200, 206]:
19 with open(save_path, "wb") as f:
20 async for chunk in response.aiter_bytes(chunk_size=8192):
21 f.write(chunk)
22 return True
23 return FalseAuswahl mehrerer Auflösungen
Weibo-Videos bieten in der Regel mehrere Auflösungen:
1def select_best_quality(media_info: dict) -> str:
2 """选择最高清晰度的视频链接"""
3 # 清晰度优先级
4 quality_priority = ["mp4_720p_mp4", "mp4_hd_mp4", "mp4_ld_mp4"]
5
6 playback_list = media_info.get("playback_list", [])
7
8 for quality in quality_priority:
9 for item in playback_list:
10 if item.get("quality_label") == quality:
11 return item.get("play_info", {}).get("url")
12
13 # 降级使用 stream_url
14 return media_info.get("stream_url")Hinweise
- Referer-Domain: Unterstützt
weibo.comundm.weibo.cn - Mehrere Auflösungen: In den Rückgabedaten sind mehrere Auflösungen auswählbar
- Ablauf des Links: Einige Links haben eine zeitliche Begrenzung
YouTube
Eigenschaften der Videolinks
YouTube-Videolinks liegen typischerweise in den folgenden Formen vor:
1# 标准视频流地址
2https://rr1---sn-xxx.googlevideo.com/videoplayback?expire=xxx&ei=xxx&ip=xxx&id=xxx&itag=xxx&source=youtube&...
3
4# 自适应流地址 (DASH)
5https://manifest.googlevideo.com/api/manifest/dash/...
6
7# 直播流地址 (HLS)
8https://manifest.googlevideo.com/api/manifest/hls_variant/...Download-Anforderungen
Strenge IP-Beschränkung: Die Video-Stream-Adresse von YouTube unterliegt einer äußerst strengen Prüfung der IP-Geolokation; zum erfolgreichen Download muss eine IP aus Los Angeles, Kalifornien, USA verwendet werden, andernfalls wird ein 403-Forbidden-Fehler zurückgegeben.
| Request-Header | Erforderlich? | Wert |
|---|---|---|
User-Agent | Erforderlich | Browser-UA |
Referer | Empfohlen | https://www.youtube.com/ |
Origin | Empfohlen | https://www.youtube.com |
| Proxy-IP | Erforderlich | IP aus Los Angeles, Kalifornien, USA |
Erläuterung der IP-Regionseinschränkung
Die Video-Stream-Adresse von YouTube wird bei der Generierung an die IP-Adresse des Anfragenden gebunden; beim Download muss dieselbe regionale IP verwendet werden:
1# 视频流 URL 中包含 IP 绑定参数
2https://rr1---sn-xxx.googlevideo.com/videoplayback?
3 expire=1234567890 # 过期时间
4 &ei=xxx # 加密标识
5 &ip=1.2.3.4 # 绑定的 IP 地址 (关键!)
6 &id=xxx
7 &itag=137 # 视频质量标识
8 &source=youtube
9 &...Warum muss es eine IP aus Los Angeles sein?
- Die meisten YouTube-CDN-Knoten befinden sich an der US-Westküste
- Bei der Generierung der Video-Stream-URL wird basierend auf der Anforderungs-IP das nächstgelegene CDN ausgewählt
- Eine nicht übereinstimmende IP-Region beim Download führt zu einem 403-Fehler
- Los Angeles ist der Standort eines der wichtigsten Rechenzentren von YouTube und bietet die beste Kompatibilität
Beispiel für serverseitigen Download
1import httpx
2
3async def download_youtube_video(
4 video_url: str,
5 save_path: str,
6 proxy: str = None # 必须是美国加州洛杉矶代理
7) -> bool:
8 """
9 下载 YouTube 视频
10
11 重要:
12 - 必须使用美国加利福尼亚州洛杉矶地区的代理 IP
13 - 其他地区 IP 会返回 403 Forbidden
14 - 视频流 URL 有时效性,通常 6 小时内有效
15 """
16 headers = {
17 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
18 "Referer": "https://www.youtube.com/",
19 "Origin": "https://www.youtube.com",
20 "Accept": "*/*",
21 "Accept-Language": "en-US,en;q=0.9",
22 "Accept-Encoding": "identity", # 避免压缩
23 "Range": "bytes=0-", # 支持断点续传
24 "Sec-Fetch-Dest": "video",
25 "Sec-Fetch-Mode": "no-cors",
26 "Sec-Fetch-Site": "cross-site",
27 }
28
29 # 代理配置 - 必须是洛杉矶 IP
30 proxies = None
31 if proxy:
32 proxies = {
33 "http://": proxy,
34 "https://": proxy,
35 }
36
37 async with httpx.AsyncClient(
38 follow_redirects=True,
39 timeout=120,
40 proxies=proxies
41 ) as client:
42 async with client.stream("GET", video_url, headers=headers) as response:
43 if response.status_code in [200, 206]:
44 with open(save_path, "wb") as f:
45 async for chunk in response.aiter_bytes(chunk_size=8192):
46 f.write(chunk)
47 return True
48 elif response.status_code == 403:
49 print("下载失败: IP 地区不符合要求,请使用美国加州洛杉矶的代理")
50 elif response.status_code == 410:
51 print("下载失败: 视频链接已过期,请重新获取")
52 return FalseTrennung von Audio und Video
YouTube-HD-Videos (1080p+) verwenden das DASH-Format; Audio und Video sind getrennt:
1import subprocess
2from pathlib import Path
3
4async def download_youtube_video_with_audio(
5 video_url: str,
6 audio_url: str,
7 save_path: str,
8 proxy: str = None
9) -> bool:
10 """
11 下载 YouTube 视频并合并音频
12
13 YouTube 1080p 及以上清晰度的视频,音频和视频是分离的
14 需要分别下载后使用 FFmpeg 合并
15 """
16 video_temp = save_path + ".video.mp4"
17 audio_temp = save_path + ".audio.m4a"
18
19 # 下载视频流
20 success = await download_youtube_video(video_url, video_temp, proxy)
21 if not success:
22 return False
23
24 # 下载音频流
25 success = await download_youtube_video(audio_url, audio_temp, proxy)
26 if not success:
27 return False
28
29 # 使用 FFmpeg 合并
30 try:
31 subprocess.run([
32 "ffmpeg", "-y",
33 "-i", video_temp,
34 "-i", audio_temp,
35 "-c:v", "copy",
36 "-c:a", "aac",
37 save_path
38 ], check=True, capture_output=True)
39
40 # 删除临时文件
41 Path(video_temp).unlink()
42 Path(audio_temp).unlink()
43 return True
44 except subprocess.CalledProcessError as e:
45 print(f"FFmpeg 合并失败: {e}")
46 return FalseBeispiel für die Proxy-Konfiguration
1# 推荐的代理配置 - 必须是洛杉矶地区
2LA_PROXIES = [
3 "http://user:pass@la-proxy1.example.com:8080", # 洛杉矶代理 1
4 "http://user:pass@la-proxy2.example.com:8080", # 洛杉矶代理 2
5 "socks5://user:pass@la-socks.example.com:1080", # SOCKS5 代理
6]
7
8# 验证代理 IP 是否在洛杉矶
9async def verify_proxy_location(proxy: str) -> bool:
10 """验证代理 IP 是否在洛杉矶地区"""
11 async with httpx.AsyncClient(proxies={"https://": proxy}) as client:
12 response = await client.get("https://ipapi.co/json/")
13 data = response.json()
14
15 city = data.get("city", "")
16 region = data.get("region", "")
17 country = data.get("country_code", "")
18
19 # 检查是否在加州洛杉矶
20 is_la = (
21 country == "US" and
22 region == "California" and
23 "Los Angeles" in city
24 )
25
26 if is_la:
27 print(f"✓ 代理 IP 位于: {city}, {region}, {country}")
28 else:
29 print(f"✗ 代理 IP 位于: {city}, {region}, {country} (不符合要求)")
30
31 return is_laZuordnungstabelle der Qualitätskennzeichnungen (itag)
| itag | Auflösung | Format | Enthält Audio? |
|---|---|---|---|
| 18 | 360p | MP4 | Ja |
| 22 | 720p | MP4 | Ja |
| 137 | 1080p | MP4 | Nein (muss zusammengeführt werden) |
| 248 | 1080p | WebM | Nein (muss zusammengeführt werden) |
| 271 | 1440p | WebM | Nein (muss zusammengeführt werden) |
| 313 | 2160p | WebM | Nein (muss zusammengeführt werden) |
| 140 | - | M4A-Audio | Nur Audio |
| 251 | - | WebM-Audio | Nur Audio |
Hinweise
- IP-Beschränkung ist extrem streng: Es muss eine IP aus Los Angeles, Kalifornien, USA verwendet werden; andere Regionen (einschließlich anderer US-Bundesstaaten) können ebenfalls fehlschlagen
- Ablaufzeit des Links: Die Video-Stream-URL ist in der Regel 6 Stunden gültig
- Trennung von Audio und Video: Bei Auflösungen über 720p müssen Audio und Video separat heruntergeladen und anschließend zusammengeführt werden
- Ratenbegrenzung: YouTube begrenzt die Download-Geschwindigkeit; für große Dateien wird segmentierter Download empfohlen
- Urheberrechtsschutz: Einige Videos sind durch DRM geschützt und können nicht direkt heruntergeladen werden
Frontend-Download-Lösung
Aufgrund von CORS-Beschränkungen kann das Frontend die Videos der meisten Plattformen nicht direkt herunterladen. Die folgenden Lösungen sind praktikabel:
Lösung 1: Backend-Proxy (empfohlen)
1// 前端请求后端代理接口
2async function downloadVideo(videoUrl, platform, filename) {
3 const proxyUrl = `/api/download/video?url=${encodeURIComponent(videoUrl)}&platform=${platform}`;
4
5 const response = await fetch(proxyUrl);
6 const blob = await response.blob();
7
8 // 创建下载链接
9 const url = URL.createObjectURL(blob);
10 const a = document.createElement('a');
11 a.href = url;
12 a.download = filename || 'video.mp4';
13 document.body.appendChild(a);
14 a.click();
15 document.body.removeChild(a);
16 URL.revokeObjectURL(url);
17}Lösung 2: In neuem Fenster öffnen (bei einigen Plattformen verfügbar)
1// 部分平台链接可以直接在新窗口打开下载
2function openVideoInNewTab(videoUrl) {
3 window.open(videoUrl, '_blank');
4}Lösung 3: Verwendung des download-Attributs (Same-Origin-Szenario)
1<!-- 仅适用于同源或允许 CORS 的资源 -->
2<a href="video.mp4" download="video.mp4">下载视频</a>Backend-Proxy-Download-Lösung
FastAPI-Proxy-Beispiel
1from fastapi import FastAPI, Query, Response
2from fastapi.responses import StreamingResponse
3import httpx
4
5app = FastAPI()
6
7# 各平台的请求头配置
8PLATFORM_HEADERS = {
9 "douyin": {
10 "Referer": "https://www.douyin.com/",
11 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
12 },
13 "tiktok": {
14 "Referer": "https://www.tiktok.com/",
15 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
16 },
17 "bilibili": {
18 "Referer": "https://www.bilibili.com/",
19 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
20 },
21 "xiaohongshu": {
22 "Referer": "https://www.xiaohongshu.com/",
23 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
24 },
25 "weibo": {
26 "Referer": "https://weibo.com/",
27 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
28 },
29}
30
31
32@app.get("/api/download/video")
33async def proxy_video(
34 url: str = Query(..., description="视频 URL"),
35 platform: str = Query(..., description="平台标识"),
36):
37 """
38 视频代理下载接口
39
40 解决前端跨域和防盗链问题
41 """
42 headers = PLATFORM_HEADERS.get(platform, {})
43
44 async def video_stream():
45 async with httpx.AsyncClient(follow_redirects=True, timeout=120) as client:
46 async with client.stream("GET", url, headers=headers) as response:
47 async for chunk in response.aiter_bytes(chunk_size=8192):
48 yield chunk
49
50 return StreamingResponse(
51 video_stream(),
52 media_type="video/mp4",
53 headers={
54 "Content-Disposition": "attachment; filename=video.mp4",
55 "Access-Control-Allow-Origin": "*", # 允许跨域
56 }
57 )
58
59
60@app.get("/api/download/image")
61async def proxy_image(
62 url: str = Query(..., description="图片 URL"),
63 platform: str = Query(..., description="平台标识"),
64):
65 """图片代理下载接口"""
66 headers = PLATFORM_HEADERS.get(platform, {})
67
68 async with httpx.AsyncClient(follow_redirects=True) as client:
69 response = await client.get(url, headers=headers)
70
71 return Response(
72 content=response.content,
73 media_type=response.headers.get("content-type", "image/jpeg"),
74 headers={
75 "Access-Control-Allow-Origin": "*",
76 }
77 )Download-Beispiel mit Fortschritt
1import httpx
2from tqdm import tqdm
3
4async def download_with_progress(url: str, save_path: str, headers: dict) -> bool:
5 """带进度条的下载"""
6 async with httpx.AsyncClient(follow_redirects=True, timeout=120) as client:
7 async with client.stream("GET", url, headers=headers) as response:
8 total = int(response.headers.get("content-length", 0))
9
10 with open(save_path, "wb") as f:
11 with tqdm(total=total, unit="B", unit_scale=True, desc="下载中") as pbar:
12 async for chunk in response.aiter_bytes(chunk_size=8192):
13 f.write(chunk)
14 pbar.update(len(chunk))
15
16 return True
17 return FalseZusammenfassung
Schnellübersicht der Download-Schlüsselpunkte je Plattform
| Plattform | Referer | Cookie-Anforderung | Besondere Behandlung | Gültigkeitsdauer des Links |
|---|---|---|---|---|
| Douyin | https://www.douyin.com/ | Optional | Keine | 24 Stunden |
| TikTok Web | https://www.tiktok.com/ | Erforderlich tt_chain_token | Region-Proxy erforderlich | 1-6 Stunden |
| TikTok App | Nicht erforderlich | Nicht erforderlich | Empfohlen zu verwenden | Länger |
| Bilibili | https://www.bilibili.com/ | Für HD SESSDATA erforderlich | Audio/Video getrennt, Zusammenführung nötig | 2 Stunden |
| Xiaohongshu | https://www.xiaohongshu.com/ | Optional | Keine | Länger |
https://weibo.com/ | Teilweise erforderlich | Mehrere Auflösungen | Länger | |
| YouTube | https://www.youtube.com/ | Nicht erforderlich | Erfordert IP aus Los Angeles + Audio/Video getrennt | 6 Stunden |
Fehlerbehebung bei häufigen Fehlern
| Fehlercode | Mögliche Ursache | Lösung |
|---|---|---|
| 403 Forbidden | Referer falsch oder fehlt | Referer-Header prüfen |
| 403 Forbidden | TikTok fehlt tt_chain_token | Cookie hinzufügen oder App-Link verwenden |
| 403 Forbidden | YouTube-IP-Region stimmt nicht überein | Proxy aus Los Angeles, Kalifornien, USA verwenden |
| 404 Not Found | Link abgelaufen | Link neu abrufen |
| 410 Gone | YouTube-Link abgelaufen | Video-Stream-URL neu abrufen |
| CORS Error | Browser-Cross-Domain-Beschränkung | Backend-Proxy verwenden |
| Timeout | Netzwerkproblem oder Datei zu groß | Timeout erhöhen, segmentiert herunterladen |
Best Practices
- Referer immer setzen: Dies ist für die meisten Plattformen eine notwendige Voraussetzung
- Backend-Proxy verwenden: Die beste Lösung zur Behebung von Frontend-CORS-Problemen
- Ablaufzeit des Links beachten: Links rechtzeitig verwenden, nach Ablauf neu abrufen
- Fortsetzbares Herunterladen unterstützen: Für große Dateien segmentiert mit dem Range-Header herunterladen
- Fehler erneut versuchen: Bei instabilem Netzwerk einen Retry-Mechanismus implementieren
Kontakt
Wenn Sie nach Befolgung der Anweisungen in diesem Dokument das Video immer noch nicht erfolgreich herunterladen können, wenden Sie sich bitte an den Kundendienst:
- Offizielle Dokumentation: https://docs.tikhub.io/
- Discord-Community: https://discord.gg/Pu2uKkFu6u
- Online-Kundendienst (Chaport): Öffnen Sie das Online-Kundendienstfenster unten rechts auf der offiziellen Website
Wenn Sie den Kundendienst kontaktieren, geben Sie bitte die folgenden Informationen an, damit wir Ihnen schneller helfen können:
- Plattformname: Douyin/TikTok/Bilibili/Xiaohongshu/Weibo/YouTube
- Videolink: URL der ursprünglichen Videoseite
- Fehlermeldung: Vollständiger Fehlercode und Fehlerbeschreibung
- Request-Header-Konfiguration: Die von Ihnen verwendeten Informationen zu Referer, Cookie usw.
- Proxy-Informationen: Falls ein Proxy verwendet wird, bitte die Proxy-Region angeben