如何使用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
相关产品推荐
相关产品推荐

