Multi-Platform Video Link Download Guide
This document provides a detailed introduction to how to correctly download video files after obtaining video links from platforms such as Douyin, TikTok, Bilibili, Xiaohongshu, Weibo, and others. It focuses on solving common issues such as cross-origin requests, hotlink protection, and request header verification.
Table of Contents
- General Issues Overview
- Douyin (Douyin)
- TikTok
- Bilibili (Bilibili)
- Xiaohongshu (Xiaohongshu)
- Weibo (Weibo)
- YouTube
- Frontend Download Solution
- Backend Proxy Download Solution
- Contact Us
General Issues Overview
After obtaining video links from various platforms, direct downloads usually encounter the following issues:
| Issue Type | Description | Solution |
|---|---|---|
| Hotlink Protection (Referer) | The server checks the request source and rejects invalid sources | Set the correct Referer header |
| Cross-Origin (CORS) | Browser security policies block cross-origin requests | Backend proxy / server-side download |
| User-Agent Verification | The server checks the client identifier | Simulate a browser UA |
| Cookie Verification | Some links require a logged-in session | Carry valid cookies |
| Link Expiration | Video links have an expiration time | Use them promptly; re-obtain after expiration |
| IP Restrictions | Some links restrict access by region | Use a proxy in the corresponding region |
Douyin (Douyin)
Video Link Characteristics
Douyin video links usually come in the following forms:
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 Requirements
| Request Header | Required | Value |
|---|---|---|
Referer | Required | https://www.douyin.com/ |
User-Agent | Recommended | Browser UA |
Cookie | Optional | Required for some HD links |
Server-Side Download Example
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-Origin Issue
Douyin video links do not support CORS; direct frontend requests will be blocked by the browser:
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 });Notes
- Link Expiration: Douyin video links are usually valid for 24 hours
- No-Watermark Links: Use
play_addrinstead ofdownload_addr - HD Links: Some 4K/original-quality videos are large; segmented download is recommended to avoid a one-time download failure
TikTok
Video Link Characteristics
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 Requirements
Web Video Links
| Request Header | Required | Value |
|---|---|---|
Referer | Required | https://www.tiktok.com/ |
User-Agent | Required | Browser UA |
Cookie | Required | Requires the tt_chain_token field |
Important: TikTok Web video links must include the
tt_chain_tokencookie in order to download, otherwise a 403 error will be returned.
App Video Links
| Request Header | Required | Value |
|---|---|---|
User-Agent | Recommended | Any UA |
Cookie | Not required | App links have no such restriction |
Recommendation: If you can obtain an App link, prioritize using the App link; there is no need to deal with cookie issues.
Server-Side Download Example
Web Download (tt_chain_token required)
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 FalseApp Download (no cookie restriction)
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 FalseHow to Obtain tt_chain_token
tt_chain_token is the cookie TikTok uses for video download authentication. Ways to obtain it:
-
From the browser: Open the TikTok website, press F12 to open Developer Tools -> Application -> Cookies -> find
tt_chain_token -
From API responses: Some TikTok API responses include this value in the
Set-Cookieheader -
Use the TikHub API: The API response data may already include an available token
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 ""Regional Restrictions
TikTok videos may have regional restrictions, and some videos are only accessible in specific countries:
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 # ...Notes
- Regional Restrictions: Some videos require a US/European IP to access
- Link Expiration: Link validity is about 24 hours
- No Watermark: Prefer the
downloadAddrfield
Bilibili (Bilibili)
Video Link Characteristics
Bilibili video links are somewhat special: audio and video are separated:
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 Requirements
| Request Header | Required | Value |
|---|---|---|
Referer | Required | https://www.bilibili.com/ |
User-Agent | Recommended | Browser UA |
Cookie | Required for HD, 4K | SESSDATA, etc. |
Range | Recommended | Segmented download |
Server-Side Download Example
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 FalseRelationship Between Quality and Cookies
| Quality | Login Required | Cookie Requirement |
|---|---|---|
| 360P/480P | No | None |
| 720P | Yes | SESSDATA |
| 1080P | Yes | SESSDATA + Premium membership |
| 4K/HDR | Yes | SESSDATA + Premium membership |
Notes
- Must Merge: DASH format separates audio and video; use FFmpeg to merge them
- Referer Required: Without the correct Referer, a 403 will be returned
- Link Expiration: Valid for about 2 hours
- Segmented Download: For large files, use the Range header for segmented downloads
Xiaohongshu (Xiaohongshu)
Video Link Characteristics
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 Requirements
| Request Header | Required | Value |
|---|---|---|
Referer | Required | https://www.xiaohongshu.com/ |
User-Agent | Recommended | Browser UA |
Cookie | Optional | Required for some content |
Server-Side Download Example
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 FalseImage Download
Xiaohongshu images also have hotlink protection:
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 FalseNotes
- Strict Referer: Must include a Referer from
xiaohongshu.com - CDN Nodes: Different nodes such as
bd(Baidu Cloud),hw(Huawei Cloud), etc. - No Watermark: Links returned by the API are usually watermark-free
Weibo (Weibo)
Video Link Characteristics
Weibo video links come in many formats:
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 Requirements
| Request Header | Required | Value |
|---|---|---|
Referer | Required | https://weibo.com/ or https://m.weibo.cn/ |
User-Agent | Recommended | Browser UA |
Cookie | Required for some | Required for private videos |
Server-Side Download Example
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 FalseMultiple Quality Options
Weibo videos usually provide multiple quality options:
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")Notes
- Referer Domain: Supports
weibo.comandm.weibo.cn - Multiple Qualities: Multiple quality options are available in the returned data
- Link Expiration: Some links have time limits
YouTube
Video Link Characteristics
YouTube video links usually come in the following forms:
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 Requirements
Strict IP Restriction: YouTube video stream URLs undergo extremely strict IP geolocation verification. You must use an IP from Los Angeles, California, USA to download successfully; otherwise, a 403 Forbidden error will be returned.
| Request Header | Required | Value |
|---|---|---|
User-Agent | Required | Browser UA |
Referer | Recommended | https://www.youtube.com/ |
Origin | Recommended | https://www.youtube.com |
| Proxy IP | Required | Los Angeles, California, USA IP |
IP Region Restriction Explanation
When YouTube video stream URLs are generated, they are bound to the requester’s IP address. During download, you must use an IP from the same region:
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 &...Why must it be a Los Angeles IP?
- Most of YouTube’s CDN nodes are located on the US West Coast
- When video stream URLs are generated, the nearest CDN is selected based on the request IP
- A region mismatch during download will cause a 403 error
- Los Angeles is where YouTube’s main data centers are located, offering the best compatibility
Server-Side Download Example
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 FalseAudio/Video Separation Handling
YouTube HD videos (1080p+) use DASH format, with audio and video separated:
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 FalseProxy Configuration Example
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_laQuality Identifier (itag) Reference Table
| itag | Resolution | Format | Includes Audio |
|---|---|---|---|
| 18 | 360p | MP4 | Yes |
| 22 | 720p | MP4 | Yes |
| 137 | 1080p | MP4 | No (needs merging) |
| 248 | 1080p | WebM | No (needs merging) |
| 271 | 1440p | WebM | No (needs merging) |
| 313 | 2160p | WebM | No (needs merging) |
| 140 | - | M4A audio | Audio only |
| 251 | - | WebM audio | Audio only |
Notes
- IP Restriction Is Extremely Strict: You must use an IP from Los Angeles, California, USA; other regions (including other US states) may fail
- Link Expiration: Video stream URLs are usually valid for 6 hours
- Audio/Video Separation: Qualities above 720p require downloading audio and video separately and then merging them
- Rate Limiting: YouTube limits download speed; for large files, segmented download is recommended
- Copyright Protection: Some videos are DRM-protected and cannot be downloaded directly
Frontend Download Solution
Due to CORS restrictions, the frontend cannot directly download videos from most platforms. The following are feasible solutions:
Solution 1: Backend Proxy (Recommended)
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}Solution 2: Open in a New Window (Available for Some Platforms)
1// 部分平台链接可以直接在新窗口打开下载
2function openVideoInNewTab(videoUrl) {
3 window.open(videoUrl, '_blank');
4}Solution 3: Use the download Attribute (Same-Origin Scenarios)
1<!-- 仅适用于同源或允许 CORS 的资源 -->
2<a href="video.mp4" download="video.mp4">下载视频</a>Backend Proxy Download Solution
FastAPI Proxy Example
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 Example with Progress
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 FalseSummary
Quick Reference Table of Download Key Points by Platform
| Platform | Referer | Cookie Requirement | Special Handling | Link Validity |
|---|---|---|---|---|
| Douyin | https://www.douyin.com/ | Optional | None | 24 hours |
| TikTok Web | https://www.tiktok.com/ | Required tt_chain_token | Requires regional proxy | 1-6 hours |
| TikTok App | Not required | Not required | Recommended | Longer |
| Bilibili | https://www.bilibili.com/ | HD requires SESSDATA | Audio/video separation requires merging | 2 hours |
| Xiaohongshu | https://www.xiaohongshu.com/ | Optional | None | Longer |
https://weibo.com/ | Required for some | Multiple qualities | Longer | |
| YouTube | https://www.youtube.com/ | Not required | Must use Los Angeles IP + audio/video separation | 6 hours |
Common Error Troubleshooting
| Error Code | Possible Cause | Solution |
|---|---|---|
| 403 Forbidden | Incorrect or missing Referer | Check the Referer header |
| 403 Forbidden | TikTok missing tt_chain_token | Add cookies or use the App link |
| 403 Forbidden | YouTube IP region mismatch | Use a Los Angeles, California, USA proxy |
| 404 Not Found | Link expired | Re-obtain the link |
| 410 Gone | YouTube link expired | Re-obtain the video stream URL |
| CORS Error | Browser cross-origin restriction | Use a backend proxy |
| Timeout | Network issues or file too large | Increase timeout, use segmented download |
Best Practices
- Always set Referer: This is a necessary condition for most platforms
- Use a Backend Proxy: The best solution for frontend CORS issues
- Handle Link Expiration: Use links promptly and re-obtain them after expiration
- Support Resume Downloads: Use the Range header for segmented downloads on large files
- Error Retries: Implement a retry mechanism when the network is unstable
Contact Us
If you still cannot successfully download videos after following the instructions in this document, please contact customer support for help:
- Official Documentation: https://docs.tikhub.io/
- Discord Community: https://discord.gg/Pu2uKkFu6u
- Online Support (Chaport): Visit the online support window in the bottom-right corner of the official website
When contacting customer support, please provide the following information so we can help you more quickly:
- Platform Name: Douyin/TikTok/Bilibili/Xiaohongshu/Weibo/YouTube
- Video Link: Original video page URL
- Error Message: Complete error code and error description
- Request Header Configuration: Referer, Cookie, and other information you used
- Proxy Information: If using a proxy, please provide the proxy region