应优先使用 colly 而非 net/http + goquery:colly 内置反爬、超时、并发控制及 robots.txt 遵守,而手写易遇重定向循环、gzip 未解压、charset 错误、malformed html panic 等问题;必须配置 async、maxdepth、transport 参数并禁用 url 重访;onhtml 失效多因 js 渲染或响应不全,需先验证响应长度;字段提取后须判空、去空格、校验 utf-8、避免 o_trunc 覆盖数据;日志需记录错误 url 以定位路径级反爬。

Go 里写 WebScraper 不该从零造轮子,直接用 colly 能在 10 分钟内跑通基础爬取逻辑,但必须立刻处理反爬、超时和并发控制,否则上线即崩。
为什么不用 net/http + goquery 手写?
手写 HTTP 请求 + HTML 解析看似灵活,实际会反复踩坑:robots.txt 忽略、重定向循环、gzip 响应没解压、charset 解析失败导致乱码。更麻烦的是,goquery 的 Find() 遇到 malformed HTML 容易 panic,而真实网页里 <div> 这类标签错位太常见。<p>实操建议:</p>
<ul>
<li>除非目标站点极简单(纯静态、无 JS、无重定向),否则别碰裸 <code>net/http + goquery
strings.NewReader(resp.Body) 前先检查 resp.Header.Get("Content-Encoding") == "gzip" 并用 gzip.NewReader() 包一层goquery.NewDocumentFromReader() 要包 recover(),否则一个错位标签就 kill goroutine
colly 初始化时必须设的三个配置项
colly 默认行为对生产环境极不友好:不限速、不守 robots.txt、不设超时。以下三项不设,爬几次就被封 IP。
实操建议:
- 用
c := colly.NewCollector(colly.Async(true), colly.MaxDepth(2))启动,Async开启并发是必须的,但MaxDepth建议从 2 起步,防意外爬进无限分页 - 必须调
c.WithTransport(&http.Transport{...}),至少设MaxIdleConns: 20和ResponseHeaderTimeout: 10 * time.Second - 立刻加
c.AllowURLRevisit = false(默认 true!),否则同一 URL 可能被重复抓 5 次以上
提取字段时 OnHTML 的常见失效场景
OnHTML("a.title") 找不到元素?大概率不是选择器错,而是页面用了 JS 渲染或内容在 iframe 里。真实爬虫里,80% 的“找不到”问题出在响应体根本没加载完目标 HTML。
实操建议:
- 先用
c.OnResponse(func(r *colly.Response) { fmt.Printf("status: %d, len: %d\n", r.StatusCode, len(r.Body)) })看响应长度是否异常小(比如只有 2KB 却该有 50KB) - 遇到 JS 渲染页面,别上
chromedp—— 先确认能否用curl -s 'URL' | grep -o 'window\.data =.*?;' | head -1提取埋点数据,很多网站把结构化数据塞在 script 标签里 -
OnHTML内部别用fmt.Println打日志,会拖慢速度;改用log.Printf并加if len(e.ChildText("a")) > 0判空再取值,避免e.Text返回空字符串还继续处理
保存结果前必做的三件事
写入文件或数据库前不校验,容易把半截 HTML、乱码字段或空结构体存进去,后续清洗成本翻倍。
实操建议:
- 每个字段提取后立刻用
strings.TrimSpace(),并检查长度:if len(title) 200就跳过这条记录 - 用
utf8.ValidString(text)过滤非 UTF-8 字符,尤其防 Windows-1252 编码的引号、破折号混入 - 写文件时用
os.OpenFile(..., os.O_CREATE|os.O_APPEND|os.O_WRONLY),别用O_TRUNC,否则程序崩溃一次就丢光之前所有数据
真正难的从来不是“怎么拿到数据”,而是“怎么稳定地、合规地、可维护地拿满 7 天”。colly 的 OnError 回调里打印 err.Error() 很重要,但更关键的是记录下触发错误的 response.Request.URL.String() —— 很多反爬策略只针对特定路径生效,不带 URL 的日志等于没记。











