多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## 一、并发爬取美图 * [https://www.umeitu.com/weimeitupian/](https://www.umeitu.com/weimeitupian/) * 并发爬虫的设想: 初始化数据通道 爬虫协程:66个协程向管道中添加图片链接 任务统计协程:检查66个任务是否都完成了,完成就关闭数据通道 下载协程:从管道中读取并下载 ## 二、下载单个图片 ~~~ package main import ( "fmt" "io/ioutil" "net/http" ) func main() { //myTest() TestDownloadImg() } //测试页面获取数据 func myTest() { //1.获取页面所有内容 pageStr := GetPageStr("https://www.umeitu.com/weimeitupian/") fmt.Println(pageStr) //2.获取图片链接 GetImg("https://www.umeitu.com/weimeitupian/") } //测试图片下载 func TestDownloadImg() { // 我们这里是单个图片下载,第一个参数是图片的链接,第二个参数是下载后文件的名字。 //这里的第一个参数是我们通过 myTest 方法获取的 ok := DownloadFile("http://kr.shanghai-jiuxin.com/file/2022/0314/aaaf761553e5ac4a63acece4b328665e.jpg", "a1.jpg") if ok { fmt.Println("下载成功!") } else { fmt.Println("下载失败!") } } //下载文件 func DownloadFile(url string, filename string) (ok bool) { //发送请求 resp, err := http.Get(url) if err != nil { HandleError(err, "http.Get") return } //关闭资源 defer resp.Body.Close() //读到数据 fBytes, e := ioutil.ReadAll(resp.Body) HandleError(e, "ioutil") err = ioutil.WriteFile("G:\\pachong\\img\\"+filename, fBytes, 644) HandleError(err, "write") if err != nil { return false } else { return true } } ~~~ 注意:如果这里报错,那么是你缺少里面的几个方法,方法在 [《简易爬虫实例》](https://www.kancloud.cn/yuankejishu/golang002/2676963) 中,你可以把[ 《简易爬虫实例》](https://www.kancloud.cn/yuankejishu/golang002/2676963)和上面的代码放到同一项目的根目录下,然后整包编译即可,这里需要我们把[ 《简易爬虫实例》 ](https://www.kancloud.cn/yuankejishu/golang002/2676963)中的main方法注释掉。或者把[ 《简易爬虫实例》](https://www.kancloud.cn/yuankejishu/golang002/2676963)中的方法放到我们当前的代码中,如下代码: ~~~ package main import ( "fmt" "io/ioutil" "net/http" "regexp" ) func main() { //myTest() TestDownloadImg() } var ( reQQEmail = `(\d+)@qq.com` reEmail = `\w+@\w+\.\w+(\.\w+)?` reLink = `href="(https?://[\s\S]+?)"` rePhone = `1[3456789]\d\s?\d{4}\s?\d{4}` //410222198611270512 reIdcard = `[123456]\d{5}((19\d{2})|(20[01]\d))((0[1-9])|(1[012]))((0[1-9])|([12]\d)|(3[01]))\d{3}[\dXx]` reImg = `(https?://[^"]+?(\.((jpg)|(png)|(jpeg)|(gif))))` ) //测试页面获取数据 func myTest() { //1.获取页面所有内容 pageStr := GetPageStr("https://www.umeitu.com/weimeitupian/") fmt.Println(pageStr) //2.获取图片链接 GetImg("https://www.umeitu.com/weimeitupian/") } //测试图片下载 func TestDownloadImg() { // 我们这里是单个图片下载,第一个参数是图片的链接,第二个参数是下载后文件的名字。 //这里的第一个参数是我们通过 myTest 方法获取的 ok := DownloadFile("http://kr.shanghai-jiuxin.com/file/2022/0314/aaaf761553e5ac4a63acece4b328665e.jpg", "a1.jpg") if ok { fmt.Println("下载成功!") } else { fmt.Println("下载失败!") } } //下载文件 func DownloadFile(url string, filename string) (ok bool) { //发送请求 resp, err := http.Get(url) if err != nil { HandleError(err, "http.Get") return } //关闭资源 defer resp.Body.Close() //读到数据 fBytes, e := ioutil.ReadAll(resp.Body) HandleError(e, "ioutil") err = ioutil.WriteFile("G:\\pachong\\img\\"+filename, fBytes, 644) HandleError(err, "write") if err != nil { return false } else { return true } } //爬图片链接 func GetImg(url string) { //获取页面数据 pageStr := GetPageStr(url) fmt.Println(pageStr) re := regexp.MustCompile(reImg) ret := re.FindAllStringSubmatch(pageStr, -1) fmt.Printf("共找到%d条结果:\n", len(ret)) for _, result := range ret { fmt.Println(result) } } //爬页面所有数据 func GetPageStr(url string) (pageStr string) { resp, err := http.Get(url) HandleError(err, "http.Get url") defer resp.Body.Close() //接收当前页面的数据 pageBytes, err := ioutil.ReadAll(resp.Body) HandleError(err, "ioutil.Read") pageStr = string(pageBytes) return pageStr } //处理异常 func HandleError(err error, why string) { if err != nil { fmt.Println(why, err) } } ~~~ ## 三、并发爬取图片 ~~~ package main import ( "fmt" "io/ioutil" "net/http" "regexp" "strconv" "strings" "sync" "time" ) //测试页面获取数据 func myTest() { //1.获取页面所有内容 pageStr := GetPageStr("https://www.umeitu.com/bizhitupian/meinvbizhi/index_2.htm") fmt.Println(pageStr) //2.获取图片链接 GetImg("https://www.umeitu.com/bizhitupian/meinvbizhi/index_2.htm") } //测试图片下载 func TestDownloadImg() { ok := DownloadFile("https://uploadfile.bizhizu.cn/up/80/01/bd/8001bdf93c44582292cdf98976f994be.jpg", "班长4.jpg") if ok { fmt.Println("下载成功!") } else { fmt.Println("下载失败!") } } //下载文件 func DownloadFile(url string, filename string) (ok bool) { //发送请求 resp, err := http.Get(url) if err != nil { HandleError(err, "http.Get") return } //关闭资源 defer resp.Body.Close() //读到数据 fBytes, e := ioutil.ReadAll(resp.Body) HandleError(e, "ioutil") err = ioutil.WriteFile("G:\\pachong\\img\\"+filename, fBytes, 644) HandleError(err, "write") if err != nil { return false } else { return true } } var ( //存放图片链接的数据通道 chanImageUrls chan string chanTask chan string waitGroup sync.WaitGroup ) func main() { //myTest() //TestDownloadImg() //1.初始化数据通道 chanImageUrls = make(chan string, 1000000) chanTask = make(chan string, 16) //2.爬虫协程:16个协程,向管道添加图片链接 for i := 1; i < 17; i++ { waitGroup.Add(1) //16个子协程,爬16页链接 go getImgUrls("https://www.umeitu.com/bizhitupian/meinvbizhi/index_" + strconv.Itoa(i) + ".htm") } //3.任务统计协程:检查所有子协程是否都完成,完成则关闭数据通道 waitGroup.Add(1) go CheckOk() //4.下载协程,别开太多 for i := 0; i < 5; i++ { waitGroup.Add(1) //下载图片 go DownloadImg() } waitGroup.Wait() } //爬当前页所有图片链接 //将爬到的链接,添加到数据管道 func getImgUrls(url string) { //取到当前页所有图片链接 urls := getImgs(url) //将每一个图片链接,添加到管道 for _, url := range urls { //chanImageUrls数据管道,存图片链接,取图片链接下载图片 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[1] fmt.Println(url) urls = append(urls, url) } return } //检查16个任务是否完成 func CheckOk() { //计数 var count int for { url := <-chanTask fmt.Printf("%s 完成爬取任务\n", url) count++ if count == 16 { //关闭管道,跳出循环 close(chanImageUrls) break } } waitGroup.Done() } //下载图片 func DownloadImg() { //遍历图片链接的管道 for url := range chanImageUrls { //根据url获取文件名 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) { //API截取 最后一个/的下标 lastIndex := strings.LastIndex(url, "/") filename = url[lastIndex+1:] //创建时间前缀,防止重名 timePrefix := strconv.Itoa(int(time.Now().UnixNano())) //拼接 filename = timePrefix + "_" + filename return } ~~~ 注意:如果这里报错,那么是你缺少里面的几个方法,方法在 [《简易爬虫实例》 ](https://www.kancloud.cn/yuankejishu/golang002/2676963)中,你可以把 [《简易爬虫实例》](https://www.kancloud.cn/yuankejishu/golang002/2676963)和上面的代码放到同一项目的根目录下,然后整包编译即可,这里需要我们把[ 《简易爬虫实例》 ](https://www.kancloud.cn/yuankejishu/golang002/2676963)中的main方法注释掉。或者把[ 《简易爬虫实例》](https://www.kancloud.cn/yuankejishu/golang002/2676963)中的方法放到我们当前的代码中。