caiguanhao / dylive

Utilities to watch Douyin live streams. 观看抖音直播工具
MIT License
57 stars 14 forks source link

关于显示直播间数量 #2

Open bommbo opened 2 years ago

bommbo commented 2 years ago

求问,这个项目是只能显示各个分类中排名靠前的直播吗,其余的怎么显示?

caiguanhao commented 2 years ago

目前请求各分类前 15 个直播是不需要签名,而且不会触发滑动验证;因为抖音的签名方法很复杂,所以我想只能通过 chromedp 访问特定页面,移动页面或者执行 JavaScript,通过截取 JSON 返回的方法获取页面余下内容。但 chromedp 也不是万能,之前我试过 Tiktok 能识别 chromedp 但不能识别 puppeteer。迟点会考虑加入用 chromedp 的命令。下面是 demo:

package main

import (
    "context"
    "errors"
    "fmt"
    "log"
    "net/url"
    "strings"
    "time"

    "github.com/chromedp/cdproto/cdp"
    "github.com/chromedp/cdproto/network"
    "github.com/chromedp/chromedp"
    "github.com/chromedp/chromedp/device"
)

func main() {
    data, _ := getResponse()
    fmt.Println(string(data))
}

func newContext() (context.Context, context.CancelFunc) {
    ctx := context.Background()
    // options := append(
    //  chromedp.DefaultExecAllocatorOptions[:],
    //  chromedp.Flag("headless", false),
    // )
    // ctx, _ = chromedp.NewExecAllocator(ctx, options...)
    return chromedp.NewContext(ctx)
}

func getResponse() ([]byte, error) {
    verbosive := true

    ctx, cancel := newContext()
    defer cancel()

    pageUrl := (&url.URL{
        Scheme: "https",
        Host:   "live.douyin.com",
        Path:   "/",
    }).String()
    if verbosive {
        log.Println("visiting", pageUrl)
    }

    chanResponse := make(chan []byte)
    chanError := make(chan error)

    chromedp.ListenTarget(ctx, func(v interface{}) {
        ev, ok := v.(*network.EventResponseReceived)
        if !ok {
            return
        }
        if ev.Type != network.ResourceTypeFetch ||
            !strings.Contains(ev.Response.URL, "/webcast/web/roadmap/") {
            return
        }
        if verbosive {
            log.Println("getting", ev.Response.URL)
        }
        go func() {
            c := chromedp.FromContext(ctx)
            cc := cdp.WithExecutor(ctx, c.Target)
            rbp := network.GetResponseBody(ev.RequestID)
            body, err := rbp.Do(cc)
            if err != nil {
                chanError <- err
                return
            }
            chanResponse <- body
        }()
    })
    go func() {
        err := chromedp.Run(
            ctx,
            network.Enable(),
            chromedp.Emulate(device.IPhoneX),
            chromedp.Navigate(pageUrl),
        )
        if err != nil {
            chanError <- err
        }
    }()

    timeout := time.After(30 * time.Second)

    select {
    case response := <-chanResponse:
        chromedp.Cancel(ctx)
        return response, nil
    case err := <-chanError:
        return nil, err
    case <-timeout:
        return nil, errors.New("timed out")
    }
}