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
}