当前位置: 首页 > article >正文

Go语言实现抖音视频下载器

🚀 Go语言实现抖音视频下载器(完整代码讲解)

下面是一篇完整的抖音视频下载工具的实现及说明。该工具采用 Go 语言 编写,支持提取 无水印视频自动重试机制保存本地功能,并通过多种方式保障视频链接获取的成功率。


📌 功能介绍

  • 支持输入 抖音视频分享链接
  • 自动获取 无水印视频真实链接
  • 支持多种视频链接提取策略(API、网页爬取、无水印第三方接口);
  • 视频文件保存到本地;
  • 支持重试和备用下载方案,提高下载成功率。

🛠️ 环境要求

  • Go 1.16 及以上版本
  • 网络环境畅通(访问抖音分享链接、调用 API 接口)

💻 完整代码

以下是完整、开箱即用的 main.go 文件:

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// 主入口函数
func main() {
	// 抖音视频分享链接(替换为自己的链接)
	videoURL := "https://v.douyin.com/xxxxxxx/"

	// 保存视频到这个目录
	savePath := "./downloaded_videos"

	err := downloadVideo(videoURL, savePath)
	if err != nil {
		fmt.Printf("❌ 下载失败: %v\n", err)
	} else {
		fmt.Println("✅ 下载完成")
	}
}


// 核心逻辑


// 下载视频的主方法
func downloadVideo(shareURL string, savePath string) error {
	fmt.Println("🚀 获取视频URL中...")

	// 获取真实的视频播放链接
	actualURL, err := getActualVideoURL(shareURL)
	if err != nil {
		return fmt.Errorf("获取视频URL失败: %v", err)
	}

	fmt.Printf("🎯 获取到视频地址: %s\n", actualURL)

	// 视频文件名
	fileName := "douyin_video.mp4"

	// 创建保存目录
	if err := os.MkdirAll(savePath, os.ModePerm); err != nil {
		return fmt.Errorf("创建目录失败: %v", err)
	}

	// 完整路径
	filePath := filepath.Join(savePath, fileName)

	// 开始下载视频
	fmt.Println("⬇️ 开始下载视频...")

	err = downloadFile(actualURL, filePath)
	if err != nil {
		return fmt.Errorf("视频下载失败: %v", err)
	}

	fmt.Printf("✅ 视频成功保存到: %s\n", filePath)
	return nil
}


// 通过API或HTML解析视频地址


// 获取真实的抖音视频播放地址
func getActualVideoURL(shareURL string) (string, error) {
	// 先尝试 API 获取视频地址
	videoURL, err := getVideoInfoFromAPI(shareURL)
	if err == nil {
		return videoURL, nil
	}

	fmt.Println("⚠️ API获取失败,尝试解析网页源码...")

	// 再尝试页面解析
	videoURL, err = getVideoURLFromHTML(shareURL)
	if err == nil {
		return videoURL, nil
	}

	fmt.Println("⚠️ 网页解析失败,尝试无水印API...")

	// 最后尝试第三方无水印API
	videoURL, err = getNoWatermarkVideo(shareURL)
	if err == nil {
		return videoURL, nil
	}

	return "", fmt.Errorf("无法获取有效视频地址")
}

// 通过抖音官方API获取视频信息
func getVideoInfoFromAPI(shareURL string) (string, error) {
	client := &http.Client{
		Timeout: 10 * time.Second,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return nil
		},
	}

	// 第一步:请求分享URL获取真实视频ID
	req, _ := http.NewRequest("GET", shareURL, nil)
	req.Header.Set("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X)")

	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	finalURL := resp.Request.URL.String()
	fmt.Printf("👉 真实跳转URL: %s\n", finalURL)

	// 从URL或页面源码中提取视频ID
	re := regexp.MustCompile(`/video/(\d+)`)
	matches := re.FindStringSubmatch(finalURL)
	if len(matches) < 2 {
		return "", fmt.Errorf("无法提取视频ID")
	}
	videoID := matches[1]

	// 第二步:请求API获取视频播放地址
	apiURL := fmt.Sprintf("https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=%s", videoID)

	apiReq, _ := http.NewRequest("GET", apiURL, nil)
	apiReq.Header.Set("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X)")

	apiResp, err := client.Do(apiReq)
	if err != nil {
		return "", err
	}
	defer apiResp.Body.Close()

	body, _ := ioutil.ReadAll(apiResp.Body)

	var result map[string]interface{}
	if err := json.Unmarshal(body, &result); err != nil {
		return "", err
	}

	itemList := result["item_list"].([]interface{})
	if len(itemList) == 0 {
		return "", fmt.Errorf("API返回空数据")
	}

	item := itemList[0].(map[string]interface{})
	video := item["video"].(map[string]interface{})
	playAddr := video["play_addr"].(map[string]interface{})
	urlList := playAddr["url_list"].([]interface{})

	return urlList[0].(string), nil
}

