## 一、并发爬取美图
* [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)中的方法放到我们当前的代码中。
- 一、数组
- 二、切片
- 三、copy
- 四、MAP
- 五、结构体
- 六、结构体参数
- 七、面向”对象“
- 1、匿名字段
- 2、方法
- 3、包和封装
- 4、接口
- 5、异常处理
- 八、Json
- 九、文件操作
- 1、写文件
- 2、读取文件内容
- 3、拷贝文件
- 十、反射
- 1、查看类型,字段和方法
- 2、查看匿名字段
- 3、修改基本类型的值
- 4、修改结构体的值
- 5、调用方法
- 十一、并发编程
- 1、并行和并发
- 2、协程
- 3、runtime包
- 5、channel的使用
- 6、close
- 7、定时器
- 8、select
- 9、协程同步锁
- 十二、socket编程
- 十三、Http编程
- 十四、并发爬虫和数据处理
- 1、简易爬虫实例
- 2、并发爬取图片
- 3、读文件中的数据
- 4、数据清洗
- 其他
- 1、推荐文章