Go语言实现多协程并发下载网页内容的完整代码

 更新时间:2025年08月04日 08:27:30   作者:程序员爱钓鱼  
在互联网项目中,我们常需要批量获取多个网页的内容,如果逐个请求(串行),效率将非常低下,Go天生支持高并发,所以本文实战演示如何使用Goroutine和Channel,实现多协程并发抓取网页内容,提升网络请求效率,为构建爬虫、内容聚合器、API 批量采集器打下基础

一、实战背景

在互联网项目中,我们常需要批量获取多个网页的内容,例如:

  • 爬虫程序抓取网页 HTML
  • 数据聚合服务请求多个 API
  • 批量检测多个 URL 的可用性

如果逐个请求(串行),效率将非常低下。Go 天生支持高并发,我们可以用 Goroutine 实现 多协程并发下载网页内容,显著提高吞吐能力。

二、实战目标

我们将构建一个小型并发网页下载器,具备以下能力:

  1. 输入一组网址列表
  2. 使用 Goroutine 并发请求多个网页
  3. 使用 Channel 收集下载结果
  4. 打印成功/失败状态与网页内容摘要
  5. 支持 WaitGroup 等待所有任务完成

三、完整代码实现

package main

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

type Result struct {
    URL    string
    Status string
    Length int
    Error  error
}

// 下载网页内容并写入结果通道
func fetchURL(url string, wg *sync.WaitGroup, resultCh chan<- Result) {
    defer wg.Done()

    client := http.Client{
        Timeout: 5 * time.Second,
    }

    resp, err := client.Get(url)
    if err != nil {
        resultCh <- Result{URL: url, Status: "请求失败", Error: err}
        return
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        resultCh <- Result{URL: url, Status: "读取失败", Error: err}
        return
    }

    resultCh <- Result{
        URL:    url,
        Status: resp.Status,
        Length: len(body),
    }
}

func main() {
    urls := []string{
        "https://example.com",
        "https://httpbin.org/get",
        "https://golang.org",
        "https://nonexistent.example.com", // 故意的错误URL
    }

    var wg sync.WaitGroup
    resultCh := make(chan Result, len(urls))

    // 启动多个下载协程
    for _, url := range urls {
        wg.Add(1)
        go fetchURL(url, &wg, resultCh)
    }

    // 等待所有任务完成后关闭通道
    go func() {
        wg.Wait()
        close(resultCh)
    }()

    // 读取结果
    for res := range resultCh {
        if res.Error != nil {
            fmt.Printf("[失败] %s:%v\n", res.URL, res.Error)
        } else {
            snippet := fmt.Sprintf("%d 字节", res.Length)
            if res.Length > 0 {
                snippet = fmt.Sprintf("%s 内容预览:%s", snippet, strings.TrimSpace(string([]byte(res.URL)[:min(50, res.Length)])))
            }
            fmt.Printf("[成功] %s:%s\n", res.URL, snippet)
        }
    }

    fmt.Println("所有网页请求已完成。")
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

四、输出示例

[成功] https://example.com:1256 字节 内容预览:https://example.com
[成功] https://httpbin.org/get:349 字节 内容预览:https://httpbin.org/get
[成功] https://golang.org:3578 字节 内容预览:https://golang.org
[失败] https://nonexistent.example.com:Get "https://nonexistent.example.com": dial tcp: ...
所有网页请求已完成。

五、重点知识点讲解

1. 使用 Goroutine 启动并发请求

go fetchURL(url, &wg, resultCh)

每个网页请求都是一个轻量级的线程(协程),同时运行,最大化资源利用。

2. 使用 sync.WaitGroup 等待所有任务完成

WaitGroup 是 Goroutine 的最佳搭档,确保主线程不会提前退出。

wg.Add(1)
defer wg.Done()

3. 使用带缓冲的 Channel 收集结果

resultCh := make(chan Result, len(urls))

避免协程阻塞,收集所有结果后统一处理。

4. 设置请求超时

使用 http.Client{ Timeout: ... } 可防止因某个 URL 卡住导致整体阻塞。

5. 防止通道未关闭阻塞

一定要在所有任务完成后关闭结果通道:

go func() {
    wg.Wait()
    close(resultCh)
}()

六、可扩展方向

这个简单的并发网页下载器可以继续扩展为:

功能方向实现建议
限制最大并发数使用带缓冲的 chan struct{} 控制令牌
下载网页保存文件使用 os.Create 写入 HTML 文件
支持重试机制封装带重试的请求逻辑
使用 context 控制取消或超时实现更复杂的任务调度系统
支持代理设置 Transport.Proxy 实现

七、小结

通过本篇案例你掌握了:

  • 使用 Goroutine 启动并发任务
  • 使用 Channel 汇总任务结果
  • 使用 WaitGroup 管理协程生命周期
  • 网络请求的错误处理与超时机制

这为你实现一个功能完善的高并发爬虫、网页检测器或 API 批量处理工具奠定了基础。

以上就是Go语言实现多协程并发下载网页内容的完整代码的详细内容,更多关于Go多协程并发下载网页内容的资料请关注脚本之家其它相关文章!

相关文章

  • Golang解析JSON遇到的坑及解决方法

    Golang解析JSON遇到的坑及解决方法

    这篇文章主要为大家介绍了Golang解析JSON时会遇到的一些坑及解决方法,文中的示例代码讲解详细,对我们学习Go语言有一点的帮助,需要的可以参考一下
    2023-02-02
  • golang使用go test输出单元测试覆盖率的方式

    golang使用go test输出单元测试覆盖率的方式

    单元测试覆盖率是衡量代码质量的一个重要指标,重要的代码文件覆盖率应该至少达到80%以上,Java 可以通过JaCoCo 统计覆盖率,那么go 项目如何进行代码覆盖率测试呢,本文将给大家详细的介绍一下golang使用go test输出单元测试覆盖率的方式,需要的朋友可以参考下
    2024-02-02
  • 使用golang实现一个MapReduce的示例代码

    使用golang实现一个MapReduce的示例代码

    这篇文章主要给大家介绍了关于如何使用golang实现一个MapReduce,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-09-09
  • golang中import cycle not allowed解决的一种思路

    golang中import cycle not allowed解决的一种思路

    这篇文章主要给大家介绍了关于golang中import cycle not allowed解决的一种思路,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧
    2018-08-08
  • 四种Golang实现middleware框架的方式小结

    四种Golang实现middleware框架的方式小结

    middleware是一般框架里面常用的形式,比如web框架、rpc框架等,本文为大家详细介绍了四种实现middleawre的方式,感兴趣的可以了解一下
    2024-03-03
  • Go单例模式与Once源码实现

    Go单例模式与Once源码实现

    这篇文章主要介绍了Go单例模式与Once源码实现,本文结合示例代码给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-12-12
  • Go源码字符串规范检查lint工具strchecker使用详解

    Go源码字符串规范检查lint工具strchecker使用详解

    这篇文章主要为大家介绍了Go源码字符串规范检查lint工具strchecker使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • Go逃逸分析示例详解

    Go逃逸分析示例详解

    这篇文章主要为大家介绍了Go逃逸分析示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Go语言开发包安装全流程

    Go语言开发包安装全流程

    这篇文章主要介绍了Go语言开发包安装全流程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-05-05
  • GoFrame框架gcache的缓存控制淘汰策略实践示例

    GoFrame框架gcache的缓存控制淘汰策略实践示例

    这篇文章主要为大家介绍了GoFrame框架gcache的缓存控制淘汰策略的实践示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06

最新评论