本文最后由 森林生灵 于 2020/05/01 18:22:37 编辑

文章目录 (?) [+]

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "regexp"
    "strings"
)

func main() {
    // 仅对非会员视频链接有效
    url := "https://v.qq.com/x/page/b0945ug7vzo.html"
    // 先确定 .html 的最早出现的位置,以排除之后 callback 的链接中可能携带的 .html
    b := strings.Index(url, ".html")
    // 从确定的 .html 向前找最后一个 / 出现的位置
    a := strings.LastIndex(url[:b], "/")
    // 获取视频 id
    vid := url[a+1 : b]

    // 获取视频 json 信息
    url = "https://vv.video.qq.com/getinfo?vids=" + vid + "&platform=101001&charge=0&otype=json"
    resp, err := http.Get(url)
    if err != nil {
        fmt.Println(err)

        return
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)

        return
    }

    // 正则出 json
    re, err := regexp.Compile("^QZOutputJson=(.*?);$")
    if err != nil {
        fmt.Println(err)

        return
    }

    ret := re.FindSubmatch(body)
    if len(ret) < 2 {
        fmt.Println("cannot get video info")

        return
    }

    // 获取 fn 与 fvkey
    // 每次获取的 fn 固定不变 fvkey 会变
    var fn, fvkey string

    // 方法一:查找关键字
    // str := string(ret[1])
    // ftfn := "\"fn\":\""
    // ftfvkey := "\"fvkey\":\""
    //
    // a = strings.LastIndex(str, ftfn)
    // if a > 0 {
    //     b = strings.Index(str[a+len(ftfn):], "\"")
    //     if b > 0 && len(str) >= a+b {
    //         // a+ 1因为下标为0开始,-1是因为计算长度时将第一位也计算在内,与当前位置重复
    //         fn = str[a+1+len(ftfn)-1 : a+len(ftfn)+b]
    //
    //     }
    //     fmt.Println("string()", a, b, len(str), len("\"fn\":\""), fn)
    // }
    //
    // a = strings.LastIndex(str, ftfvkey)
    // if a > 0 {
    //     b = strings.Index(str[a+len(ftfvkey):], "\"")
    //     if b > 0 && len(str) >= a+b {
    //         // a+ 1因为下标为0开始,-1是因为计算长度时将第一位也计算在内,与当前位置重复
    //         fvkey = str[a+1+len(ftfvkey)-1 : a+len(ftfvkey)+b]
    //
    //     }
    //     fmt.Println("string()", a, b, len(str), len("\"fvkey\":\""), fvkey)
    // }

    // 方法二:正则匹配
    re, err = regexp.Compile(`"fn":"(.*?)"`)
    if err != nil {
        fmt.Println(err)

        return
    }

    retfn := re.FindSubmatch(ret[1])
    if len(retfn) < 2 {
        fmt.Println("cannot get the key")

        return
    }
    fn = string(retfn[1])

    re, err = regexp.Compile(`"fvkey":"(.*?)"`)
    if err != nil {
        fmt.Println(err)

        return
    }

    retfvkey := re.FindSubmatch(ret[1])
    if len(retfvkey) < 2 {
        fmt.Println("cannot get the key")

        return
    }
    fvkey = string(retfvkey[1])

    // 方法三:json 解码 emmm...
    // var info map[string]interface{}
    // dec := json.NewDecoder(bytes.NewBuffer(ret[1]))
    // err = dec.Decode(&info)
    // if err != nil {
    //     fmt.Println(err)
    // }
    //
    // if m, exist := info["vl"]; exist {
    //     if m, ok := m.(map[string]interface{}); ok {
    //         if m, exist := m["vi"]; exist {
    //             // fmt.Printf("%T\n", m)
    //             if m, ok := m.([]interface{}); ok {
    //                 if len(m) > 0 {
    //                     if m, ok := m[0].(map[string]interface{}); ok {
    //                         if m, exist := m["fn"]; exist {
    //                             if m, ok := m.(string); ok {
    //                                 fn = m
    //                             }
    //                         }
    //
    //                         if m, exist := m["fvkey"]; exist {
    //                             if m, ok := m.(string); ok {
    //                                 fvkey = m
    //                             }
    //                         }
    //                     }
    //                 }
    //             }
    //         }
    //     }
    // }

    if fn != "" && fvkey != "" {
        src := "https://ugcws.video.gtimg.com/" + fn + "?vkey=" + fvkey
        fmt.Println(src)

        return
    }

    fmt.Println("cannot get the key")
}