go colly如何用协程爬取视频

我用go colly仿照一个python的视频爬取教程,写了一个go的视频爬取程序,然后稍稍改造了一下,用来爬取某站的合集视频,但是爬取速度不太让人满意,一次只能爬取一个视频,大家能不能 用goroutine改写一下 ,用多个协程,增加爬取速度?

我的go语言入门还不深,我试过给主函数加协程,但是每次都还是只能爬取两个链接,不知道为什么,也试过用协程来同时爬取音频视频,但是我的方法也不对

改一下对应位置应该能用的

package main

import (
    "bookCrawler/collyTry/06test/crawler"
    "fmt"
    "strconv"
)

func main() {
    url := "https://www.bilibili.com/video/把这串中文替换为合集视频的BV号?p="
    for i := 1; i <= 合集数量; i++ { // 用于循环爬取具有合集的某站视频
        fmt.Println("正在处理第", i, "个文件")
        fmt.Println(crawler.Response(url + strconv.Itoa(i)))
    }
}
package crawler

import (
    "encoding/json"
    "fmt"
    "github.com/gocolly/colly/v2"
    "os"
    "os/exec"
    "regexp"
)

// Response response 访问解析链接
func Response(url string) string {
    c := colly.NewCollector(
        colly.UserAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60"))
    re := regexp.MustCompile(`window.__playinfo__=(.*?)}`)
    var title string

    c.OnHTML("title", func(e *colly.HTMLElement) {
        title = e.Text[:len(e.Text)-22]
        fmt.Println(title)
    })

    c.OnHTML("script", func(e *colly.HTMLElement) {
        match := re.FindStringSubmatch(e.Text) // 用正则表达式匹配palyinfo
        if len(match) > 1 {
            //fmt.Println(e.Text)
            fmt.Println()
            var data map[string]interface{}
            q := []byte(e.Text)
            q = q[20:] // 删去不需要的内容来转化为json格式
            err := json.Unmarshal(q, &data)
            if err != nil {
                fmt.Println("发现错误:", err)
            }
            // 分别提取音频和视频链接
            audioUrl := data["data"].(map[string]interface{})["dash"].(map[string]interface{})["audio"].([]interface{})[0].(map[string]interface{})["baseUrl"]
            videoUrl := data["data"].(map[string]interface{})["dash"].(map[string]interface{})["video"].([]interface{})[0].(map[string]interface{})["baseUrl"]
            // 下载文件
            download(videoUrl.(string), audioUrl.(string), title, c)
        }
    })
    if err := c.Visit(url); err != nil {
        fmt.Println("cVisit访问页面链接失败:", err)
    }
    c.Wait()
    return url + "finished"
}

// download 下载文件
func download(videoUrl string, audioUrl string, title string, c *colly.Collector) {
    d := c.Clone()
    // 防盗链
    d.OnRequest(func(r *colly.Request) {
        r.Headers.Set("Referer", "https://www.bilibili.com")
    }) // On scraped response
    d.OnScraped(func(e *colly.Response) {
        fmt.Println("Downloading:", e.Request.URL.String())
        if e.Request.URL.String() == audioUrl {
            if err := e.Save(title + ".mp3"); err != nil {
                fmt.Println("mp3保存失败:", err)
            }
        } else {
            if err := e.Save(title + ".mp4"); err != nil {
                fmt.Println("mp4保存失败:", err)
            }
        }
    })
    if err := d.Visit(videoUrl); err != nil {
        fmt.Println("访问视频失败:", err)
    }
    if err := d.Visit(audioUrl); err != nil {
        fmt.Println("访问音频失败:", err)
    }
    d.Wait()
    merge(title)
}

// merge 合并文件
func merge(title string) {
    mp3File, mp4File := title+".mp3", title+".mp4"
    outputFile := "video/" + title + "_.mp4"
    cmd := exec.Command("ffmpeg", "-i", mp4File, "-i", mp3File, "-c:v", "copy", "-c:a", "aac", "-strict", "experimental", outputFile)
    fmt.Println("ffmpeg程序开始运行")
    err := cmd.Run()
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    if err := os.Remove(mp3File); err != nil {
        fmt.Println("删除mp3失败:", err)
    }
    if err := os.Remove(mp4File); err != nil {
        fmt.Println("删除mp4失败:", err)
    }
    fmt.Println(title, " Success")
}

另外,大家对用chatgpt改代码有没有什么意见呀

可以使用goroutine和channel来改写这个程序,提高爬取速度。具体可以这样做:

  1. 定义一个channel,用于协调不同goroutine间的工作,比如:
    go
    ch := make(chan string, 10) // 容量为10的channel
  2. 在主函数中启动多个goroutine去爬取视频,并通过channel发送爬取完成的链接:
    go
    for i := 0; i < 10; i++ { // 启动10个goroutine
    go func(i int) {
     url := "<https://www.bilibili.com/video/BV号?p=" + strconv.Itoa(i)
     ch <- crawler.Response(url)  // 发送完成url到channel
    
    }(i)
    }
  3. 在主函数中通过遍历channel获取各个goroutine发送过来的完成链接,并进行后续处理:
    go
    for i := 0; i < 10; i++ { // 从channel接收10个链接
    url := <-ch
    fmt.Println(url + " finished")
    }
  4. 在crawler.Response函数中使用协程安全的Collector,以免多个goroutine同时访问:
    go
    c := colly.NewCollector(
    // 其他配置...
    colly.Async(true), // 协程安全模式
    )
  5. 在download函数中也采用协程安全的d.Visit访问链接,否则会报错。
    这么改写后,程序可以同时启动多个goroutine爬取视频,并通过channel进行同步,大大提高爬取效率。
    如果您在理解或实践中有任何问题,欢迎提出,我会继续解答。希望这些改写建议能帮助您解决视频爬取速度慢的问题。
不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 以下回答来自chatgpt: 首先,你可以使用goroutine来实现多个协程同时爬取网页并提高爬取速度。下面是一个简单的示例代码: ``` package main import ( "fmt" "github.com/gocolly/colly" ) func main() { urls := []string{"url1", "url2", "url3", "url4", "url5", "url6"} // 网页链接列表 results := make(chan string) // 创建通道 for _, url := range urls { go func(url string) { // 启动goroutine c := colly.NewCollector() // 访问链接,处理响应的方法 c.OnRequest(func(r *colly.Request) { fmt.Println("Visiting", r.URL.String()) }) c.OnResponse(func(r *colly.Response) { results <- string(r.Body) // 将响应内容发送到通道中 }) // 实际爬取网页 c.Visit(url) }(url) } for i := 0; i < len(urls); i++ { fmt.Println(<-results) // 从通道中读取响应内容并打印 } } ``` 上述代码将给定的网页链接列表(urls)进行了遍历,对每个链接都启动了一个goroutine,使用colly库爬取网页并将响应内容发送到`results`通道中。最后,从通道中读取响应内容并打印。 注意,如果你要爬取的网页数量很多,那么这个方式可能会导致你的机器负载过高,这时候需要限制同时启动的goroutine数量。你可以使用类似于线程池的技术,先创建一批goroutine,然后将待爬取的网页链接放入一个队列中,每个goroutine从队列中取出一个链接进行爬取,并在完成后再取出下一个链接进行爬取。这种方式可以让你控制goroutine的数量,避免机器负载过高的情况。 关于使用chatgpt实现改造的想法,我建议你在完成爬虫部分后,先尝试一下chatgpt的单独使用,以确保你对该模型具有足够的了解。然后,你可以根据实际情况考虑在爬虫程序中集成chatgpt模型来实现更高级的功能,例如自动回复某些评论或问题等。不过,在此之前,你需要学习一些基础的自然语言处理知识,以便更好地理解chatgpt模型的使用。

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^