## 一、并發爬取美圖
* [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
}
}
~~~
注意:如果這里報錯,那么是你缺少里面的幾個方法,方法在 [《簡易爬蟲實例》](http://www.hmoore.net/yuankejishu/golang002/2676963) 中,你可以把[ 《簡易爬蟲實例》](http://www.hmoore.net/yuankejishu/golang002/2676963)和上面的代碼放到同一項目的根目錄下,然后整包編譯即可,這里需要我們把[ 《簡易爬蟲實例》 ](http://www.hmoore.net/yuankejishu/golang002/2676963)中的main方法注釋掉。或者把[ 《簡易爬蟲實例》](http://www.hmoore.net/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
}
~~~
注意:如果這里報錯,那么是你缺少里面的幾個方法,方法在 [《簡易爬蟲實例》 ](http://www.hmoore.net/yuankejishu/golang002/2676963)中,你可以把 [《簡易爬蟲實例》](http://www.hmoore.net/yuankejishu/golang002/2676963)和上面的代碼放到同一項目的根目錄下,然后整包編譯即可,這里需要我們把[ 《簡易爬蟲實例》 ](http://www.hmoore.net/yuankejishu/golang002/2676963)中的main方法注釋掉。或者把[ 《簡易爬蟲實例》](http://www.hmoore.net/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、推薦文章