// 解析网页源码获取视频URL
func getVideoURLFromHTML(shareURL string) (string, error) {
	client := &http.Client{
		Timeout: 10 * time.Second,
	}

	req, _ := http.NewRequest("GET", shareURL, nil)
	req.Header.Set("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X)")

	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)

	// 使用正则提取播放地址
	re := regexp.MustCompile(`"playAddr":"(.*?)"`)
	matches := re.FindStringSubmatch(string(body))
	if len(matches) < 2 {
		return "", fmt.Errorf("无法解析视频地址")
	}

	// 处理转义字符
	videoURL := strings.ReplaceAll(matches[1], "\\u002F", "/")

	return videoURL, nil
}

// 调用第三方API获取无水印视频
func getNoWatermarkVideo(shareURL string) (string, error) {
	apiURL := fmt.Sprintf("https://api.douyin.wtf/api?url=%s", url.QueryEscape(shareURL))

	client := &http.Client{
		Timeout: 10 * time.Second,
	}

	req, _ := http.NewRequest("GET", apiURL, nil)
	req.Header.Set("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X)")

	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)

	var result map[string]interface{}
	if err := json.Unmarshal(body, &result); err != nil {
		return "", err
	}

	videoURL := result["nwm_video_url"].(string)
	if videoURL == "" {
		return "", fmt.Errorf("第三方API未返回有效URL")
	}

	return videoURL, nil
}


// 下载文件


func downloadFile(fileURL, filePath string) error {
	client := &http.Client{
		Timeout: 30 * time.Second,
	}

	req, _ := http.NewRequest("GET", fileURL, nil)
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return fmt.Errorf("状态码错误: %d", resp.StatusCode)
	}

	out, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return err
	}

	return nil
}

📂 文件结构

douyin_downloader/
├── main.go
├── downloaded_videos/   # 下载后的视频保存目录

📝 代码结构解析

模块说明
main()程序入口,初始化分享链接和保存目录,调用下载方法
downloadVideo()核心下载逻辑,先获取视频链接,执行下载,保存到本地
getActualVideoURL()获取真实视频地址,依次尝试 API、HTML解析、第三方API获取
getVideoInfoFromAPI()从分享链接跳转获取视频ID,通过API提取真实播放地址
getVideoURLFromHTML()通过HTML源码解析 playAddr,作为兜底方案
getNoWatermarkVideo()通过第三方无水印API获取无水印视频链接
downloadFile()下载视频流并保存文件

🧰 可扩展功能(进阶)

  1. 批量下载:读取多个链接批量处理;
  2. 命令行参数:支持 CLI 参数传递链接与路径;
  3. 进度条:下载时加入进度条提示;
  4. 并发优化:使用 Go 协程批量并发下载多个视频;
  5. 断点续传:支持大文件续传功能;

⚠️ 注意事项

  • 抖音接口和数据结构可能变化,需定期维护正则表达式和API路径;
  • 使用第三方API请注意接口稳定性;
  • 仅供学习交流使用,不得用于任何非法用途;

✅ 运行方法

  1. videoURL 替换为自己的抖音视频分享链接;
  2. 终端执行:
go run main.go
  1. 下载完成后文件保存在 downloaded_videos 文件夹。

🎉 总结

本文展示了一个完整的 抖音无水印视频下载器,通过 API+网页解析+第三方接口 多种方式保证了视频下载的鲁棒性。Go 语言的并发和HTTP请求能力也让下载器高效稳定!


http://www.kler.cn/a/611648.html

相关文章:

  • K8S学习之基础五十八:部署nexus服务
  • asp.net mvc 向前端响应json数据。用到jquery
  • postman测试调用WebService时不会自动添加命名空间
  • 【Pandas】pandas Series to_frame
  • HTTP 1.0和2.0 有什么区别?
  • Ubuntu24.04 离线安装 MySQL8.0.41
  • 盾牌图鉴(死亡细胞)
  • Python爬虫教程003:请求对象的定制、get请求的quote和urlencode方法
  • TypeScript三斜线指令深度解析:原理剖析与实战指南
  • 【机器学习】——机器学习思考总结
  • Jmeter生成HTML测试报告
  • 如何量化“模糊需求”的技术可行性
  • 自然语言处理(12:RNN(正式介绍))
  • YoloV8训练和平精英人物检测模型
  • 26.红黑树及其模拟实现
  • LibVLC —— 《基于Qt的LibVLC专业开发技术》视频教程
  • 【redis】集群 如何搭建集群详解
  • AI for CFD入门指南(传承版)
  • mac m 芯片 动态切换 jdk 版本jdk8.jdk11.jdk17
  • 【洛谷题单】暴力枚举(上)