Webサイトの変更検知→通知を自動化|curl×diff×Slack/メールでアラート運用

監視・アラート
スポンサーリンク

「更新されたかどうか毎回見に行くのがつらい」「仕様変更に即気づきたい」——その悩み、curl + diff + 通知で自動化できます。
この記事は Webページ(HTML / JSON / API)の“差分検知”→Slackやメールに通知までを、コピペで動く最小構成から実運用のコツまでまとめました。

できるようになること

  • 指定URLの内容を定期取得して変更があった時だけ通知
  • Slack Webhookまたはメールにアラート送信
  • 多重実行の防止タイムアウト/再試行、**ノイズ除去(任意のフィルタ処理)**に対応

仕組み(ざっくり)

  1. curl でページを取得(圧縮展開・タイムアウト・再試行つき)
  2. 前回スナップショットと diff で比較
  3. 差分があれば、URL・ハッシュ・差分抜粋を Slack/メールに通知
  4. スナップショットを更新
  5. cron で定期実行(flock で多重起動防止)

事前準備(必要コマンド)

  • 必須: curl, diff, sha256sum, cron
  • Slack通知するなら: jq(JSON生成用、軽量)
  • メール通知するなら: mailx(S-nail/Heirloom 推奨)または sendmail

例:Debian/Ubuntu
sudo apt install -y curl diffutils coreutils jq s-nail

コピペで動く Bash スクリプト

保存先:/usr/local/bin/webwatch.sh(実行権限付与 sudo chmod 755 /usr/local/bin/webwatch.sh

#!/usr/bin/env bash
# webwatch.sh - Webサイトの変更検知→Slack/メール通知
set -euo pipefail

# ===== 設定(要変更) =========================================
URL="https://example.com/"             # 監視対象URL(HTML/JSONどちらでもOK)
NAME="example-home"                    # 任意の識別名(ファイル名に使用)
STATE_DIR="/var/lib/webwatch"          # スナップショット保存先
LOCK="/var/lock/webwatch-${NAME}.lock" # 排他ロック
TIMEOUT=20                             # curl全体タイムアウト秒
RETRY=3                                # 再試行回数

# 通知(どちらか / 併用可)
SLACK_WEBHOOK=""                       # 例: "https://hooks.slack.com/services/XXX/YYY/ZZZ" (空なら未使用)
EMAIL_TO=""                            # 例: "ops@example.com" (空なら未使用)

# 監視ノイズ除去用の任意フィルタ(任意・空OK)
# 例)JSONは整形して並び替え: FILTER_CMD='jq -S .'
# 例)HTMLの本文だけ取得:     FILTER_CMD='pup "main text{}"'
FILTER_CMD=""
# =============================================================

# 作業ディレクトリ
mkdir -p "$STATE_DIR"
WORKDIR="$(mktemp -d)"
trap 'rm -rf "$WORKDIR"' EXIT

SNAP="${STATE_DIR}/${NAME}.snap"
CUR="${WORKDIR}/current"
DIFF="${WORKDIR}/diff.txt"
HEADERS="${WORKDIR}/headers.txt"

USER_AGENT="webwatch/1.0 (+contact: you@example.com)"

# === 取得 ===
# -fsS: fail-silent + show errors, --compressed: 圧縮展開, -L: リダイレクト追跡
# --retry: ネットワークゆらぎ対策, --max-time: 全体タイムアウト
curl -fsS --compressed -L \
  --retry "$RETRY" --retry-delay 2 --retry-connrefused \
  --max-time "$TIMEOUT" \
  -A "$USER_AGENT" \
  -D "$HEADERS" \
  -o "$CUR" \
  "$URL"

# === 任意フィルタ(ノイズ除去・正規化) ===
if [[ -n "${FILTER_CMD}" ]]; then
  if command -v bash >/dev/null 2>&1; then
    # シェル経由でパイプ適用
    bash -c "$FILTER_CMD" < "$CUR" > "${CUR}.f" || cp "$CUR" "${CUR}.f"
    mv "${CUR}.f" "$CUR"
  fi
fi

# === 初回:スナップショット作成のみ ===
if [[ ! -f "$SNAP" ]]; then
  cp "$CUR" "$SNAP"
  echo "Initialized snapshot for ${NAME} ($URL)"
  exit 0
fi

# === 差分比較 ===
if diff -q "$SNAP" "$CUR" >/dev/null 2>&1; then
  # 変更なし
  exit 0
fi

# 変更あり → 差分抽出・ハッシュ算出
diff -u "$SNAP" "$CUR" | head -n 200 > "$DIFF" || true
OLD_HASH="$(sha256sum "$SNAP" | awk '{print $1}')"
NEW_HASH="$(sha256sum "$CUR"  | awk '{print $1}')"
WHEN="$(date +'%F %T %Z')"

# === 通知メッセージ(共通) ===
MSG_FILE="${WORKDIR}/message.txt"
{
  echo "🔔 変更検知: ${NAME}"
  echo "URL: ${URL}"
  echo "検知時刻: ${WHEN}"
  echo "hash: ${OLD_HASH} → ${NEW_HASH}"
  echo
  echo "差分(先頭200行まで):"
  cat "$DIFF"
} > "$MSG_FILE"

# === Slack通知 ===
if [[ -n "${SLACK_WEBHOOK}" ]]; then
  if command -v jq >/dev/null 2>&1; then
    # 改行を保持してJSON化
    jq -Rs --arg pretext "🔔 変更検知: ${NAME}\n${URL}\n${WHEN}\n${OLD_HASH} → ${NEW_HASH}\n" \
      'split("\n") as $lines |
       {text: $pretext} + {blocks: [{type:"section", text:{type:"mrkdwn", text: $pretext}},
                                   {type:"section", text:{type:"mrkdwn", text: ( $lines[0:200] | join("\n") )}} ]}' \
      < "$MSG_FILE" \
      | curl -fsS -X POST -H 'Content-type: application/json' --data @- "$SLACK_WEBHOOK" >/dev/null || true
  else
    echo "Slack送信をスキップ(jq が必要)" >&2
  fi
fi

# === メール通知 ===
SUBJECT="[webwatch] 変更検知: ${NAME}"
if [[ -n "${EMAIL_TO}" ]]; then
  if command -v mailx >/dev/null 2>&1; then
    mailx -s "$SUBJECT" -- "$EMAIL_TO" < "$MSG_FILE" || true
  elif command -v sendmail >/dev/null 2>&1; then
    {
      echo "Subject: $SUBJECT"
      echo "To: $EMAIL_TO"
      echo "Content-Type: text/plain; charset=UTF-8"
      echo
      cat "$MSG_FILE"
    } | sendmail -t || true
  else
    echo "メール送信をスキップ(mailx/sendmail なし)" >&2
  fi
fi

# === スナップショット更新 ===
cp "$CUR" "$SNAP"

ポイント:

  • FILTER_CMDjq -S . を入れると API/JSON の並び順が安定します。
  • HTMLなら pup 'main text{}' で本文テキストだけにして広告や日時のノイズを排除可能。
    pupsudo apt install -y pup で導入可)

