吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 10760|回复: 42
收起左侧

[其他转载] [golang]抖音无水印视频下载

[复制链接]
hahaxi 发表于 2021-5-31 18:34
本帖最后由 hahaxi 于 2021-10-15 18:56 编辑

参考地址

本贴教程代码仅供个人学习交流使用,严禁非法获利。

今天看到Quincy_007 的这个帖子,用go重写了一下,获取sec_uid那里的正则有点问题,就用截取法截取了。

使用方式:同目录下创建 url.txt  每个抖音分享链接址一行  执行  go run main.go 即可  
需要按年归档的 后面加 -y  即 go run main.go -y

url.txt如图
微信图片_20210531215359.png


[Golang] 纯文本查看 复制代码
package main

import (
        "bufio"
        "flag"
        "fmt"
        "github.com/cilidm/toolbox/file"
        "github.com/dustin/go-humanize"
        "github.com/kirinlabs/HttpRequest"
        "github.com/tidwall/gjson"
        "io"
        "log"
        "net/http"
        "os"
        "regexp"
        "strconv"
        "strings"
        "time"
)

var year bool

func init() {
        flag.BoolVar(&year, "y", false, "是否按年归类")
}

func main() {
        flag.Parse()
        SpiderDY(year)
}

// --------------spider----------------
var req *HttpRequest.Request
var downloadDir = "download"

func init() {
        req = HttpRequest.NewRequest()
        req.SetHeaders(map[string]string{
                "User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36",
        })
        req.CheckRedirect(func(req *http.Request, via []*http.Request) error {
                return http.ErrUseLastResponse /* 不进入重定向 */
        })
}

func SpiderDY(year bool) {
        lines, err := ReadLine("url.txt")
        if err != nil {
                os.Create("url.txt")
                log.Fatal("未找到url.txt文件,已自动创建,请在同目录下url.txt文件加入分享链接,每个分享链接一行")
        }
        for _, line := range lines {
                reg := regexp.MustCompile(`[a-z]+://[\S]+`)
                url := reg.FindAllString(line, -1)[0]
                resp, err := req.Get(url)
                defer resp.Close()
                if err != nil {
                        fmt.Errorf(err.Error())
                        continue
                }
                if resp.StatusCode() != 302 {
                        continue
                }
                location := resp.Headers().Values("location")[0]

                regNew := regexp.MustCompile(`(?:sec_uid=)[a-z,A-Z,0-9, _, -]+`)
                sec_uid := strings.Replace(regNew.FindAllString(location, -1)[0], "sec_uid=", "", 1)

                respIes, err := req.Get(fmt.Sprintf("https://www.iesdouyin.com/web/api/v2/user/info/?sec_uid=%s", sec_uid))
                defer respIes.Close()
                if err != nil {
                        fmt.Errorf(err.Error())
                        continue
                }
                body, err := respIes.Body()
                result := gjson.Get(string(body), "user_info.nickname").String()
                dirPath := fmt.Sprintf("%s/%s/", downloadDir, result)
                err = file.IsNotExistMkDir(dirPath)
                if err != nil {
                        fmt.Errorf(err.Error())
                        continue
                }
                GetByMonth(sec_uid, dirPath, year)
        }
}

