Go爬取美女壁纸图片

693 阅读3分钟

前言

最近正好在学习和使用Go相关的东西,想起来上一次GAN生成女朋友的图片始终不理想,很可能就是因为数据集的质量不够,所以这次想着用Go重新爬点图片玩玩。

目标网址:美女壁纸

在这里插入图片描述

开始动手

这个网页很简单,一看url最后是1,随便打开第二页发现是2,那很明显直接for loop里面改变最后的页数就可以实现翻页爬取了。

再来看看网页中的图片url 在这里插入图片描述 全部都是https?://uploadfile.bizhizu.cn\... .jpg/ 所以处理起来也很简单,直接正则匹配每一页中所有的图片url就行

理清一下爬虫的思路

  1. 循环爬取每一页
  2. 正则匹配每一页所有的图片urls,并且将urls添加到一个管道中
  3. 在传入每页图片urls添加这页的url到chanTask,然后检查的时候读取,无法读取数据时检查下载页数与处理数是否相等
  4. 从管道中读取图片的url并下载,同时检查chanTask,判断下载页数与处理数

代码

package main

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

// HandleError 错误处理
func HandleError(err error, why string) {
	if err != nil {
		fmt.Println(why, err)
	}
}

// GetHTML 抽取根据url获取内容
func GetHTML(url string) (html string) {
	resp, err := http.Get(url)
	HandleError(err, "http.Get url")
	defer resp.Body.Close()
	// 2.读取页面内容
	pageBytes, err := ioutil.ReadAll(resp.Body)
	HandleError(err, "ioutil.ReadAll")
	// 字节转字符串
	html = string(pageBytes)
	return html
}

// DownloadFile 下载图片,传入图片名称
func DownloadFile(url string, filename string) (ok bool) {
	resp, err := http.Get(url)
	//如果获取网页失败就抛出错误
	HandleError(err, "http.get.url")

	//开始读取网页数据
	defer resp.Body.Close()
	bytes, err := ioutil.ReadAll(resp.Body)
	HandleError(err, "resp.body")

	//判断是否存在images文件夹,如果不存在就创建该文件夹
	path:="./images"
	if _, err := os.Stat(path); err != nil {
		fmt.Println("path not exists ", path)
		err := os.MkdirAll(path, 0711)
		if err != nil {
			log.Println("Error creating directory")
			log.Println(err)
			return
		}
	}
	filename = "./images/" + filename
	// 写出数据
	err = ioutil.WriteFile(filename, bytes, 0666)
	if err != nil {
		return false
	} else {
		return true
	}
}

// 定义一些全局变量
var (
	// 需要爬取的页数
	pages int
	// ImageUrls 存放图片链接的数据管道
	ImageUrls chan string
	wg     sync.WaitGroup
	// 用于监控协程
	chanTask chan string
	// 定义正则匹配图片链接的规则
	reImg    = `https://uploadfile.bizhizu.cn[^"]+?(\.jpg)`
)


// 获取当前页图片链接
func getUrls(url string) (urls []string) {
	pageStr := GetHTML(url)
	// 正则匹配网页所有的图片urls
	re := regexp.MustCompile(reImg)
	results := re.FindAllStringSubmatch(pageStr, -1)
	fmt.Printf("共找到%d条结果\n", len(results))

	// 由于re.FindAllStringSubmatch的特性,返回的不是纯粹的urls,所以重新提取赋值
	for _, result := range results {
		url := result[0]
		urls = append(urls, url)
	}
	return urls
}


// 爬图片链接到管道
// url是传的整页链接
func getImgUrls(url string) {
	urls := getUrls(url)
	// 遍历切片里所有链接,存入数据管道
	for _, url := range urls {
		ImageUrls <- url
	}
	// 标识当前协程完成
	// 每完成一个任务,写一条数据
	// 用于监控协程知道已经完成了几个任务
	chanTask <- url
	wg.Done()
}


// GetFilenameFromUrl 截取url名字
func GetFilenameFromUrl(url string) (filename string) {
	// 返回最后一个/的位置
	lastIndex := strings.LastIndex(url, "/")
	// 切出来
	filename = url[lastIndex+1:]
	return filename
}



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



// CheckOK 任务统计协程
// 在传入每页图片urls添加这页的url到chanTask,然后检查的时候读取,无法读取数据时检查页数与处理数是否相等
func CheckOK() {
	var count int
	for {
		url := <-chanTask
		fmt.Printf("%s 完成了爬取任务\n", url)
		count++
		if count == pages {
			close(ImageUrls)
			break
		}
	}
	wg.Done()
}




func main() {
	fmt.Println("请输入需要爬取的页数:")
	fmt.Scanf("%d",&pages)
	// 初始化管道
	ImageUrls = make(chan string, 1000000)
	// 根据爬取的页数设置协程任务数
	chanTask = make(chan string, pages)
	// 协程循环爬取网页
	for i := 1; i < pages+1; i++ {
		wg.Add(1)
		go getImgUrls("https://www.bizhizu.cn/shouji/tag-%E7%BE%8E%E5%A5%B3/" + strconv.Itoa(i) + ".html")
	}
	// 任务统计协程,统计任务是否都完成,完成则关闭管道
	wg.Add(1)
	go CheckOK()
	// 下载协程:从管道中读取链接并下载
	for i := 0; i < 7; i++ {
		wg.Add(1)
		go DownloadImg()
	}
	wg.Wait()
}


在这里插入图片描述 在这里插入图片描述