5.6 Go语言项目实战:并发爬虫

Amiee7 · 2019-03-17 17:41:23 · 3920 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2019-03-17 17:41:23 的主题,其中的信息可能已经有所发展或是发生改变。

本篇文章IT兄弟连GO语言学院小美

给读者们分享一下

对GO语言感兴趣想要学习Golang开发技术的小伙伴就随小编来了解一下吧。

导入类库

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

定义全局数据

var (
    //存放图片链接
    chanImgUrls chan string
    //存放147个任务是否已完成
    chanTask  chan string
    waitGroup sync.WaitGroup
)

爬取一个页面上的全部图片链接,返回结果切片

func SpiderPrettyImg(url string) (urls []string) {
    pageStr := GetPageStr(url)
    //fmt.Println(pageStr)

    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
}

从url中提取文件名称

func GetFilenameFromUrl(url string, dirPath string) (filename string) {
    lastIndex := strings.LastIndex(url, "/")
    filename = url[lastIndex+1:]
    timePrefix := strconv.Itoa(int(time.Now().UnixNano()))
    filename = timePrefix + "_" + filename
    filename = dirPath + filename
    //fmt.Println(fileName)
    return
}

下载url对应的文件到指定路径

func DownloadFile(url string, filename string) (ok bool) {
    resp, err := http.Get(url)
    if err != nil {
        HandleError(err, "http.Get(url)")
        return
    }
    defer resp.Body.Close()

    //ioutil.ReadAll(resp.Body)read tcp 192.168.20.50:57178->175.6.244.4:80: wsarecv:
    // An existing connection was forcibly closed by the remote host.
    fBytes, e := ioutil.ReadAll(resp.Body)
    HandleError(e, "ioutil.ReadAll(resp.Body)")
    err = ioutil.WriteFile(filename, fBytes, 0644)
    HandleError(err, "http.Get(url)")
    if err != nil {
        return false
    } else {
        return true
    }
}

爬取一个页面下的所有图片链接,并丢入全局待下载数据管道

func SpiderImgUrls(url string) {
    //获取一个页面下的所有图片链接
    urls := SpiderPrettyImg(url)
    //将所有图片超链接丢入数据管道
    for _, url := range urls {
        chanImgUrls <- url
    }

    //通知当前协程任务完成
    chanTask <- url
    waitGroup.Done()
}

同步下载图片链接管道中的所有图片

func DownloadImg() {
    for url := range chanImgUrls {
        filename := GetFilenameFromUrl(url, "D:/BJBlockChain1801/demos/W4/day4/img/")
        ok := DownloadFile(url, filename)
        if ok {
            fmt.Printf("%s下载成功!\n", filename)
        } else {
            fmt.Printf("%s下载失败!!!!!!!!!!!!\n", filename)
        }
    }
    waitGroup.Done()
}

检查147个任务是否全部完成,完成则关闭数据管道

func CheckIfAllSpidersOk() {
    var count int
    for {
        url := <-chanTask
        fmt.Printf("%s完成爬取任务\n", url)
        count ++
        if count == 147 {
            close(chanImgUrls)
            break
        }
    }
    waitGroup.Done()
}

主程序

func main() {
    //初始化数据管道
    chanImgUrls = make(chan string, 1000000)
    chanTask = make(chan string, 147)

    //爬虫协程:源源不断地往管道中添加图片链接
    for i := 1; i < 148; i++ {
        waitGroup.Add(1)
        go SpiderImgUrls("http://www.umei.cc/tags/meinv_" + strconv.Itoa(i) + ".htm")
    }

    //开辟任务统计协程,如果147个任务全部完成,则关闭数据管道
    waitGroup.Add(1)
    go CheckIfAllSpidersOk()

    //下载协程:源源不断地从管道中读取地址并下载
    for i := 0; i < 10; i++ {
        waitGroup.Add(1)
        go DownloadImg()
    }
    waitGroup.Wait()
}

想要了解更多关于GO语言开发方面内容的小伙伴,

请关注IT兄弟连官网、公众号:GO语言研习社,

IT兄弟连教育有专业的微软、谷歌讲师为您指导,

此外IT兄弟连老师精心推出的GO语言教程定能让你快速掌握GO语言从入门到精通开发实战技能。


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3920 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传