func GetByMonth(sec_uid, dirPath string, year bool) {
        y := 2018
        nowY, _ := strconv.Atoi(time.Now().Format("2006"))
        nowM, _ := strconv.Atoi(time.Now().Format("01"))

        for i := y; i <= nowY; i++ {
                for m := 1; m <= 12; m++ {
                        var (
                                begin int64
                                end   int64
                        )
                        if i == nowY && m > nowM {
                                break
                        }
                        begin = GetMonthStartAndEnd(strconv.Itoa(i), strconv.Itoa(m))
                        if m == 12 {
                                end = GetMonthStartAndEnd(strconv.Itoa(i+1), "1")
                        } else {
                                end = GetMonthStartAndEnd(strconv.Itoa(i), strconv.Itoa(m+1))
                        }
                        resp, err := req.Get(fmt.Sprintf("https://www.iesdouyin.com/web/api/v2/aweme/post/?sec_uid=%s&count=200&min_cursor=%d&max_cursor=%d&aid=1128&_signature=PtCNCgAAXljWCq93QOKsFT7QjR",
                                sec_uid, begin, end))
                        defer resp.Close()
                        if err != nil {
                                fmt.Errorf(err.Error())
                                continue
                        }
                        body, err := resp.Body()

                        r := gjson.Get(string(body), "aweme_list").Array()
                        if len(r) > 0 {
                                if year {
                                        dirPath = fmt.Sprintf("%s%d/", dirPath, i)
                                        err = file.IsNotExistMkDir(dirPath)
                                        if err != nil {
                                                fmt.Errorf(err.Error())
                                                continue
                                        }
                                }
                                for n := 0; n < len(r); n++ {
                                        videotitle := gjson.Get(string(body), fmt.Sprintf("aweme_list.%d.desc", n)).String()
                                        videourl := gjson.Get(string(body), fmt.Sprintf("aweme_list.%d.video.play_addr.url_list.0", n)).String()
                                        err = DownloadFile(dirPath+videotitle+".mp4", videourl)
                                        if err != nil {
                                                fmt.Errorf(err.Error())
                                                continue
                                        }
                                }
                        }
                }
        }
}

// -----------------util----------------------
// 下载文件显示进度条
type WriteCounter struct {
        Name  string
        Total uint64
}

func (wc *WriteCounter) Write(p []byte) (int, error) {
        n := len(p)
        wc.Total += uint64(n)
        wc.PrintProgress()
        return n, nil
}

func (wc WriteCounter) PrintProgress() {
        fmt.Printf("\r%s", strings.Repeat(" ", 35))
        fmt.Printf("\r 【%s】 Downloading... %s complete", wc.Name, humanize.Bytes(wc.Total))
}

func DownloadFile(fileName string, url string) error {
        req, err := http.NewRequest("GET", url, nil)
        req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36")

        out, err := os.Create(fileName + ".tmp")
        if err != nil {
                return err
        }
        resp, err := (&http.Client{}).Do(req)
        if err != nil {
                out.Close()
                return err
        }
        defer resp.Body.Close()
        counter := &WriteCounter{}
        fileNameSp := strings.Split(fileName, "/")
        counter.Name = fileNameSp[len(fileNameSp)-1]
        if _, err = io.Copy(out, io.TeeReader(resp.Body, counter)); err != nil {
                out.Close()
                return err
        }
        fmt.Print("\n")
        out.Close()
        if err = os.Rename(fileName+".tmp", fileName); err != nil {
                return err
        }
        return nil
}

//GetMonthStartAndEnd 获取月份的第一天和最后一天
func GetMonthStartAndEnd(myYear string, myMonth string) int64 {
        // 数字月份必须前置补零
        if len(myMonth) == 1 {
                myMonth = "0" + myMonth
        }
        yInt, _ := strconv.Atoi(myYear)

        timeLayout := "2006-01-02 15:04:05"
        loc, _ := time.LoadLocation("Local")
        theTime, _ := time.ParseInLocation(timeLayout, myYear+"-"+myMonth+"-01 00:00:00", loc)
        newMonth := theTime.Month()

        t1 := time.Date(yInt, newMonth, 1, 0, 0, 0, 0, time.Local).UnixNano() / 1e6
        return t1
}

// 按行读取配置
func ReadLine(fileName string) (lines []string, err error) {
        f, err := os.Open(fileName)
        if err != nil {
                return nil, err
        }
        buf := bufio.NewReader(f)
        for {
                line, err := buf.ReadString('\n')
                line = strings.TrimSpace(line)
                lines = append(lines, line)
                if err != nil {
                        if err == io.EOF {
                                return lines, nil
                        }
                        return nil, err
                }
        }
}


免费评分

参与人数 6吾爱币 +5 热心值 +4 收起 理由
PrincessSnow + 1 + 1 我很赞同!
baohun + 1 + 1 我很赞同!
airwenlee + 1 + 1 谢谢@Thanks!
dgy + 1 谢谢@Thanks!
小黑啊123 + 1 谢谢@Thanks!
哥尔赞777 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

