Go语言做爬虫可行但需谨慎,新手应优先考虑成熟方案;其标准库缺乏反爬支持、HTML解析不便,colly虽好但易因配置错误失败,且不适用于JS渲染页面。
Go 语言做爬虫不是“不能”,而是得先想清楚:你真需要自己写爬虫,还是该用更稳的方案?
Go 的并发模型(goroutine + channel)看起来天生适合爬虫,但新手容易忽略几个硬伤:
net/http 不自动处理重定向、Cookie 持久化、User-Agent 轮换、TLS 指纹等常见反爬要素golang.org/x/net/html 是底层解析器,不提供类似 Python 的 BeautifulSoup.find() 那种链式 API)colly 功能强,但文档松散、源码抽象层多,出错时堆栈难定位(比如 OnHTML 回调没触发,常因 selector 匹配失败或 DOM 加载时机问题)colly 入手:最小可行爬虫长什么样如果你坚持用 Go,colly 是目前最接近“开箱即用”的选择。但别一上来就写分布式或带登录态的复杂逻辑。
下面这个例子只抓取标题和链接,但它暴露了新手最常卡住的三个点:
package mainimport ( "fmt" "log" "github.com/gocolly/colly" )
func main() { c := colly.NewCollector( colly.AllowedDomains("httpbin.org"), // 注意:这里必须是域名,不是完整 URL colly.Async(true), // 开启异步,但需手动调用 Wait() )
c.OnHTML("title", func(e *colly.HTMLElement) { fmt.Println("Title:", e.Text) }) c.OnHTML("a[href]", func(e *colly.HTMLElement) { link := e.Attr("href") if link != "" { fmt.Println("Link:", link) } }) c.OnRequest(func(r *colly.Request) { r.Headers.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36") }) c.Visit("https://httpbin.org/html") c.Wait() // 必须加!否则 main 退出,goroutine 被强制终止}
AllowedDomains 参数只接受域名(如 "httpbin.org"),传 "https://httpbin.org" 会导致请求被静默丢弃OnRequest 设置 Header 必须在 Visit 之前,顺序错了就无效c.Wait() 是同步等待所有 goroutine 完成的唯一可靠方式;漏掉它,程序大概率输出空结果就退出context deadline exceeded 怎么调这是 Go 爬虫最常报的错误,本质是 HTTP 请求超时。但 colly 默认超时是 30 秒,实际中多数网站响应在 2–5 秒,所以问题往往不在“太短”,而在“没设对地方”。
colly.NewCollector(colly.Async(true), colly.MaxDepth(2)) 不影响超时,得单独设 colly.UserAgent 或改底层 http.Client
http.Client 并注入:c := colly.NewCollector()
c.WithTransport(&http.Transport{
DialContext: (&net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 5 * time.Second,
})
c.WithTimeout(10 * time.Second)WithTimeout 控制的是单次请求生命周期(含 DNS、连接、TLS、响应头),不是整个爬取过程Go 生态里没有成熟、轻量、可嵌入的 Headless 浏览器驱动。有人用 chromedp,但它依赖本地 Chrome 进程,启动慢、内存高、调试难,且无法在无图形界面服务器(如阿里云轻量应用服务器)上稳定运行。
如果目标页面内容由 JS 渲染(比如 React/Vue SPA),更现实的做法是:
/api/xxx 接口)requests + playwright 做渲染层,Go 只做后续清洗和入库,通过 HTTP 或消息队列通信
强行在 Go 里塞浏览器自动化,最后八成是在给运维添麻烦。