加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Golang系列文章:并发抓取网页内容

发布时间:2020-12-16 09:33:59 所属栏目:大数据 来源:网络整理
导读:在上一篇中,我们根据命令行的URL参数输入,抓取对应的网页内容并保存到本地磁盘,今天来记录一下如何利用并发,来抓取多个站点的网页内容。 首先,我们在上一次代码的基础上稍作改造,使它能够获取多个站点的内容。下面代码中,我们首先定义好三个URL,然后

在上一篇中,我们根据命令行的URL参数输入,抓取对应的网页内容并保存到本地磁盘,今天来记录一下如何利用并发,来抓取多个站点的网页内容。

首先,我们在上一次代码的基础上稍作改造,使它能够获取多个站点的内容。下面代码中,我们首先定义好三个URL,然后逐个发送网络请求,获取数据并保存,最后统计消耗的总时间:

// fetch.go

package main

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

// 创建正则常量
var RE = regexp.MustCompile("w+.w+$")

func main() {
    urls := []string {
        "http://www.qq.com","http://www.163.com","http://www.sina.com",}

    // 开始时间
    start := time.Now()

    for _,url := range urls {
        start := time.Now()

        // 发送网络请求
        res,err := http.Get(url)

        if err != nil {
            fmt.Fprintf(os.Stderr,"fetch: %vn",err)
            os.Exit(1)
        }

        // 读取资源数据
        body,err := ioutil.ReadAll(res.Body)

        // 关闭资源
        res.Body.Close()

        if err != nil {
            fmt.Fprintf(os.Stderr,"fetch: reading %s: %vn",url,err)
            os.Exit(1)
        }

        fileName := getFileName(url)

        // 写入文件
        ioutil.WriteFile(fileName,body,0644)

        // 消耗的时间
        elapsed := time.Since(start).Seconds()

        fmt.Printf("%.2fs %sn",elapsed,fileName)
    }

    // 消耗的时间
    elapsed := time.Since(start).Seconds()

    fmt.Printf("%.2fs elapsedn",elapsed)
}

// 获取文件名
func getFileName(url string) string {
    // 从URL中匹配域名后面部分
    return RE.FindString(url) + ".txt"
}

在上面代码中,我们使用正则表达式来从URL中匹配域名后面部分,作为最终的文件名。关于正则表达式,后续会做总结。

下面来看看程序运行后的控制台信息:

$ ./fetch
0.12s qq.com.txt
0.20s 163.com.txt
0.27s sina.com.txt
0.59s elapsed

从打印信息中可以看出,最后消耗的总时间等于三次执行的总和。这种方式效率低下,并且不能充分利用计算机资源,下面我们就对程序进行改造,使其能够并发地执行三个抓取操作:

// fetch.go

package main

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

// 创建正则
var RE = regexp.MustCompile("w+.w+$")

func main() {
    urls := []string {
        "http://www.qq.com",}

    // 创建channel
    ch := make(chan string)

    // 开始时间
    start := time.Now()

    for _,url := range urls {
        // 开启一个goroutine
        go fetch(url,ch)
    }

    for range urls {
        // 打印channel中的信息
        fmt.Println(<-ch)
    }

    // 总消耗的时间
    elapsed := time.Since(start).Seconds()

    fmt.Printf("%.2fs elapsedn",elapsed)
}

// 根据URL获取资源内容
func fetch(url string,ch chan<- string) {
    start := time.Now()

    // 发送网络请求
    res,err := http.Get(url)

    if err != nil {
        // 输出异常信息
        ch <- fmt.Sprint(err)
        os.Exit(1)
    }

    // 读取资源数据
    body,err := ioutil.ReadAll(res.Body)

    // 关闭资源
    res.Body.Close()

    if err != nil {
        // 输出异常信息
        ch <- fmt.Sprintf("while reading %s: %v",err)
        os.Exit(1)
    }

    // 写入文件
    ioutil.WriteFile(getFileName(url),0644)

    // 消耗的时间
    elapsed := time.Since(start).Seconds()

    // 输出单个URL消耗的时间
    ch <- fmt.Sprintf("%.2fs %s",url)
}

// 获取文件名
func getFileName(url string) string {
    // 从URL中匹配域名部分
    return RE.FindString(url) + ".txt"
}

上面代码中,我们先创建一个channel,然后对每个抓取操作开启一个goruntine,待抓取程序完成后,通过channel发送消息告知主线程,主线程再做相应的处理操作。关于这部分的原理细节,后续再做总结。

我们运行上面的程序,执行结果如下:

$ ./fetch
0.10s http://www.qq.com
0.19s http://www.163.com
0.29s http://www.sina.com
0.29s elapsed

从结果中可以看出,最后消耗的总时间与耗时最长的那个操作等同,可见并发在性能方面带来的提升是非常可观的。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读