忆夕语 发表于 2021-6-13 21:45
本帖最后由 忆夕语 于 2021-6-13 21:50 编辑
hahaxi 发表于 2021-5-31 22:05
原帖不知道怎么编辑代码  老是带有格式  更新在这里好了
修改下载功能   验证视频在本地是否存在 存在则 ...
package main

import (
        "bufio"
        "flag"
        "fmt"
        "github.com/cilidm/toolbox/file"
        "github.com/dustin/go-humanize"
        "github.com/kirinlabs/HttpRequest"
        "github.com/tidwall/gjson"
        "io"
        "log"
        "net/http"
        "os"
        "path/filepath"
        "regexp"
        "strconv"
        "strings"
        "sync"
        "time"
)

var IsYear bool

func init() {
        flag.BoolVar(&IsYear, "y", false, "是否按年归类")
}

func main() {
        flag.Parse()
        SpiderDY()
}

// --------------spider----------------
var (
        req         *HttpRequest.Request
        downloadDir = "download"
)

func init() {
        req = HttpRequest.NewRequest()
        req.SetHeaders(map[string]string{
                "User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36",
        })
        req.CheckRedirect(func(req *http.Request, via []*http.Request) error {
                return http.ErrUseLastResponse /* 不进入重定向 */
        })
}

// 爬取文件
func SpiderDY() {
        lines, err := ReadLine("url.txt")
        if err != nil {
                os.Create("url.txt")
                log.Fatal("未找到url.txt文件,已自动创建,请在同目录下url.txt文件加入分享链接,每个分享链接一行")
        }
        for _, line := range lines {
                reg := regexp.MustCompile(`[a-z]+://[\S]+`)
                url := reg.FindAllString(line, -1)[0]
                resp, err := req.Get(url)
                defer resp.Close()
                if err != nil {
                        fmt.Errorf(err.Error())
                        continue
                }
                if resp.StatusCode() != 302 {
                        continue
                }
                location := resp.Headers().Values("location")[0]

                regNew := regexp.MustCompile(`(?:sec_uid=)[a-z,A-Z,0-9, _, -]+`)
                sec_uid := strings.Replace(regNew.FindAllString(location, -1)[0], "sec_uid=", "", 1)

                respIes, err := req.Get(fmt.Sprintf("https://www.iesdouyin.com/web/api/v2/user/info/?sec_uid=%s", sec_uid))
                defer respIes.Close()
                if err != nil {
                        fmt.Errorf(err.Error())
                        continue
                }
                body, err := respIes.Body()
                result := gjson.Get(string(body), "user_info.nickname").String()
                dirPath := fmt.Sprintf("%s/%s/", downloadDir, result)
                err = file.IsNotExistMkDir(dirPath)
                if err != nil {
                        fmt.Errorf(err.Error())
                        continue
                }
                GetByMonth(sec_uid, dirPath)
        }
}

