fafa想放假 2023-04-18 19:17 采纳率: 66.7%
浏览 30
已结题

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改代码有没有什么意见呀

  • 写回答

2条回答 默认 最新

  • 雾满拦江I 2023-04-19 16:09
    关注

    可以使用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进行同步,大大提高爬取效率。
      如果您在理解或实践中有任何问题,欢迎提出,我会继续解答。希望这些改写建议能帮助您解决视频爬取速度慢的问题。
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 4月27日
  • 已采纳回答 4月19日
  • 修改了问题 4月18日
  • 创建了问题 4月18日

悬赏问题

  • ¥20 西门子S7-Graph,S7-300,梯形图
  • ¥50 用易语言http 访问不了网页
  • ¥50 safari浏览器fetch提交数据后数据丢失问题
  • ¥15 matlab不知道怎么改,求解答!!
  • ¥15 永磁直线电机的电流环pi调不出来
  • ¥15 用stata实现聚类的代码
  • ¥15 请问paddlehub能支持移动端开发吗?在Android studio上该如何部署?
  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效
  • ¥15 悬赏!微信开发者工具报错,求帮改