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

如何使用go-colly抓取站点TLS握手阶段的证书

问题原因

你写的两个TLS回调不触发,本质是回调的适用场景搞混了:

  • GetCertificate是TLS服务端侧的回调,作用是根据客户端发来的ClientHello信息返回服务端自身的证书,爬虫作为TLS客户端时根本不会调用这个方法
  • GetClientCertificate是双向TLS认证场景下的客户端回调,只有服务端主动要求客户端提供身份证书时才会执行,普通公开HTTPS站点不会请求客户端证书,自然不会触发打印
    这两个接口都不是用来获取服务端下发证书的入口。
可行实现方案

要在Colly爬取流程中同步拿到TLS握手时服务端出示的证书,直接在自定义http.Transport的TLS配置里注册VerifyPeerCertificate回调即可。这个回调是TLS客户端侧的标准钩子,会在收到服务端证书后、默认证书校验流程执行时触发,入参直接包含服务端传来的原始证书字节数据、以及已完成校验的证书链对象,不需要修改默认的安全校验逻辑。

可直接运行的参考代码:

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"net/http"

	"github.com/gocolly/colly/v2"
)

func main() {
	c := colly.NewCollector(
		colly.AllowedDomains("pkg.go.dev"),
	)

	transport := &http.Transport{
		TLSClientConfig: &tls.Config{
			// 握手收到服务端证书后自动触发该回调
			VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
				// verifiedChains 存储已通过默认校验的证书链,第一个元素为站点叶子证书
				if len(verifiedChains) > 0 && len(verifiedChains[0]) > 0 {
					leafCert := verifiedChains[0][0]
					fmt.Printf("获取到站点证书,CN: %s\n", leafCert.Subject.CommonName)
					fmt.Printf("证书颁发机构: %s\n", leafCert.Issuer.CommonName)
					fmt.Printf("证书过期时间: %v\n", leafCert.NotAfter)

					// 若需要和当前请求绑定,可将证书写入对应请求的Context中
				}
				// 返回nil表示继续执行默认证书校验,不打断正常请求流程
				return nil
			},
		},
	}

	if err := c.WithTransport(transport); err != nil {
		panic(err)
	}

	c.OnResponse(func(r *colly.Response) {
		fmt.Printf("请求完成: %s\n", r.Request.URL)
	})

	_ = c.Visit("https://pkg.go.dev")
}

补充注意事项

  • 并发爬取场景下如果需要把证书和对应请求一一绑定,建议在OnRequest回调中给当前请求的Context初始化专属存储结构,在VerifyPeerCertificate回调中把解析好的证书写入对应上下文,后续OnResponse处理时直接从上下文读取即可,避免并发下数据串扰。
  • 如果需要爬取自签名证书的测试环境站点,可以把TLS配置中的InsecureSkipVerify设为true跳过默认证书校验,此时回调的verifiedChains参数为空,直接遍历rawCerts中的原始字节,用x509.ParseCertificate手动解析证书即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 00:24:43