go 协程抓取网站图片

package main

import (
        "fmt"
        "io/ioutil"
        "net/http"
        "os"
        "regexp"
        "strconv"
        "strings"
        "sync"
        "time"
)

//并发抓思路
//1.初始化数据管道
//2.爬虫写出:26个协程向管道中添加图片连接
//3.任务统计协程:检查26个任务是否都完成,完成则关闭数据管道
//4.下载协程:从管道中读取链接,并下载
var (
        //正则匹配
        reImages=`https?://[^"]+?(\.((jpg)|(png)|(jpeg)|(gif)|(bmp)))`
        //存放图片连接的数据管道
        chanImageUrls chan string
    //等待组
        waitGroup sync.WaitGroup
        //任务统计协程 用于监控协程的
        chanTask chan string
        )
func main(){
    //1.初始化管道
        chanImageUrls= make(chan string,1000000)
    chanTask =make(chan string,26)//协程数 26个
    //2.爬虫协程
    for i:=1;i<26;i++{
        waitGroup.Add(1)
        go getImgUrls("https://www.bizhizu.cn/shouji/tag-%E5%8F%AF%E7%88%B1/" + strconv.Itoa(i) + ".html")
        }
        //3.任务统计协程,统计26任务是否完成
        waitGroup.Add(1)
    go TaskOk()
    //4.下载协程;从管道中读取连接并下载
    for i:=0;i<5;i++{
        //每开一个协程都需要添加一个协程 waitGroup
        waitGroup.Add(1)
        //下载图片的协程
        go DownLoadImg()
        }
        waitGroup.Wait() //所有的协程都需要开一个等待协程
}
func DownLoadImg(){
  for url := range chanImageUrls{
       filename :=GetFilenameFromUrl(url)
       DownloadFile(url,filename)
  }
}
// 下载图片,传入的是图片叫什么
func DownloadFile(url string, filename string) (ok bool) {
        resp, err := http.Get(url)
        HandleErrors(err, "http.get.url")
        defer resp.Body.Close()
        bytes, err := ioutil.ReadAll(resp.Body)
        HandleErrors(err, "resp.body")
        runtimeDir := fmt.Sprintf("%s/imgs", "D:/go/test/pa")
        if !FileExist(runtimeDir) {
                err := os.Mkdir(runtimeDir, os.ModePerm)
                if err != nil {
                        panic(err)
                }
        }
        filename = "D:/go/test/pa/imgs/" + filename
        // 写出数据
        err = ioutil.WriteFile(filename, bytes, 0777)
        if err != nil {
                return false
        } else {
                return true
        }
}
//判断文件是否存在
func FileExist(path string) (exist bool) {
        _, err := os.Stat(path)
        if err == nil {
                exist = true
        } else if os.IsNotExist(err) {
                exist = false
        }
        return
}
//截取url 名字
func GetFilenameFromUrl(url string)(filename string){
        //返回最后一个/位置
        lastIndex :=strings.LastIndex(url,"/")
        //切出来
        filename =url[lastIndex+1:]
        //时间戳 解决重名
        timePrefix :=strconv.Itoa(int(time.Now().UnixNano()))
        filename =timePrefix+"_"+filename
        return
}
//任务统计协程
func TaskOk(){
        var count int
        for {
                url:=<-chanTask
                fmt.Printf("%s 完成了爬虫任务\n",url)
                count++
                if count==26{
                        //关闭图片数据管道
                        close(chanImageUrls)
                        break
                }
        }
        waitGroup.Done() //关闭主协程
}
//爬图片连接到管道
//url是传的整页连接
func getImgUrls(url string){
        urls :=getImgs(url)
        //遍历切片里所有链接,存入数据管道
        for _,url:=range urls{
                chanImageUrls<-url
        }
        //监控协程,标识当前协程完成 监控爬虫的26个进程
        //每完成一个任务,写一条数据
        //用于监控协程,知道已经完成了几个任务
        chanTask<-url
        //所有内容加入管道之后需要done 关闭管道
        waitGroup.Done() //控制主协程是否结束
}

//获取当前页的图片链接
func getImgs(url string)(urls []string){
        pageStr:=GetPageStrs(url)
        re:=regexp.MustCompile(reImages)
        results:=re.FindAllStringSubmatch(pageStr,-1)
        fmt.Printf("共找到%d条结果\n",len(results))
        for _,result :=range results{
                url:=result[0]
                urls =append(urls,url)
        }
        return
}
//错误处理
func HandleErrors(err error,message string){
        if err !=nil{
                fmt.Println(message,err)
        }
}

//抽取根据url获取内容
func GetPageStrs(url string)(pageStr string){
        resp,err:=http.Get(url)
        HandleErrors(err,"http err")
        defer resp.Body.Close()
        //读取页面内容
        respByte,err :=ioutil.ReadAll(resp.Body)
        HandleErrors(err,"ioutil err")
        pageStr =string(respByte)
        return pageStr
}