ウェブサイトの差分を検知してメール通知するシェルスクリプト

2020年は数多くのブルベに参加することになったが、各BRMでウェブサイトの更新を監視するのは辛い。

無料のウェブサービスもあるが、登録件数が不足したり、どこが変更されたのか分からなかったりしたので、 変更を検知したら差分箇所をメールで送信するシェルスクリプトを書いてみた。

事前に必要なもの

  • Linuxが動作するサーバ (ここではOracle CloudにUbuntu 18.04のインスタンスを作成した)
  • メールを送信するためのアカウント (ここでは個人用のGmailアカウント)

Gmailのアカウントを使う時は、下記を参照してアプリパスワードを生成しておく。

ssmtpのインストール

sendmailのために、今回はssmtpを使って設定してみる。

postfixのほうが好きな方はそちらで。

まずはssmtpをインストール。

sudo apt install ssmtp

設定ファイル(/etc/ssmtp/ssmtp.conf)を編集し、使うメールアカウントに沿って設定箇所を記入。

#
# Config file for sSMTP sendmail
#
# The person who gets all mail for userids < 1000
# Make this empty to disable rewriting.
root=***@gmail.com

# The place where the mail goes. The actual machine name is required no 
# MX records are consulted. Commonly mailhosts are named mail.domain.com
mailhub=smtp.gmail.com:587

# Where will the mail seem to come from?
rewriteDomain=gmail.com

# The full hostname
hostname=gmail.com

UseTLS=Yes
UseSTARTTLS=Yes
AuthUser=***@gmail.com
AuthPass=********(生成したアプリパスワード)********
AuthMethod=LOGIN

# Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
FromLineOverride=YES

スクリプト本体

webchecker.shという名前で下記の内容を書き込む。

#!/bin/bash

SCRIPT_DIR=$(cd $(dirname $0);pwd)
LISTFILE=${SCRIPT_DIR}/checklist.txt
DATETIME=$(date '+%Y%M%d_%h%m%s')

MAILTO="***@gmail.com"
MAILFROM="***@gmail.com"

curl=/usr/bin/curl
diff=/usr/bin/diff
sendmail=/usr/sbin/sendmail
cut=/usr/bin/cut
awk=/usr/bin/awk

#for s in $SITES; do
cat ${LISTFILE} | while read s
do
  name=$(echo $s | $cut -d'|' -f 1)
  url=$(echo $s | $cut -d'|' -f 2)
  begin=$(echo $s | $cut -d'|' -f 3 | sed 's|/|\\/|g')
  end=$(echo $s | $cut -d'|' -f 4 | sed 's|/|\\/|g')
  newfile="${SCRIPT_DIR}/result/$name/new.html"
  oldfile="${SCRIPT_DIR}/result/$name/old.html"
  newrange="${SCRIPT_DIR}/result/$name/new_range.html"
  oldrange="${SCRIPT_DIR}/result/$name/old_range.html"
  errorcount="${SCRIPT_DIR}/result/$name/count.txt"

  if [[ "$name" == "" ]]; then
    continue
  fi

  # 先頭が"#"の場合はスキップ
  if [[ "${name:0:1}" == "#" ]]; then
    continue
  fi

  echo "NAME:    "$name
  echo "  URL:   "$url
  echo "  BEGIN: "$begin
  echo "  END:   "$end
  echo ""

  mkdir -p "${SCRIPT_DIR}/result/$name/"

  statuscode=$($curl "$url" -o "${newfile}" -w '%{http_code}\n' -s)

  if [[ "${statuscode}" != "200" ]]; then
    echo "Status Code Error"
    count=$(cat "${errorcount}")
    echo $((++count)) > "${errorcount}"

    if [[ ${count} -eq 3 ]]; then
      tmp=$(mktemp)
      echo "To: ${MAILTO}" > $tmp
      echo "From: ${MAILFROM}" >> $tmp
      echo "Subject: ${name} Error" >> $tmp
      echo "" >> $tmp
      echo "${url}" >> $tmp

      $sendmail -t < $tmp
      echo -n "MAIL Sent"
      rm $tmp
    fi

  elif [[ -f "$oldfile" ]]; then
    cat "$oldfile" | $awk "/${begin}/,/${end}/" > "${oldrange}"
    cat "$newfile" | $awk "/${begin}/,/${end}/" > "${newrange}"
    echo '0' > "${errorcount}"

    if [[ ! -s "${newrange}" ]];then
      cp "${newfile}" "${newfile}".$(date '+%Y%m%d%H%M')
    fi
    if ! $diff "${oldrange}" "${newrange}" >/dev/null 2>&1; then
      echo -n "CHANGED "
      tmp=$(mktemp)
      echo "To: ${MAILTO}" > $tmp
      echo "From: ${MAILFROM}" >> $tmp
      echo "Subject: ${name} Changed" >> $tmp
      echo "" >> $tmp
      echo "${url}" >> $tmp
      echo "" >> $tmp
      $diff "${oldrange}" "${newrange}" -u >> $tmp

      $sendmail -t < $tmp
      echo -n "MAIL Sent"
      rm $tmp

      cp "${newfile}" "${oldfile}"
    else
      echo -n "NO CHANGED "
    fi
  else
    echo -n "NEW "
    mv "${newfile}" "${oldfile}"
  fi
  echo ""
