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

Written by

in

文章目录
  • 在互联网项目中,我们常需要批量获取多个网页的内容,例如: 爬虫程序抓取网页 HTML 数据聚合服务请求多个 API 批量检测多个 URL 的可用性 如果逐个请求(串行),效率将非常低下。Go 天生支持高并发,我们可以用 Goroutine 实现 多协程并发下载网页内容,显著提高吞吐能力。
  • 我们将构建一个小型并发网页下载器,具备以下能力: 输入一组网址列表 使用 Goroutine 并发请求多个网页 使用 Channel 收集下载结果 打印成功/失败状态与网页内容摘要 支持 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:%vn”, 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:%sn”, 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: … 所有网页请求已完成。
  • 这个简单的并发网页下载器可以继续扩展为: 功能方向 实现建议 限制最大并发数 使用带缓冲的 chan struct{} 控制令牌 下载网页保存文件 使用 os.Create 写入 HTML 文件 支持重试机制 封装带重试的请求逻辑 使用 context 控制取消或超时 实现更复杂的任务调度系统 支持代理 设置 Transport.Proxy 实现
  • 通过本篇案例你掌握了: 使用 Goroutine 启动并发任务 使用 Channel 汇总任务结果 使用 WaitGroup 管理协程生命周期 网络请求的错误处理与超时机制 这为你实现一个功能完善的高并发爬虫、网页检测器或 API 批量处理工具奠定了基础。 以上就是Go语言实现多协程并发下载网页内容的完整代码的详细内容,更多关于Go多协程并发下载网页内容的资料请关注风君子博客其它相关文章! 您可能感兴趣的文章: Go语言实现多协程文件下载器的过程详解 golang实现多协程下载文件(支持断点续传) 并发安全本地化存储go-cache读写锁实现多协程并发访问 详解Go多协程并发环境下的错误处理
  • 目录
    • 一、实战背景
    • 二、实战目标
    • 三、完整代码实现
    • 四、输出示例
    • 五、重点知识点讲解
      • 1. 使用 Goroutine 启动并发请求
      • 2. 使用 sync.WaitGroup 等待所有任务完成
      • 3. 使用带缓冲的 Channel 收集结果
      • 4. 设置请求超时
      • 5. 防止通道未关闭阻塞
    • 六、可扩展方向
      • 七、小结

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

        • 爬虫程序抓取网页 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:%vn", 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:%sn", 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: ...
        所有网页请求已完成。
        

        go fetchURL(url, &wg, resultCh)
        

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

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

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

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

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

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

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

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

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

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

        通过本篇案例你掌握了:

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

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

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

        您可能感兴趣的文章:

        • Go语言实现多协程文件下载器的过程详解
        • golang实现多协程下载文件(支持断点续传)
        • 并发安全本地化存储go-cache读写锁实现多协程并发访问
        • 详解Go多协程并发环境下的错误处理

        站内搜索