返回空或403因go默认client无user-agent被拦截;需手动设user-agent、accept等header,用http.newrequest+client.do并设超时;html解析推荐goquery而非标准库;并发需用channel限速防封ip。

用 net/http 发起请求时,为什么返回空或 403?
Go 默认的 http.Client 不带 User-Agent,多数网站会直接拒绝这种“裸请求”。不是代码写错了,是被服务端拦截了。
- 必须手动设置
req.Header.Set("User-Agent", "Mozilla/5.0 ..."),哪怕只是模拟基础浏览器 - 部分站点还校验
Accept、Accept-Language,加一句req.Header.Set("Accept", "text/html")更稳妥 - 别用
http.Get(url)简写——它没法设 header,得走http.NewRequest+client.Do()流程 - 超时没设?默认无超时,遇到卡死连接会永久阻塞。建议显式设
http.Client{Timeout: 10 * time.Second}
解析 HTML 用 golang.org/x/net/html 还是第三方库?
标准库 golang.org/x/net/html 能跑,但写起来反直觉:它是流式解析器,没有 CSS 选择器,也不能直接 FindAll。真要快速提取字段,不如用轻量第三方。
- 推荐
github.com/PuerkitoBio/goquery:API 类 jQuery,支持$(selector).Each()和.Text(),学习成本低 - 如果只跑一次、不维护,且 DOM 结构极简单,标准库够用;但一旦要处理嵌套、属性筛选、容错(比如标签不闭合),
goquery的Document.Find()明显更稳 - 注意
goquery底层仍用标准库 parser,所以编码问题(如 GBK 页面)得自己先转 UTF-8,它不自动猜
并发抓取时,为什么 goroutine 泛滥或被封 IP?
开 1000 个 goroutine 直接发请求,既压垮自己内存,也大概率触发目标站风控——这不是并发能力问题,是没控速。
- 用带缓冲的 channel 控制并发数,比如
sem := make(chan struct{}, 10),每次请求前sem ,结束后 <code> - 别忽略
time.Sleep:即使并发数压住了,连续请求间隔太短(比如毫秒级)也会被识别为脚本。保守起见,加time.Sleep(1 * time.Second) -
http.Transport的MaxIdleConnsPerHost建议设为和并发数一致,否则复用连接失败,反而新建更多 TCP 连接 - 所有请求共用一个
http.Client实例,别每个 goroutine 都 new 一个
保存数据到文件,ioutil.WriteFile 为啥总出错?
ioutil.WriteFile 已被弃用,Go 1.16+ 应该用 os.WriteFile;但更关键的是路径和权限问题——尤其在 Windows 或容器里运行时。
- 路径别硬写
"./data/output.txt",用filepath.Join("data", "output.txt")兼容不同系统分隔符 - 目录不存在就写入?会报
no such file or directory。先调os.MkdirAll("data", 0755) - 内容含中文却显示乱码?确保字符串本身就是 UTF-8 编码(Go 字符串默认就是),但写入后用记事本打开异常,是因为记事本默认 ANSI。用 VS Code 或 Vim 看,或加 BOM(不推荐)
- 追加写入别用
WriteFile(它总是覆盖),改用os.OpenFile+os.O_APPEND | os.O_CREATE | os.O_WRONLY
真正麻烦的从来不是语法,而是每个环节的隐式依赖:HTTP 头、编码、连接复用、限速策略、文件系统行为……漏掉任何一个,程序都可能在某个环境里静默失败。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











