You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言使用channel收发数据异常 批量爬取URL写入文件问题

问题根因

你的代码存在3个核心问题导致channel无法正常工作:

  • 错误的channel关闭时机:你在每个getpageCanal函数中都加了defer close(canal),第一次调用getpageCanal执行结束就会关闭channel,后续其他调用往已关闭的channel写入数据会直接触发panic,就算未触发panic,主函数的range循环也会因为channel关闭提前退出,无法接收后续数据。
  • 无缓冲channel同步调用导致死锁:你当前是同步调用getpageCanal,无缓冲channel的发送操作必须有接收方同时就绪才能执行成功,你的主函数接收逻辑在所有getpageCanal调用结束后才执行,第一个getpageCanal执行到写入channel的步骤就会永久阻塞,程序直接死锁。
  • 潜在panic和资源泄漏风险:http.Get返回的响应体page.Body没有主动关闭,会导致TCP连接泄漏;如果http.Get请求出错,page变量会是nil,后续调用page.Body会直接触发空指针panic。
修复方案

我们用sync.WaitGroup来统计所有请求协程的执行状态,等所有请求完成后再统一关闭channel,同时给每个请求开启独立goroutine实现并发,完善错误和资源处理逻辑,修复后代码如下:

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"sync"
)

func main() {
	initial := "https://www1.medion.de/downloads/index.pl?op=detail&id="
	ending := "&type=treiber&lang=uk"

	links := []string{}
	os.Remove("dump.txt")
	dumpFile, err := os.Create("dump.txt")
	if err != nil {
		fmt.Println("创建文件失败:", err)
		return
	}
	defer dumpFile.Close() // 最后统一关闭文件

	c := make(chan string)
	var wg sync.WaitGroup

	for i := 16000; i < 16004; i++ {
		links = append(links, initial+fmt.Sprint(i)+ending)
	}
	fmt.Println(links[0])

	for _, link := range links {
		wg.Add(1) // 每个请求计数+1
		// 开启goroutine并发请求
		go func(target string) {
			defer wg.Done() // 请求完成计数-1
			getpageCanal(c, target)
		}(link)
	}

	// 单独开一个goroutine等所有请求完成后关闭channel
	go func() {
		wg.Wait()
		close(c)
	}()

	// 遍历channel接收所有数据,channel关闭后自动退出循环
	for el := range c {
		fmt.Println(el)
		n, err := dumpFile.WriteString(el)
		if err != nil {
			fmt.Println("写入文件失败:", err)
			return
		}
		if n == 0 {
			fmt.Println("无数据写入")
		}
	}
	fmt.Println("所有数据处理完成")
}

func getpageCanal(canal chan string, url string) {
	page, err := http.Get(url)
	if err != nil {
		fmt.Println("请求失败:", url, err)
		return
	}
	defer page.Body.Close() // 读完响应自动关闭Body释放资源

	content, er2 := ioutil.ReadAll(page.Body)
	if er2 != nil {
		fmt.Println("读取响应失败:", url, er2)
		return
	}
	canal <- string(content)
}
逻辑说明
  1. sync.WaitGroup会跟踪所有请求协程的执行状态,所有请求完成后才会关闭channel,避免提前关闭的问题
  2. 每个请求对应独立goroutine,无缓冲channel的发送操作和主函数的接收操作同时运行,不会出现阻塞死锁
  3. 所有资源(响应Body、文件)都加了defer自动释放,错误逻辑做了提前返回处理,避免空指针panic

内容的提问来源于stack exchange,提问作者dreamer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 06:54:03