嘟嘟社区

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


  1. package main
  2. import (
  3.         "fmt"
  4.         "io/ioutil"
  5.         "net/http"
  6.         "os"
  7.         "regexp"
  8.         "strconv"
  9.         "strings"
  10.         "sync"
  11.         "time"
  12. )
  13. var (
  14.         reImg        = `https?://[^"]+?(.((jpg)|(png)|(jpeg)|(gif)|(bmp)))`
  15.         taskChan     chan string                                                      // 记录任务完成的通道
  16.         imgChan      chan string                                                      // 图片通道
  17.         waitGroup    sync.WaitGroup                                                   // 协程会涉及到并发,这个的作用具体的可百度
  18.         pageStart    int            = 1                                               // 从第几页开始
  19.         pageSize     int            = 30                                              // 扫描页数
  20.         DownLoadPath string         = "D:/HBuilderX/project/GolangProject/爬虫小案例/img/" // 此处是图片下载的地址,需要修改为你们自己的地址
  21. )
  22. func main() {
  23.         fmt.Println("从第几页开始")
  24.         fmt.Scanln(&pageStart)
  25.         fmt.Println("扫描页数")
  26.         fmt.Scanln(&pageSize)
  27.         taskChan = make(chan string, pageSize) // 给管道缓冲值
  28.         imgChan = make(chan string, 100000)    // 给管道缓冲值
  29.         for i := pageStart; i < pageStart+pageSize; i++ {
  30.                 waitGroup.Add(1)
  31.                 go getWebBody(i) // 获取地址的body
  32.         }
  33.         waitGroup.Add(1)
  34.         go handleImg() // 处理img
  35.         waitGroup.Add(1)
  36.         go checkOk()
  37.         waitGroup.Wait()
  38.         fmt.Println("执行完毕,回车退出")
  39.         var input string
  40.         fmt.Scanln(&input)
  41. }
  42. func getWebBody(i int) {
  43.         url := "https://www.bizhizu.cn/shouji/tag-%E5%8F%AF%E7%88%B1/" + strconv.Itoa(i) + ".html"
  44.         resp, err := http.Get(url)
  45.         if err != nil {
  46.                 fmt.Println(err)
  47.         }
  48.         defer resp.Body.Close() // 此处不可少,否则会造成内存泄露或者溢出问题
  49.         body, _ := ioutil.ReadAll(resp.Body)
  50.         re := regexp.MustCompile(reImg) // 正则匹配图片地址
  51.         result := re.FindAllStringSubmatch(string(body), -1)
  52.         println("第" + strconv.Itoa(i) + "页找到数据" + strconv.Itoa(len(result)) + "条")
  53.         for _, v := range result {
  54.                 imgChan <- v[0] // 把得到的数据写到 管道 里
  55.         }
  56.         taskChan <- url
  57.         waitGroup.Done()
  58. }
  59. // 处理图片方法
  60. func handleImg() {
  61.         for url := range imgChan {
  62.                 fileName := GetFilenameFromUrl(url)
  63.                 result := DownloadFile(url, fileName)
  64.                 if result {
  65.                         fmt.Printf("已下载完毕 %v n", url)
  66.                 } else {
  67.                         fmt.Printf("下载失败 %v n", url)
  68.                 }
  69.         }
  70.         waitGroup.Done()
  71. }
  72. // 检查管道任务是否完成
  73. func checkOk() {
  74.         count := 0
  75.         for {
  76.                 url := <-taskChan
  77.                 fmt.Printf("完成爬取:%v n", url)
  78.                 count++
  79.                 if count == pageSize {
  80.                         close(imgChan)
  81.                         close(taskChan)
  82.                         break
  83.                 }
  84.         }
  85.         waitGroup.Done()
  86. }
  87. // 截取url名字
  88. func GetFilenameFromUrl(url string) (filename string) {
  89.         // 返回最后一个/的位置
  90.         lastIndex := strings.LastIndex(url, "/")
  91.         // 切出来
  92.         filename = url[lastIndex+1:]
  93.         // 时间戳解决重名
  94.         timePrefix := strconv.Itoa(int(time.Now().UnixNano()))
  95.         filename = timePrefix + "_" + filename
  96.         return
  97. }
  98. // 下载文件方法
  99. func DownloadFile(url string, filename string) bool {
  100.         var err error
  101.         resp, err := http.Get(url)
  102.         if err != nil {
  103.                 fmt.Println(err)
  104.         }
  105.         defer resp.Body.Close()
  106.         bytes, err := ioutil.ReadAll(resp.Body)
  107.         if err != nil {
  108.                 fmt.Println(err)
  109.         }
  110.         if _, err := os.Stat(DownLoadPath); os.IsNotExist(err) {
  111.                 // 必须分成两步
  112.                 // 先创建文件夹
  113.                 os.Mkdir(DownLoadPath, 0777)
  114.                 // 再修改权限
  115.                 os.Chmod(DownLoadPath, 0666)
  116.         }
  117.         filename = DownLoadPath + filename
  118.         err = ioutil.WriteFile(filename, bytes, 0666)
  119.         if err != nil {
  120.                 return false
  121.         }
  122.         return true
  123. }

复制代码

人生苦短,我用PYTHON
支持看到了if err 舒服了