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
}

  

posted on 2022-01-28 10:27  kevin_yang123  阅读(51)  评论(0编辑  收藏  举报