背景
做 SERP 监控需要告警,Slack / Email / Webhook / SMS 各有取舍。
def alert_slack(text):
requests.post(SLACK_WEBHOOK, json={
"blocks": [
{"type": "section", "text": {"type": "mrkdwn", "text": text}}
]
})
优点:实时 (秒级)、富文本块、thread 回复、@ 提醒 缺点:依赖 Slack,团队需 Slack 账号
def alert_email(to, subject, body):
import smtplib
from email.mime.text import MIMEText
msg = MIMEText(body)
msg["Subject"] = subject
msg["To"] = to
with smtplib.SMTP("smtp.gmail.com", 587) as s:
s.starttls()
s.login(USER, PASS)
s.send_message(msg)
优点:通用,不需要装 app,异步不阻塞 缺点:延迟 (分钟级)、易进垃圾箱、HTML 渲染差
def alert_webhook(url, payload):
requests.post(url, json=payload, timeout=5)
优点:通用 (JSON 任意接收方)、可路由到不同系统 缺点:需要接收端,失败重试要自己做
def alert_sms(to, text):
from twilio.rest import Client
client = Client(TWILIO_SID, TWILIO_TOKEN)
client.messages.create(
from_="+1234567890", to=to, body=text
)
优点:无网也能收、紧急最高优先级 缺点:贵 (每条 $0.01+)、字符限制 160、有 spam filter
| 指标 | Slack | Webhook | SMS | |
|---|---|---|---|---|
| 到达延迟 | 1-3s | 30-300s | 1-5s | 5-30s |
| 到达率 | 99% | 90% | 99%(自己控制) | 95% |
| 成本/月 | $0 | $0 | $0 | $5+ |
| 字符限制 | 40k | 无 | 无 | 160 |
| 异步支持 | ✓ | ✓ | ✓ | ✗ |
| 富文本 | ✓(blocks) | ✓(HTML) | JSON 自由 | ✗ |
| 团队协作 | ✓(thread) | ✗ | ✗ | ✗ |
| 移动端 | ✓(app) | ✓(mail app) | ✗ | ✓(SMS) |
| 场景 | 选 |
|---|---|
| 团队用 Slack | Slack |
| 非开发人员 | |
| 接自己系统 | Webhook |
| 紧急高优 | SMS |
def alert_with_fallback(text):
try:
alert_slack(text)
except Exception:
try:
alert_webhook(BACKUP_WEBHOOK, {"text": text})
except Exception:
alert_email(EMERGENCY_EMAIL, "Alert", text)
last_alert = {}
def alert_with_dedup(key, text, cooldown=300):
if key in last_alert and time.time() - last_alert[key] < cooldown:
return # 5 分钟内同 key 不重发
last_alert[key] = time.time()
alert_slack(text)
alert_slack_blocks(
f"排名变化",
blocks=[
{"type": "section", "text": {"type": "mrkdwn", "text": "*{{kw}}* 跌了"}},
{"type": "section", "fields": [
{"type": "mrkdwn", "text": f"上次: #{old}"},
{"type": "mrkdwn", "text": f"这次: #{new}"}
]},
{"type": "actions", "elements": [
{"type": "button", "text": "查看详情", "url": url}
]}
]
)
import threading
def alert_async(text):
threading.Thread(target=alert_slack, args=(text,)).start()
# Slack webhook 限速 1 条/秒
import time
last_call = 0
def alert_rate_limited(text):
global last_call
now = time.time()
if now - last_call < 1:
time.sleep(1 - (now - last_call))
last_call = time.time()
alert_slack(text)
import requests
SLACK_WEBHOOK = "https://hooks.slack.com/services/..."
def alert_slack_with_blocks(query, old_rank, new_rank, url):
blocks = [
{
"type": "header",
"text": {"type": "plain_text", "text": "📉 SERP 排名变化"}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*{query}* 跌了 5 位以上"
}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": f"上次: #{old_rank}"},
{"type": "mrkdwn", "text": f"这次: #{new_rank}"}
]
},
{
"type": "actions",
"elements": [
{"type": "button", "text": {"type": "plain_text", "text": "查看"}, "url": url}
]
}
]
requests.post(SLACK_WEBHOOK, json={"blocks": blocks}, timeout=5)
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
def alert_email_rich(to, query, old_rank, new_rank, url):
msg = MIMEMultipart()
msg["From"] = "alerts@yourdomain.com"
msg["To"] = to
msg["Subject"] = f"SERP Alert: {query} 变化"
html = f"""
<h2>SERP 排名变化</h2>
<p>关键词: <b>{query}</b></p>
<p>上次: #{old_rank} → 这次: #{new_rank}</p>
<p><a href="{url}">查看 SERP 结果</a></p>
"""
msg.attach(MIMEText(html, "html"))
with smtplib.SMTP("smtp.gmail.com", 587) as s:
s.starttls()
s.login(USER, APP_PASS)
s.send_message(msg)
import requests
import hmac
import hashlib
def alert_webhook_signed(url, payload, secret):
body = json.dumps(payload)
sig = hmac.new(secret.encode(), body.encode(), hashlib.sha256).hexdigest()
headers = {
"X-Signature": f"sha256={sig}",
"Content-Type": "application/json",
}
for attempt in range(3):
try:
r = requests.post(url, data=body, headers=headers, timeout=5)
if r.ok:
return
except Exception:
time.sleep(2 ** attempt)
from twilio.rest import Client
def alert_sms_emergency(to, query, new_rank):
client = Client(TWILIO_SID, TWILIO_TOKEN)
client.messages.create(
from_="+1234567890",
to=to,
body=f"SEO: {query} 跌到 #{new_rank},立即查看!",
)
| 指标 | 数值 |
|---|---|
| 告警总次数 | 28 次 |
| Slack 到达 | 28/28(100%) |
| Email 到达 | 24/25(96%) |
| Webhook 到达 | 28/28(100%) |
| SMS 到达 | 5/5(100%,仅用紧急) |
| 平均延迟 Slack | 2s |
| 平均延迟 Email | 60s |
| 成本 Slack | $0 |
| 成本 Email | $0 |
| 成本 SMS | $0.05(5 条紧急) |
4 种告警方式选型:
serpbase + 任意告警方式,5 分钟搭好。serpbase 的 1.4s P50 + auto-refund 让你告警永远及时,失败不扣 credit。