👁 226 次阅读
零依赖监控的价值
在运维和站长日常工作中,网站可用性监控是不可或缺的一环。市面上有大量成熟的监控工具,但很多需要安装第三方库、注册账号、部署Agent,甚至产生费用。而”零依赖”意味着只使用Python标准库,无需执行任何pip install命令,在任何有Python 3环境的机器上都能直接运行。这对于轻量级部署、快速验证、资源受限环境(如树莓派、VPS)以及不想引入额外供应链风险的场景来说,价值巨大。下面我将手把手教你用纯标准库实现一个覆盖四个维度的网站监控脚本。
一、监控四个维度概览
一个完整的网站可用性监控至少需要关注以下四个维度:
1. DNS解析检查:确认域名能否正常解析为IP地址。DNS故障往往是网站不可访问的最早信号。
2. HTTP状态码检查:确认服务器返回的响应码是否在正常范围内(200-399)。4xx和5xx都意味着不同的问题。
3. 响应时间测量:即使状态码正常,如果响应时间过长,用户体验也会严重受损。
4. 内容关键词检查:验证页面是否包含预期内容,防止白屏、错误页面或内容被篡改。
这四个维度从基础设施层到应用层逐层递进,组合起来能覆盖绝大多数常见的网站故障场景。
二、DNS解析检查
DNS是网站访问的第一步。如果域名无法解析,后续一切检查都没有意义。Python的socket库提供了gethostbyname和getaddrinfo函数,可以完成DNS查询。
import socket
import time
def check_dns(domain, timeout=5):
"""检查域名DNS解析是否正常"""
start = time.time()
try:
ip = socket.gethostbyname(domain)
elapsed = time.time() - start
return {
"status": "OK",
"ip": ip,
"resolve_time_ms": round(elapsed * 1000, 2)
}
except socket.gaierror as e:
elapsed = time.time() - start
return {
"status": "FAIL",
"error": f"DNS解析失败: {e}",
"resolve_time_ms": round(elapsed * 1000, 2)
}
except Exception as e:
return {
"status": "ERROR",
"error": str(e)
}
# 使用示例
result = check_dns("www.example.com")
print(result)
gethostbyname会将域名解析为IPv4地址,如果域名不存在或DNS服务器无响应,会抛出socket.gaierror异常。同时记录解析耗时,如果解析时间异常长(比如超过1000ms),也值得关注。
三、HTTP状态码检查
DNS解析正常后,下一步是向目标服务器发起HTTP请求,检查返回的状态码。Python标准库中的urllib.request模块可以完成这个任务。
import urllib.request
import urllib.error
def check_http_status(url, timeout=10):
"""检查HTTP状态码"""
try:
req = urllib.request.Request(
url,
headers={
"User-Agent": "Mozilla/5.0 (MonitorBot/1.0)",
"Accept": "text/html,application/xhtml+xml"
}
)
with urllib.request.urlopen(req, timeout=timeout) as response:
status_code = response.getcode()
return {
"status": "OK" if 200 <= status_code < 400 else "WARN",
"http_code": status_code,
"headers": dict(response.headers)
}
except urllib.error.HTTPError as e:
return {
"status": "FAIL",
"http_code": e.code,
"reason": e.reason
}
except urllib.error.URLError as e:
return {
"status": "FAIL",
"error": f"连接失败: {e.reason}"
}
except Exception as e:
return {
"status": "ERROR",
"error": str(e)
}
# 使用示例
result = check_http_status("https://www.example.com"
print(result)
这里需要注意几点:设置合理的User-Agent避免被某些WAF拦截;设置timeout防止请求无限等待;HTTPError捕获4xx/5xx响应,URLError捕获连接层面的失败。
四、响应时间测量
单次测量的响应时间可能受网络波动影响,因此最佳实践是进行多次采样,取平均值和最大值来评估性能。
import urllib.request
import time
import statistics
def check_response_time(url, samples=5, timeout=10):
"""多次采样测量响应时间"""
times = []
errors = 0
for i in range(samples):
try:
start = time.time()
req = urllib.request.Request(url, headers={"User-Agent": "MonitorBot/1.0"})
with urllib.request.urlopen(req, timeout=timeout) as resp:
resp.read(1024) # 读取少量数据即可
elapsed = time.time() - start
times.append(elapsed)
except Exception:
errors += 1
time.sleep(0.5) # 采样间隔,避免对服务器造成压力
if not times:
return {
"status": "FAIL",
"error": "所有采样均失败",
"errors": errors
}
avg_time = statistics.mean(times)
max_time = max(times)
min_time = min(times)
status = "OK" if avg_time < 3.0 else "WARN"
if avg_time >= 5.0:
status = "FAIL"
return {
"status": status,
"samples": samples,
"success": len(times),
"errors": errors,
"avg_ms": round(avg_time * 1000, 1),
"max_ms": round(max_time * 1000, 1),
"min_ms": round(min_time * 1000, 1)
}
# 使用示例
result = check_response_time("https://www.example.com" samples=5)
print(result)
这里设置了三个性能阈值:平均响应时间低于3秒为正常,3-5秒为警告,超过5秒为失败。采样间隔0.5秒既能保证结果稳定,又不会对目标服务器造成明显压力。
五、内容关键词检查
有时候页面返回200但实际内容是错误页面或已被篡改。通过检查页面中是否包含预期关键词,可以捕获这类”静默故障”。
import urllib.request
def check_content_keyword(url, keywords, timeout=10):
"""检查页面内容是否包含指定关键词"""
try:
req = urllib.request.Request(url, headers={"User-Agent": "MonitorBot/1.0"})
with urllib.request.urlopen(req, timeout=timeout) as response:
content = response.read().decode("utf-8", errors="ignore")
results = {}
all_found = True
for keyword in keywords:
found = keyword.lower() in content.lower()
results[keyword] = found
if not found:
all_found = False
return {
"status": "OK" if all_found else "FAIL",
"content_length": len(content),
"keywords_check": results,
"missing": [k for k, v in results.items() if not v]
}
except Exception as e:
return {
"status": "ERROR",
"error": str(e)
}
# 使用示例
keywords = ["首页", "联系我们"]
result = check_content_keyword("https://www.example.com" keywords)
print(result)
关键词检查是大小写不敏感的,这样可以避免因页面格式调整导致的误报。返回结果中包含了缺失的关键词列表,方便快速定位问题。
六、完整主程序整合与定时执行
将以上四个模块整合为一个完整的监控脚本,并添加日志输出和告警逻辑:
#!/usr/bin/env python3
"""零依赖网站全维度监控脚本"""
import socket
import urllib.request
import urllib.error
import time
import statistics
import json
import os
import sys
from datetime import datetime
LOG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "monitor.log")
def log(message, level="INFO"):
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
entry = f"[{timestamp}] [{level}] {message}"
print(entry)
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(entry + "\n")
def run_full_check(config):
"""执行全维度检查"""
url = config["url"]
domain = config.get("domain") or url.split("//")[1].split("/")[0]
keywords = config.get("keywords", [])
samples = config.get("samples", 5)
timeout = config.get("timeout", 10)
log(f"开始监控: {url}")
overall_status = "OK"
results = {}
# 1. DNS检查
log(f"[1/4] DNS解析检查: {domain}")
results["dns"] = check_dns(domain, timeout)
if results["dns"]["status"] == "FAIL":
overall_status = "FAIL"
log(f"DNS解析失败,跳过后续检查", "ERROR")
return {"overall": overall_status, "results": results}
# 2. HTTP状态码检查
log(f"[2/4] HTTP状态码检查")
results["http_status"] = check_http_status(url, timeout)
if results["http_status"]["status"] == "FAIL":
overall_status = "FAIL"
# 3. 响应时间测量
log(f"[3/4] 响应时间测量 ({samples}次采样)")
results["response_time"] = check_response_time(url, samples, timeout)
if results["response_time"]["status"] == "FAIL":
overall_status = "FAIL"
elif results["response_time"]["status"] == "WARN" and overall_status == "OK":
overall_status = "WARN"
# 4. 内容关键词检查
if keywords:
log(f"[4/4] 内容关键词检查: {keywords}")
results["content"] = check_content_keyword(url, keywords, timeout)
if results["content"]["status"] == "FAIL":
overall_status = "FAIL"
else:
log("[4/4] 未配置关键词,跳过内容检查")
log(f"监控完成,总体状态: {overall_status}")
return {"overall": overall_status, "results": results}
# 配置
MONITOR_CONFIG = {
"url": "https://www.example.com"
"domain": "www.example.com",
"keywords": ["首页", "关于我们"],
"samples": 5,
"timeout": 10
}
if __name__ == "__main__":
result = run_full_check(MONITOR_CONFIG)
# 输出JSON格式结果,便于其他系统消费
print(json.dumps(result, ensure_ascii=False, indent=2))
# 非OK状态时以非零退出码结束,方便crontab捕获
sys.exit(0 if result["overall"] == "OK" else 1)
将脚本保存为monitor.py后,通过crontab实现定时执行。编辑crontab:
crontab -e
添加以下行,表示每5分钟执行一次:
*/5 * * * * /usr/bin/python3 /path/to/monitor.py >> /path/to/monitor_cron.log 2>&1
如果需要将告警推送到邮件,可以在脚本末尾添加基于smtplib的邮件发送逻辑(同样属于标准库,无需额外安装)。
七、总结
通过本文的教程,我们使用Python标准库中的socket、urllib.request、time和statistics模块,实现了一个覆盖DNS解析、HTTP状态码、响应时间和内容关键词四个维度的网站监控脚本。整个方案零第三方依赖,部署简单,资源占用极低,适合在各种环境中快速部署。
零依赖并不意味着功能简陋——对于大多数中小站点的可用性监控需求,这四个维度已经足够覆盖95%以上的故障场景。如果你需要更复杂的监控(如SSL证书过期检测、多节点分布式探测、Webhook告警等),可以在这个基础上逐步扩展,仍然保持标准库优先的原则。监控的核心价值在于”早发现、早处理”,工具越轻量,部署阻力越小,越有可能被真正用起来。



💡 想快速检测你的网站是否宕机?
推荐使用 Uptimerobot 免费监控工具 —— 支持 HTTP/TCP/Ping 多协议监控,5分钟免费检查间隔,宕机第一时间邮件/SMS 通知。