python标准库ssl模块可直接获取证书有效期:建立未验证tls连接后调用getpeercert()获取notafter,再用ssl.cert_time_to_seconds()转为utc时间戳,需确保系统时间同步,并设置超时与重试机制。

用 ssl 模块获取证书有效期,别依赖第三方库
Python 标准库的 ssl 就能直接连服务器、抓证书、解析时间,不需要装 requests 或 pyOpenSSL——后者反而容易因 OpenSSL 版本不一致导致 SSLError 或解析失败。关键在绕过验证握手,只做 TLS 握手拿到原始证书链:
- 用
ssl.create_default_context().wrap_socket()建立未验证的连接(设check_hostname=False和cert_reqs=ssl.CERT_NONE) - 调用
getpeercert()得到字典格式证书信息,其中notAfter是 PEM 中的过期时间字符串(如'Jan 15 12:34:56 2025 GMT') - 用
ssl.cert_time_to_seconds()直接转成时间戳,比手动datetime.strptime()更稳——它已处理了各种 OpenSSL 输出格式变体
检查时区与系统时间必须同步,否则误报率极高
ssl.cert_time_to_seconds() 返回的是 UTC 时间戳,但本地系统时间若未同步(比如虚拟机休眠后没更新、Docker 容器没挂载 /etc/timezone),对比就会出错。常见现象是:证书明明还有 3 天过期,脚本却报“已过期”。
- Linux 主机跑定时任务前,先加
ntpdate -s time.windows.com或启用systemd-timesyncd - Docker 容器里运行,启动命令加上
--network=host或挂载宿主机时间:-v /etc/localtime:/etc/localtime:ro - 别用
time.time()直接减,统一用datetime.datetime.now(tz=datetime.timezone.utc)转为 UTC 再比较
发通知别卡在 SMTP,优先走 Webhook(如钉钉/企业微信)
自建 SMTP 服务常被云厂商拦截(尤其腾讯云轻量服务器默认封 25 端口),而 Webhook 只需一个 POST 请求,稳定且免配置。
- 钉钉机器人需开启「自定义关键词」或「加签」,否则消息发不出;关键词建议设为
SSL过期,避免被过滤 - 请求头必须带
Content-Type: application/json,否则企业微信返回415 Unsupported Media Type - 构造消息体时,把域名、过期时间、剩余天数都塞进
text字段,别依赖 markdown 格式——部分旧版客户端不渲染
生产环境必须加超时和重试,否则单个网站卡住整个检测流程
目标网站如果 TLS 握手慢(如 CDN 回源超时、WAF 拦截)、或中间网络抖动,wrap_socket() 默认会卡住几十秒,导致后续域名全跳过。
- 用
socket.setdefaulttimeout(10)全局设上限(注意:影响所有 socket 操作,建议在函数内临时设置再恢复) - 对单个域名最多重试 2 次,每次间隔 3 秒;重试后仍失败,记日志并跳过,不抛异常
- 不要用
threading并发检测——CPython 的 GIL 让多线程对 IO 并不提速,改用concurrent.futures.ThreadPoolExecutor(max_workers=5)更可控
getpeercert() 仍能取到 notAfter,但用户浏览器可能因链不全报错——这需要额外用 ssl.SSLContext().load_verify_locations() 验证整条链,属于另一层检测维度。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