func GetByMonth(secUid, dirPath string) {
        y := 2018
        nowY, _ := strconv.Atoi(time.Now().Format("2006"))
        nowM, _ := strconv.Atoi(time.Now().Format("01"))
        // 原始目录路径
        rawDirPath := dirPath
        for i := y; i <= nowY; i++ {
                for m := 1; m <= 12; m++ {
                        var (
                                begin int64
                                end   int64
                        )
                        if i == nowY && m > nowM {
                                break
                        }
                        begin = GetMonthStartAndEnd(strconv.Itoa(i), strconv.Itoa(m))
                        if m == 12 {
                                end = GetMonthStartAndEnd(strconv.Itoa(i+1), "1")
                        } else {
                                end = GetMonthStartAndEnd(strconv.Itoa(i), strconv.Itoa(m+1))
                        }
                        resp, err := req.Get(fmt.Sprintf("https://www.iesdouyin.com/web/api/v2/aweme/post/?sec_uid=%s&count=200&min_cursor=%d&max_cursor=%d&aid=1128&_signature=PtCNCgAAXljWCq93QOKsFT7QjR",
                                secUid, begin, end))
                        defer resp.Close()
                        if err != nil {
                                fmt.Errorf(err.Error())
                                continue
                        }
                        body, err := resp.Body()

                        r := gjson.Get(string(body), "aweme_list").Array()
                        if len(r) > 0 {
                                if IsYear {
                                        // 原始目录路径 + 年
                                        dirPath = fmt.Sprintf("%s%d/", rawDirPath, i)
                                        err = file.IsNotExistMkDir(dirPath)
                                        if err != nil {
                                                fmt.Errorf(err.Error())
                                                continue
                                        }
                                }
                                for n := 0; n < len(r); n++ {
                                        videotitle := gjson.Get(string(body), fmt.Sprintf("aweme_list.%d.desc", n)).String()
                                        videourl := gjson.Get(string(body), fmt.Sprintf("aweme_list.%d.video.play_addr.url_list.0", n)).String()
                                        err = DownloadFile(dirPath+videotitle+".mp4", videourl)
                                        if err != nil {
                                                fmt.Errorf(err.Error())
                                                continue
                                        }
                                }
                        }
                }
        }
}

// -----------------util----------------------
// 下载文件显示进度条
type WriteCounter struct {
        Name  string
        Total uint64
}

func (wc *WriteCounter) Write(p []byte) (int, error) {
        n := len(p)
        wc.Total += uint64(n)
        wc.PrintProgress()
        return n, nil
}

func (wc WriteCounter) PrintProgress() {
        fmt.Printf("\r%s", strings.Repeat(" ", 35))
        fmt.Printf("\r 【%s】 Downloading... %s complete", wc.Name, humanize.Bytes(wc.Total))
}
func DownloadFile(fileName string, url string) error {
        req, err := http.NewRequest("GET", url, nil)
        req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36")
        resp, err := (&http.Client{}).Do(req)
        if err != nil {
                return err
        }
        defer resp.Body.Close()

        fileNameSp := strings.Split(fileName, "/")
        if !file.CheckNotExist(fileName) {
                stat, err := os.Stat(fileName)
                if err != nil {
                        return err
                }
                newSize := resp.Header.Get("Content-Length")
                if newSize == strconv.FormatInt(stat.Size(), 10) {
                        log.Println(fileNameSp[len(fileNameSp)-1], "已存在,跳过")
                        return nil
                }
        }

        out, err := os.Create(fileName + ".tmp")
        if err != nil {
                defer out.Close()
                return err
        }
        counter := &WriteCounter{}
        counter.Name = fileNameSp[len(fileNameSp)-1]
        if _, err = io.Copy(out, io.TeeReader(resp.Body, counter)); err != nil {
                out.Close()
                return err
        }
        fmt.Println()
        out.Close()
        if err = os.Rename(fileName+".tmp", fileName); err != nil {
                return err
        }
        return nil
}

//GetMonthStartAndEnd 获取月份的第一天和最后一天
func GetMonthStartAndEnd(myYear string, myMonth string) int64 {
        // 数字月份必须前置补零
        if len(myMonth) == 1 {
                myMonth = "0" + myMonth
        }
        yInt, _ := strconv.Atoi(myYear)

        timeLayout := "2006-01-02 15:04:05"
        loc, _ := time.LoadLocation("Local")
        theTime, _ := time.ParseInLocation(timeLayout, myYear+"-"+myMonth+"-01 00:00:00", loc)
        newMonth := theTime.Month()
        return time.Date(yInt, newMonth, 1, 0, 0, 0, 0, time.Local).UnixNano() / 1e6
}

// 按行读取配置
func ReadLine(fileName string) (lines []string, err error) {
        f, err := os.Open(fileName)
        if err != nil {
                return nil, err
        }
        buf := bufio.NewReader(f)
        for {
                line, err := buf.ReadString('\n')
                line = strings.TrimSpace(line)
                lines = append(lines, line)
                if err != nil {
                        if err == io.EOF {
                                return lines, nil
                        }
                        return nil, err
                }
        }
}