cron に登録(多重実行防止つき)

例:5分おきにチェック。PATHとSHELLを明示し、flock で多重起動をブロック。

crontab -e

以下を追記:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash

*/5 * * * * /usr/bin/flock -n /var/lock/webwatch-example.lock \
  /usr/local/bin/webwatch.sh >> /var/log/webwatch-example.log 2>&1
  • 監視間隔は対象サイトの負荷・規約に合わせて調整(過剰ポーリングはNG)。
  • ログ出力先(/var/log/...)で実行状況を追跡できます。

よくある“ノイズ”を減らすコツ

  • 日時/トラッキングIDの除去sed -E 's/[0-9]{4}-[0-9]{2}-[0-9]{2}//g' などで置換
  • 特定セクションだけ比較pup 'main text{}'hxselect で本文抽出
  • JSONは正規化FILTER_CMD='jq -S .'(キー順を固定)
  • バイナリ/画像は対象外:テキスト化できるAPI/HTMLに限定

失敗しない設計(実務のツボ)

  • タイムアウト/再試行を必ず設定(--max-time, --retry
  • ユーザーエージェントを明示(連絡先を含めると吉)
  • 多重起動は事故の元flock で必ずガード
  • 初回は通知しない運用にするとノイズ減(上のスクリプトは初回保存のみ)
  • 差分は上限付きで送る(Slackメッセージが長すぎると拒否される)

変更検知の“次の一手”(発展)

  • 差分ファイルを添付してメール送信(mailx -amutt を使用)
  • 重大語をハイライトgrep -Ei 'error|critical|deprecated' で差分から抽出
  • 複数URLの一括監視:URLリストを回すラッパースクリプト化
  • Docker化:依存関係をコンテナに閉じ込め、cronはホスト側で管理

法的・倫理面の注意

  • robots.txt・サイト規約を守る(スクレイピングを禁止していないか)
  • レート制限夜間の配慮キャッシュ活用If-Modified-Sinceなど)で負荷軽減
  • 個人情報や著作物の二次利用に注意(検知はOKでも再配布はNGな場合あり)

トラブルシューティング

  • 通知が来ない:Slack Webhook URL の権限/有効期限、メールは MTA設定を確認
  • cronでだけ失敗:PATH不足・権限・改行コード(LF)を確認
  • 差分が毎回出る:フィルタで日付や動的IDを削除、または JSON 正規化
  • SSLエラー:証明書ストアを更新(ca-certificates パッケージ)

まとめ

  • curl で取得、diff で比較、変わった時だけ通知が最小の王道。
  • フィルタ処理で“本当に見たい差分”に寄せるとアラート精度が上がります。
  • cron×flock×タイムアウト/再試行安定稼働へ。

まずは1URLから始めて、必要に応じてフィルタと通知の粒度を育てていきましょう。

スポンサーリンク
Bash玄

はじめまして!Bash玄です。

エンジニアとしてシステム運用に携わる中で、手作業の多さに限界を感じ、Bashスクリプトを活用して業務を効率化したのがきっかけで、この道に入りました。「手作業は負け」「スクリプトはシンプルに」をモットーに、誰でも実践できるBashスクリプトの書き方を発信しています。

このサイトでは、Bashの基礎から実践的なスクリプト作成まで、初心者でもわかりやすく解説しています。少しでも「Bashって便利だな」と思ってもらえたら嬉しいです!

# 好きなこと
- シンプルなコードを書くこと
- コマンドラインを快適にカスタマイズすること
- 自動化で時間を生み出すこと

# このサイトを読んでほしい人
- Bashに興味があるけど、何から始めればいいかわからない人
- 定型業務を自動化したい人
- 効率よくターミナルを使いこなしたい人

Bashの世界に一歩踏み出して、一緒に「Bash道」を極めていきましょう!

Bash玄をフォローする