Guide de téléchargement des liens vidéo multi-plateformes
Ce document présente en détail comment télécharger correctement des fichiers vidéo après avoir obtenu des liens vidéo depuis des plateformes comme Douyin, TikTok, Bilibili, Xiaohongshu, Weibo, etc. Il se concentre sur la résolution des problèmes courants tels que le cross-domain, le hotlinking et la vérification des en-têtes de requête.
Sommaire
- Vue d’ensemble des problèmes généraux
- 抖音 (Douyin)
- TikTok
- 哔哩哔哩 (Bilibili)
- 小红书 (Xiaohongshu)
- 微博 (Weibo)
- YouTube
- Solution de téléchargement côté front-end
- Solution de téléchargement via proxy côté back-end
- Nous contacter
Vue d’ensemble des problèmes généraux
Après avoir obtenu des liens vidéo depuis différentes plateformes, un téléchargement direct rencontre généralement les problèmes suivants :
| Type de problème | Description | Solution |
|---|---|---|
| Hotlinking (Referer) | Le serveur vérifie l’origine de la requête et refuse les sources non autorisées | Définir un en-tête Referer correct |
| Cross-domain (CORS) | La politique de sécurité du navigateur bloque les requêtes cross-domain | Proxy back-end / téléchargement côté serveur |
| Vérification du User-Agent | Le serveur vérifie l’identifiant du client | Simuler un UA de navigateur |
| Vérification des cookies | Certains liens nécessitent une session connectée | Transmettre des cookies valides |
| Validité du lien | Les liens vidéo ont une date d’expiration | Utiliser rapidement, puis récupérer à nouveau après expiration |
| Restriction IP | Certains liens limitent l’accès par région | Utiliser un proxy de la région correspondante |
抖音 (Douyin)
Caractéristiques des liens vidéo
Les liens vidéo Douyin se présentent généralement sous les formes suivantes :
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=xxxExigences de téléchargement
| En-tête de requête | Obligatoire | Valeur |
|---|---|---|
Referer | Obligatoire | https://www.douyin.com/ |
User-Agent | Recommandé | UA du navigateur |
Cookie | Facultatif | Requis pour certains liens HD |
Exemple de téléchargement côté serveur
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 FalseProblème de cross-domain côté front-end
Les liens vidéo Douyin ne prennent pas en charge CORS ; une requête directe depuis le front-end sera bloquée par le navigateur :
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 });Remarques
- Validité du lien : les liens vidéo Douyin sont généralement valides pendant 24 heures
- Lien sans filigrane : utiliser
play_addrplutôt quedownload_addr - Lien HD : certaines vidéos 4K / qualité originale sont volumineuses ; il est recommandé d’utiliser un téléchargement par segments pour éviter un échec de téléchargement unique
TikTok
Caractéristiques des liens vidéo
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.mp4Exigences de téléchargement
Liens vidéo côté Web
| En-tête de requête | Obligatoire | Valeur |
|---|---|---|
Referer | Obligatoire | https://www.tiktok.com/ |
User-Agent | Obligatoire | UA du navigateur |
Cookie | Obligatoire | Nécessite le champ tt_chain_token |
Important : les liens vidéo renvoyés par TikTok côté Web doivent inclure le cookie
tt_chain_tokenpour pouvoir être téléchargés, sinon une erreur 403 sera renvoyée.
Liens vidéo côté App
| En-tête de requête | Obligatoire | Valeur |
|---|---|---|
User-Agent | Recommandé | UA quelconque |
Cookie | Non requis | Les liens côté App n’ont pas cette restriction |
Recommandation : si vous pouvez obtenir un lien côté App, privilégiez-le ; il n’est pas nécessaire de gérer le problème des cookies.
Exemple de téléchargement côté serveur
Téléchargement côté Web (nécessite 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 FalseTéléchargement côté App (sans restriction de cookies)
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 FalseComment obtenir tt_chain_token
tt_chain_token est le cookie utilisé par TikTok pour l’authentification du téléchargement vidéo ; méthodes d’obtention :
-
Depuis le navigateur : ouvrez le site TikTok, outils de développement F12 -> Application -> Cookies -> trouvez
tt_chain_token -
Depuis la réponse API : certains en-têtes
Set-Cookiedes réponses API TikTok contiennent cette valeur -
Utiliser l’API TikHub : les données renvoyées par l’API peuvent déjà inclure un token utilisable
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 ""Restriction géographique
Les vidéos TikTok peuvent être soumises à des restrictions régionales ; certaines ne sont accessibles que dans des pays spécifiques :
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 # ...Remarques
- Restriction géographique : certaines vidéos nécessitent une IP américaine/européenne pour être accessibles
- Validité du lien : la durée de validité est d’environ 24 heures
- Sans filigrane : privilégier le champ
downloadAddr
哔哩哔哩 (Bilibili)
Caractéristiques des liens vidéo
Les liens vidéo de Bilibili sont particuliers, l’audio et la vidéo sont séparés :
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.flvExigences de téléchargement
| En-tête de requête | Obligatoire | Valeur |
|---|---|---|
Referer | Obligatoire | https://www.bilibili.com/ |
User-Agent | Recommandé | UA du navigateur |
Cookie | HD, 4K obligatoires | SESSDATA, etc. |
Range | Recommandé | Téléchargement par segments |
Exemple de téléchargement côté serveur
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 FalseRelation entre qualité et cookie
| Qualité | Connexion requise | Exigence de cookie |
|---|---|---|
| 360P/480P | Non | Aucune |
| 720P | Oui | SESSDATA |
| 1080P | Oui | SESSDATA + abonnement premium |
| 4K/HDR | Oui | SESSDATA + abonnement premium |
Remarques
- Fusion obligatoire : le format DASH sépare l’audio et la vidéo, il faut les fusionner avec FFmpeg
- Referer obligatoire : sans Referer correct, une erreur 403 sera renvoyée
- Validité du lien : environ 2 heures
- Téléchargement par segments : pour les gros fichiers, il est recommandé d’utiliser l’en-tête Range pour télécharger par segments
小红书 (Xiaohongshu)
Caractéristiques des liens vidéo
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.jpgExigences de téléchargement
| En-tête de requête | Obligatoire | Valeur |
|---|---|---|
Referer | Obligatoire | https://www.xiaohongshu.com/ |
User-Agent | Recommandé | UA du navigateur |
Cookie | Facultatif | Requis pour certains contenus |
Exemple de téléchargement côté serveur
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 FalseTéléchargement d’images
Les images Xiaohongshu ont également une protection anti-hotlinking :
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 FalseRemarques
- Referer strict : il faut impérativement un Referer de
xiaohongshu.com - Nœuds CDN :
bd(Baidu Cloud),hw(Huawei Cloud), etc., selon les différents nœuds - Sans filigrane : les liens renvoyés par l’API sont généralement sans filigrane
微博 (Weibo)
Caractéristiques des liens vidéo
Les formats de liens vidéo Weibo sont variés :
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=xxxExigences de téléchargement
| En-tête de requête | Obligatoire | Valeur |
|---|---|---|
Referer | Obligatoire | https://weibo.com/ ou https://m.weibo.cn/ |
User-Agent | Recommandé | UA du navigateur |
Cookie | Requis pour certains cas | Requis pour les vidéos privées |
Exemple de téléchargement côté serveur
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 FalseChoix de plusieurs définitions
Les vidéos Weibo proposent généralement plusieurs niveaux de qualité :
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")Remarques
- Domaine du Referer : prend en charge
weibo.cometm.weibo.cn - Plusieurs qualités : plusieurs qualités sont disponibles dans les données renvoyées
- Validité du lien : certains liens ont une durée de validité limitée
YouTube
Caractéristiques des liens vidéo
Les liens vidéo YouTube se présentent généralement sous les formes suivantes :
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/...Exigences de téléchargement
Restriction IP stricte : l’adresse du flux vidéo YouTube fait l’objet d’une vérification extrêmement stricte de la géolocalisation IP ; il faut impérativement utiliser une IP située à Los Angeles, Californie, États-Unis pour réussir le téléchargement, sinon une erreur 403 Forbidden sera renvoyée.
| En-tête de requête | Obligatoire | Valeur |
|---|---|---|
User-Agent | Obligatoire | UA du navigateur |
Referer | Recommandé | https://www.youtube.com/ |
Origin | Recommandé | https://www.youtube.com |
| IP proxy | Obligatoire | IP de Los Angeles, Californie, États-Unis |
Explication de la restriction géographique IP
L’adresse du flux vidéo YouTube est liée à l’adresse IP du demandeur lors de sa génération ; au téléchargement, il faut utiliser une IP de la même région :
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 &...Pourquoi une IP de Los Angeles est-elle obligatoire ?
- La plupart des nœuds CDN de YouTube sont situés sur la côte ouest des États-Unis
- Lors de la génération de l’URL du flux vidéo, le CDN le plus proche est choisi selon l’IP de la requête
- Une incohérence de région IP au téléchargement entraîne une erreur 403
- Los Angeles est le principal emplacement des centres de données de YouTube, avec la meilleure compatibilité
Exemple de téléchargement côté serveur
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 FalseTraitement de la séparation audio/vidéo
Les vidéos YouTube en haute définition (1080p+) utilisent le format DASH, avec audio et vidéo séparés :
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 FalseExemple de configuration du proxy
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_laTableau de correspondance des identifiants de qualité (itag)
| itag | Résolution | Format | Contient l’audio |
|---|---|---|---|
| 18 | 360p | MP4 | Oui |
| 22 | 720p | MP4 | Oui |
| 137 | 1080p | MP4 | Non (fusion requise) |
| 248 | 1080p | WebM | Non (fusion requise) |
| 271 | 1440p | WebM | Non (fusion requise) |
| 313 | 2160p | WebM | Non (fusion requise) |
| 140 | - | Audio M4A | Audio uniquement |
| 251 | - | Audio WebM | Audio uniquement |
Remarques
- Restriction IP extrêmement stricte : il faut impérativement utiliser une IP de Los Angeles, Californie, États-Unis ; les autres régions (y compris les autres États américains) peuvent échouer
- Validité du lien : l’URL du flux vidéo est généralement valide pendant 6 heures
- Séparation audio/vidéo : les qualités supérieures à 720p nécessitent de télécharger séparément l’audio et la vidéo puis de les fusionner
- Limitation de débit : YouTube limite la vitesse de téléchargement ; pour les gros fichiers, il est recommandé de télécharger par segments
- Protection des droits d’auteur : certaines vidéos sont protégées par DRM et ne peuvent pas être téléchargées directement
Solution de téléchargement côté front-end
En raison des restrictions CORS, le front-end ne peut pas télécharger directement les vidéos de la plupart des plateformes. Voici les solutions possibles :
Solution 1 : proxy back-end (recommandé)
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 : ouvrir dans une nouvelle fenêtre (disponible pour certaines plateformes)
1// 部分平台链接可以直接在新窗口打开下载
2function openVideoInNewTab(videoUrl) {
3 window.open(videoUrl, '_blank');
4}Solution 3 : utiliser l’attribut download (scénario même origine)
1<!-- 仅适用于同源或允许 CORS 的资源 -->
2<a href="video.mp4" download="video.mp4">下载视频</a>Solution de téléchargement via proxy côté back-end
Exemple de proxy FastAPI
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 )Exemple de téléchargement avec progression
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 FalseRésumé
Tableau de référence rapide des points clés de téléchargement par plateforme
| Plateforme | Referer | Exigence de cookie | Traitement spécial | Validité du lien |
|---|---|---|---|---|
| 抖音 | https://www.douyin.com/ | Facultatif | Aucun | 24 heures |
| TikTok Web | https://www.tiktok.com/ | Obligatoire tt_chain_token | Nécessite un proxy régional | 1-6 heures |
| TikTok App | Non requis | Non requis | Recommandé | Plus longue |
| B站 | https://www.bilibili.com/ | Pour la HD, SESSDATA obligatoire | La séparation audio/vidéo nécessite une fusion | 2 heures |
| 小红书 | https://www.xiaohongshu.com/ | Facultatif | Aucun | Plus longue |
| 微博 | https://weibo.com/ | Requis pour certains cas | Plusieurs qualités | Plus longue |
| YouTube | https://www.youtube.com/ | Non requis | IP de Los Angeles obligatoire + séparation audio/vidéo | 6 heures |
Dépannage des erreurs courantes
| Code d’erreur | Cause possible | Solution |
|---|---|---|
| 403 Forbidden | Referer incorrect ou manquant | Vérifier l’en-tête Referer |
| 403 Forbidden | TikTok sans tt_chain_token | Ajouter le cookie ou utiliser un lien côté App |
| 403 Forbidden | IP YouTube non conforme à la région | Utiliser un proxy de Los Angeles, Californie, États-Unis |
| 404 Not Found | Lien expiré | Récupérer à nouveau le lien |
| 410 Gone | Lien YouTube expiré | Récupérer à nouveau l’URL du flux vidéo |
| CORS Error | Restriction cross-domain du navigateur | Utiliser un proxy back-end |
| Timeout | Problème réseau ou fichier trop volumineux | Augmenter le délai d’attente, télécharger par segments |
Bonnes pratiques
- Définir systématiquement le Referer : c’est une condition nécessaire pour la plupart des plateformes
- Utiliser un proxy back-end : la meilleure solution pour résoudre les problèmes CORS côté front-end
- Gérer la validité des liens : utiliser les liens rapidement et les récupérer à nouveau après expiration
- Prendre en charge la reprise sur interruption : pour les gros fichiers, utiliser l’en-tête Range pour télécharger par segments
- Réessayer en cas d’erreur : mettre en place un mécanisme de retry lorsque le réseau est instable
Nous contacter
Si, après avoir suivi les instructions de ce document, vous ne parvenez toujours pas à télécharger la vidéo, veuillez contacter le service client pour obtenir de l’aide :
- Documentation officielle : https://docs.tikhub.io/
- Communauté Discord : https://discord.gg/Pu2uKkFu6u
- Service client en ligne (Chaport) : accédez à la fenêtre de service client en ligne en bas à droite du site officiel
Lorsque vous contactez le service client, veuillez fournir les informations suivantes afin que nous puissions vous aider plus rapidement :
- Nom de la plateforme : 抖音/TikTok/B站/小红书/微博/YouTube
- Lien vidéo : URL de la page vidéo originale
- Message d’erreur : code d’erreur complet et description de l’erreur
- Configuration des en-têtes de requête : informations sur le Referer, les cookies, etc. que vous utilisez
- Informations sur le proxy : si vous utilisez un proxy, veuillez indiquer la région du proxy