18978429109 发表于 2022-3-20 00:57

Golang协程并发爬取图片源码,仅供学习

本帖最后由 18978429109 于 2022-3-20 01:05 编辑

效果图





-----------------------------------------------------------------------------------------------------------------------------------------------------------------
上代码
package main

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

var (
      reImg      = `https?://[^"]+?(\.((jpg)|(png)|(jpeg)|(gif)|(bmp)))`
      taskChan   chan string                                                      // 记录任务完成的通道
      imgChan      chan string                                                      // 图片通道
      waitGroup    sync.WaitGroup                                                   // 协程会涉及到并发,这个的作用具体的可百度
      pageStart    int            = 1                                             // 从第几页开始
      pageSize   int            = 30                                              // 扫描页数
      DownLoadPath string         = "D:/HBuilderX/project/GolangProject/爬虫小案例/img/" // 此处是图片下载的地址,需要修改为你们自己的地址
)

func main() {
      fmt.Println("从第几页开始")
      fmt.Scanln(&pageStart)

      fmt.Println("扫描页数")
      fmt.Scanln(&pageSize)

      taskChan = make(chan string, pageSize) // 给管道缓冲值
      imgChan = make(chan string, 100000)    // 给管道缓冲值

      for i := pageStart; i < pageStart+pageSize; i++ {
                waitGroup.Add(1)
                go getWebBody(i) // 获取地址的body
      }

      waitGroup.Add(1)
      go handleImg() // 处理img

      waitGroup.Add(1)
      go checkOk()

      waitGroup.Wait()

      fmt.Println("执行完毕,回车退出")
      var input string
      fmt.Scanln(&input)
}

func getWebBody(i int) {
      url := "https://www.bizhizu.cn/shouji/tag-%E5%8F%AF%E7%88%B1/" + strconv.Itoa(i) + ".html"
      resp, err := http.Get(url)
      if err != nil {
                fmt.Println(err)
      }
      defer resp.Body.Close() // 此处不可少,否则会造成内存泄露或者溢出问题

      body, _ := ioutil.ReadAll(resp.Body)

      re := regexp.MustCompile(reImg) // 正则匹配图片地址

      result := re.FindAllStringSubmatch(string(body), -1)

      println("第" + strconv.Itoa(i) + "页找到数据" + strconv.Itoa(len(result)) + "条")

      for _, v := range result {
                imgChan <- v // 把得到的数据写到 管道 里
      }

      taskChan <- url

      waitGroup.Done()
}

// 处理图片方法
func handleImg() {
      for url := range imgChan {
                fileName := GetFilenameFromUrl(url)
                result := DownloadFile(url, fileName)
                if result {
                        fmt.Printf("已下载完毕 %v \n", url)
                } else {
                        fmt.Printf("下载失败 %v \n", url)
                }
      }
      waitGroup.Done()
}

// 检查管道任务是否完成
func checkOk() {
      count := 0
      for {
                url := <-taskChan
                fmt.Printf("完成爬取:%v \n", url)
                count++
                if count == pageSize {
                        close(imgChan)
                        close(taskChan)
                        break
                }
      }

      waitGroup.Done()
}

// 截取url名字
func GetFilenameFromUrl(url string) (filename string) {
      // 返回最后一个/的位置
      lastIndex := strings.LastIndex(url, "/")
      // 切出来
      filename = url
      // 时间戳解决重名
      timePrefix := strconv.Itoa(int(time.Now().UnixNano()))
      filename = timePrefix + "_" + filename
      return
}

// 下载文件方法
func DownloadFile(url string, filename string) bool {
      var err error
      resp, err := http.Get(url)
      if err != nil {
                fmt.Println(err)
      }
      defer resp.Body.Close()
      bytes, err := ioutil.ReadAll(resp.Body)
      if err != nil {
                fmt.Println(err)
      }

      if _, err := os.Stat(DownLoadPath); os.IsNotExist(err) {
                // 必须分成两步
                // 先创建文件夹
                os.Mkdir(DownLoadPath, 0777)
                // 再修改权限
                os.Chmod(DownLoadPath, 0666)
      }

      filename = DownLoadPath + filename
      err = ioutil.WriteFile(filename, bytes, 0666)
      if err != nil {
                return false
      }
      return true
}






mokleolee 发表于 2022-3-20 08:57

好东西啊。。。

15504582159 发表于 2022-3-20 09:17

谢谢分享

wws741 发表于 2022-4-25 23:18

感谢分享,学习一下
页: [1]
查看完整版本: Golang协程并发爬取图片源码,仅供学习