func RemoveFiles() {
        files, err := GetDirs("./download/Fairy", ".tmp")
        if err != nil {
                fmt.Println(files, err)
                return
        }
        filesNum := len(files)
        //fmt.Println(strings.Join(files, "\n"))
        fmt.Printf("文件总数: %d\n", filesNum)
        var waitGroup sync.WaitGroup
        for _, v := range files {
                fileName := v
                waitGroup.Add(1)
                go func() {
                        defer waitGroup.Done()
                        os.Remove(fileName)
                }()
        }
        fmt.Println("finish ^^!")
        waitGroup.Wait()
}

// 获取目录下的符合后缀的所有文件
func GetDirs(dirPath, suffix string) (files []string, err error) {
        files = make([]string, 0, 500)
        suffix = strings.ToUpper(suffix)
        err = filepath.Walk(dirPath, func(fileName string, fi os.FileInfo, err error) error {
                if fi.IsDir() {
                        return nil
                }
                if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
                        files = append(files, fileName)
                }
                return nil
        })
        return files, err
}

fix:
  • 修复按照年度下载文件目录累加问题
  • 修复第二次下载,出现重复文件跳过时,存在tmp文件

其他还可以优化,有错误请指出


:loveliness:

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
hahaxi + 1 + 1 谢谢@Thanks!

查看全部评分

 楼主| hahaxi 发表于 2021-5-31 22:05
本帖最后由 hahaxi 于 2021-5-31 22:22 编辑

原帖不知道怎么编辑代码  老是带有格式  更新在这里好了
修改下载功能   验证视频在本地是否存在 存在则跳过  修改这个函数就可以
[Golang] 纯文本查看 复制代码
func DownloadFile(fileName string, url string) error {
        req, err := http.NewRequest("GET", url, nil)
        req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36")

        out, err := os.Create(fileName + ".tmp")
        if err != nil {
                return err
        }
        resp, err := (&http.Client{}).Do(req)
        if err != nil {
                out.Close()
                return err
        }
        defer resp.Body.Close()

        fileNameSp := strings.Split(fileName, "/")
        if !file.CheckNotExist(fileName){
                stat,err := os.Stat(fileName)
                if err != nil{
                        return err
                }
                newSize := resp.Header.Get("Content-Length")
                if newSize == strconv.FormatInt(stat.Size(),10){
                        log.Println(fileNameSp[len(fileNameSp)-1],"已存在,跳过")
                        return nil
                }
        }
        counter := &WriteCounter{}
        counter.Name = fileNameSp[len(fileNameSp)-1]
        if _, err = io.Copy(out, io.TeeReader(resp.Body, counter)); err != nil {
                out.Close()
                return err
        }
        fmt.Print("\n")
        out.Close()
        if err = os.Rename(fileName+".tmp", fileName); err != nil {
                return err
        }
        return nil
}


如果有bug 请留言或站内信
蹲街,看美女 发表于 2021-5-31 20:14
wushengli 发表于 2021-5-31 20:48
感谢分享,这软件对有的人估计太别有用。
不负韶华 发表于 2021-5-31 21:13
无水印链接怎么分析的能发一下不
棕熊哒哒 发表于 2021-5-31 21:17
感谢楼主分享
nanaqilin 发表于 2021-5-31 21:32
go语言感觉还是挺先进的,实现一些东西很方便
 楼主| hahaxi 发表于 2021-5-31 22:00
蹲街,看美女 发表于 2021-5-31 20:14
可以批量无水印下载吗

可以  
url.txt 里每行放一个链接地址  
 楼主| hahaxi 发表于 2021-5-31 22:03
不负韶华 发表于 2021-5-31 21:13
无水印链接怎么分析的能发一下不

参考这个帖子做的
https://www.52pojie.cn/thread-1431173-1-1.html
那个帖子里讲的很详细
主要就是禁用重定向来获取headers['location'],再从中提取sec_id
然后轮询年月,获取时间戳毫秒数,通过接口获得该月视频列表
小黑啊123 发表于 2021-5-31 22:39
感谢 感谢  回去试试
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-3 09:28

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表