Go的go-colly爬虫本地正常,部署至Heroku无数据输出问题排查
问题
用Go语言的go-colly库开发的网页爬虫,本地运行正常,但部署到Heroku的Docker容器后无法返回任何数据,求排查方向。
爬虫代码
type Song struct { Title string Subtitle string Link string } var wg sync.WaitGroup var mu sync.Mutex func getTune(query string, allSongs *[]Song, wg *sync.WaitGroup) { c := colly.NewCollector( colly.AllowedDomains("https://get-tune.cc", "get-tune.cc"), ) c.WithTransport(&http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, DualStack: true, }).DialContext, IdleConnTimeout: 120 * time.Second, TLSHandshakeTimeout: 20 * time.Second, ExpectContinueTimeout: 1 * time.Second, }) runtime.LockOSThread() mu.Lock() defer mu.Unlock() defer wg.Done() c.OnHTML(".playlist li", func(element *colly.HTMLElement) { link := element.Attr("data-mp3") songs := element.DOM song := songs.Find(".playlist-name").Find("b").Text() em := songs.Find(".playlist-name").Find("em").Text() if len(em) > 5 && !strings.Contains(strings.ToLower(em), "remix") && !strings.Contains(strings.ToLower(em), "mix") && !strings.Contains(strings.ToLower(em), "edit") && !strings.Contains(strings.ToLower(song), "mix") && !strings.Contains(strings.ToLower(song), "edit") && !strings.Contains(strings.ToLower(song), "remix") { *allSongs = append(*allSongs, Song{ Title: song, Subtitle: em, Link: link, }) } }) err := c.Visit(fmt.Sprintf("https://get-tune.cc/search/f/%s/", strings.Join(strings.Split(query, " "), "+"))) if err != nil { fmt.Printf("Error: %v", err) } } func Crawler(query string) []Song { songs := []Song{} wg.Add(1) go getTune(query, &songs, &wg) wg.Wait() return songs }
Dockerfile
FROM golang:1.19.2-alpine3.15 AS builder WORKDIR /app COPY go.mod ./ COPY go.sum ./ RUN go mod download COPY . . RUN go build -o main . EXPOSE 4000 CMD ["./main"]
docker-compose.yml
version: '3.9' services: redis: image: 'bitnami/redis:latest' command: redis-server --requirepass password ports: - 6379:6379 volumes: - $PWD/redis-data:/var/lib/redis - $PWD/redis.conf:/usr/local/etc/redis/redis.conf environment: - REDIS_REPLICATION_MODEL=master - ALLOW_EMPTY_PASSWORD=yes app: build: . command: go run main.go volumes: - .:/app ports: - 4000:4000 depends_on: - redis
heroku.yml
build: docker: web: Dockerfile run: web: ./main
可能的问题原因及排查方向
- AllowedDomains配置错误:colly的
AllowedDomains参数应传入不带协议的纯域名,你写了"https://get-tune.cc",会导致域名匹配失败,爬虫被阻止发起请求。修正为仅"get-tune.cc"即可。 - 锁的使用逻辑错误:
getTune函数一开始就调用mu.Lock(),并通过defer在函数结束时解锁,但OnHTML的回调是在colly内部的goroutine中执行的,此时锁已释放,向allSongs追加数据时会出现竞态条件。另外,整个函数持有锁会让goroutine失去并发意义,应把锁的范围缩小到修改allSongs的代码块内。 - 网络访问限制:Heroku容器环境可能存在出站网络限制,或者目标网站对Heroku的IP段做了拦截。可以在Heroku容器内执行
curl https://get-tune.cc测试连通性,或在代码中增加更详细的网络错误日志。 - 错误日志未输出到标准错误:当前用
fmt.Printf输出错误,Heroku日志可能无法捕获,改用log.Printf或直接输出到os.Stderr,方便在heroku logs中查看具体错误信息。 - runtime.LockOSThread()无意义且可能引发问题:该函数会将goroutine绑定到OS线程,容器环境中线程资源有限,这种绑定可能导致线程耗尽,直接删除即可。
- 端口监听问题:如果服务硬编码了监听4000端口,Heroku会动态分配端口,需要读取
PORT环境变量设置监听端口,否则服务无法被外部访问(虽不是爬虫无数据的直接原因,但会影响整体服务可用性)。
内容的提问来源于stack exchange,提问作者Javokhirbek Khaydaraliev
相关产品推荐
相关产品推荐

