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 }