done

実行権限を付与。

chmod +x webchecker.sh

設定ファイル

checklist.txtという名前で監視対象のURLと範囲を設定するファイルをwebchecker.shと同じディレクトリに作成。

BRM229|https://randonneurs.tokyo/?p=9950|<header>|<footer>
BRM320|https://audax-kinki.com/20brm0320_600/|<section>|<footer>
BRM328|https://randonneurs.tokyo/?p=9960|<header>|<footer>
BRM411|http://www.aj-kanagawa.org/brmkako-no-kaisai/brm2020/brm411-300|<td id="sites-canvas-wrapper">|DNS連絡フォーム</h2>
BRM418|http://www.vcraoba.yokohama/2020/2020brm418.html|<section>|</section>
RM430|https://randonneurs.tokyo/?p=9599|<header>|<footer>
RM430-Cue|https://randonneurs.tokyo/?p=11738|<header>|<footer>

次の書式で入力。

  • 1行につき1つの監視対象を記入
  • 区切り文字は「|」(パイプ記号) ※URLに使われそうにない文字を適当に選定した
  • 各フィールドには以下の順番で記入
    • 名前(結果の保存ディレクトリ名やメールの件名に使用)
    • 監視対象のURL
    • diffをかける範囲の先頭行に含まれる文字列
    • diffをかける範囲の最終行に含まれる文字列

動作確認

bash上で

./webchecker.sh

とすると、初回は「NEW」、2回目以降で変更がなければ「NO CHANGED」と表示される。

2回目以降で前回取得時から変更があれば「CHANGED」と表示され、メールが届くはず。

なお、正常に取得できなかった時は3回目にメールするようにした(1回だと結構誤検知が発生した)。

cronの設定

あとはcronで定期的に実行されるようにする。

crontab -e

5分おきに実行するなら下記のようにする。

*/5 * * * * /home/ubuntu/webchecker/webchecker.sh > /dev/null 2>&1

実行例

あとは設定がうまくできていれば、次のようなメールが届く。

https://randonneurs.tokyo/?p=9950

--- /home/ubuntu/webchecker/result/BRM229/old_range.html        2020-02-28 09:55:01.681675366 +0000
+++ /home/ubuntu/webchecker/result/BRM229/new_range.html        2020-02-28 09:55:01.685675416 +0000
@@ -20,7 +20,8 @@


   <div id="the-content" class="entry-content">
-  <p><span style="color: #ff0000;"><span class="red">【お知らせ/2月24日追記】</span><br />
+  <p><span style="color: #ff0000;"><strong>※BRM229東京300ウルトラオレンヂの開催は延期いたしました。</strong></span></p>
+<p><span style="color: #ff0000;"><span class="red">【お知らせ/2月24日追記】</span><br />
 試走の結果、国135号の渋滞が想定以上であったためPC3 LAWSON伊東渚町店を通過チェックに変更します。最新のキューシート(V1.1)にも反映しましたのでご確認ください。<br />
 特に復路の熱川温泉以降は側溝に蓋のない状態となりますので十分に注意してください。</span></p>
 <p><span style="color: #000000;">ゴール受付場所が変更になります。概要欄の変更点を確認の上でご参加ください。</span></p>

diffの内容も本文に入れることで、どこが更新されたのかを分かりやすくした。