首页 文章 个人博客 基于golang实现的并发爬虫,爬取图片

基于golang实现的并发爬虫,爬取图片

发布时间:2021-04-5编辑:RainNight阅读(38)

基于golang实现的并发爬虫,爬取图片


代码篇


package main

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

// 并发爬思路:
// 1.初始化数据管道
// 2.爬虫写出:26个协程向管道中添加图片链接
// 3.任务统计协程:检查26个任务是否都完成,完成则关闭数据管道
// 4.下载协程:从管道里读取链接并下载

var (
	// 存放图片链接的数据管道
	chanImageUrls chan string
	waitGroup     sync.WaitGroup
	// 用于监控协程
	chanTask chan string
	reImg    = `https?://[^"]+?(\.((jpg)|(png)|(jpeg)|(gif)|(bmp)))`
)

func main() {

	//DownloadFile("http://i1.shaodiyejin.com/uploads/tu/201909/10242/e5794daf58_4.jpg", "1.jpg")

	// 1.初始化管道
	chanImageUrls = make(chan string,1000000)
	chanTask = make(chan string, 26)
	// 2.爬虫协程
	for i := 1;i < 3; 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 checkOK()
	// 4.下载协程:从管道中读取链接并下载
	for i := 0; i < 3; i++ {
		waitGroup.Add(1)
		go DownloadImg()
	}
	waitGroup.Wait()
}

// 下载图片,传入的是图片叫什么
func DownloadFile(url string, filename string) (ok bool) {
	resp, _ := http.Get(url)
	defer resp.Body.Close()
	bytes, err := ioutil.ReadAll(resp.Body)
	filename = "/Users/shiyuxiang/develop/goweb/Crawler/" + filename
	// 写数据
	err = ioutil.WriteFile(filename, bytes, 0666)
	if err != nil {
		return false
	} else {
		return true
	}
}

// 下载图片
func DownloadImg()  {
	for url := range chanImageUrls {
		filename := GetFilenameFromUrl(url)
		ok := DownloadFile(url,filename)
		if ok {
			fmt.Printf("%s 下载成功\n", filename)
		}else {
			fmt.Printf("%s 下载失败\n", filename)
		}
	}
	waitGroup.Done()
}

// 截取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 checkOK()  {
	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)  {

	fmt.Println(url)

	urls := getImgs(url)
	// 遍历切片里所有链接,存入数据管道
	for _,url := range urls {
		chanImageUrls <- url
	}
	// 标识当前协程完成
	// 每完成一个任务,写一条数据
	// 用于监控协程知道已经完成了几个任务
	chanTask <- url
	waitGroup.Done()

}

// 获取当前页图片链接
func getImgs(url string) (urls []string)  {
	pageStr := GetPageStr(url)
	re := regexp.MustCompile(reImg)
	results := re.FindAllStringSubmatch(pageStr,-1)
	fmt.Printf("共找到%d条结果\n", len(results))
	for _, result := range results {
		url := result[0]
		urls = append(urls, url)
	}
	return
}

// 抽取根据url获取内容
func GetPageStr(url string) (pageStr string)  {
	resp,_ := http.Get(url)

	defer resp.Body.Close()

	// 2.读取页面内容
	pageBytes, _ := ioutil.ReadAll(resp.Body)

	// 字节转字符串
	pageStr = string(pageBytes)
	return pageStr
}

结果:


共找到30条结果
https://www.bizhizu.cn/shouji/tag-%E5%8F%AF%E7%88%B1/2.html 完成了爬取任务
共找到30条结果
https://www.bizhizu.cn/shouji/tag-%E5%8F%AF%E7%88%B1/1.html 完成了爬取任务
1617623572937548000_eb2323dec01f0815cf654528cb8b1d11.jpg 下载成功
1617623572937561000_ecb45559bda357aea824fde70cdde995.jpg 下载成功
1617623572937535000_a9f826667ec8ad28d8a20c0d5265c9c6.jpg 下载成功
1617623573951542000_8f699a344db3426080a24772a1707813.jpg 下载成功
1617623573687566000_4f58f2517f268f71c5937bc432d71902.jpg 下载成功
1617623579431880000_1c4669fad1469108c70c438c25e39988.jpg 下载成功

书籍归档