[Python]定期的にRSSの更新を検知し、Gmailメール通知＋CSV保存する方法 メモ

* 定期的にRSSをパースし、更新があれば、その内容をGmail通知とCSV保存する方法についてメモする※N番煎じ記事
* [先日試した定期処理](https://qiita.com/KWS_0901/items/ccf1d9dee30894979a64)を学習目的で拡張した。
* 利用ライブラリ
  * 定期実行：`schedule`
  * メール送信：`smtplib`
  * RSSパース：`feedparser`



## コード

* `run.py`

  ```python
  import csv
  from schedule import every, repeat, run_pending
  import time
  import feedparser
  import datetime as dt
  from email.mime.text import MIMEText
  from email.utils import formatdate
  import smtplib
  import pytz
  
  # RSS URL
  RSS_URL = "RSS_URL"
  # メール送信元
  FROM_ADDR = "FROM_ADDRESS"
  # メール送信先
  TO_ADDR = "TO_ADDRESS"
  # Gmailアドレス
  USERNAME = "YOUR_GMAIL_ADDRESS"
  # Googleアプリパスワードを指定
  # https://support.google.com/accounts/answer/185833?hl=ja
  PASSWORD = "YOUR_APP_PASSWORD"
  
  # メッセージ生成
  def create_message(from_addr, to_addr, subject, updated_entries):
      # メール本文
      message_body = "Updated NEWS:\n"
      for entry in updated_entries:
          joined_entry = " ".join(entry)+"\n"
          message_body += joined_entry
      msg = MIMEText(message_body)
      # その他
      msg['Subject'] = subject
      msg['From'] = from_addr
      msg['To'] = to_addr
      msg['Date'] = formatdate()
      return msg
  
  # Gメール通知
  def send_mail(body_msg):
      smtpobj = smtplib.SMTP('smtp.gmail.com', 587)
      smtpobj.starttls()
      # Gmailログイン
      smtpobj.login(USERNAME, PASSWORD)
      # メール通知
      smtpobj.send_message(body_msg)
      smtpobj.close()
  
  # CSVファイル書き込み
  def write_csv(updated_entries):
      with open("updated_entries.csv", "a", newline='', encoding='utf_8') as f:
          writer = csv.writer(f)
          for updated_entry in updated_entries:
              print(updated_entry)
              writer.writerow(updated_entry)
  
  # 主要処理
  # RSS読み込み → 更新検知 → CSV保存＋メール通知
  @ repeat(every(6).hours)
  def job():
      updated_entries = []
      d = feedparser.parse(RSS_URL)
      jst = pytz.timezone('Asia/Tokyo')
      # 各エントリーの確認
      for entry in d.entries:
          # 現在日時
          now = dt.datetime.now(tz=jst)
          # 更新日時
          entry_time = dt.datetime.strptime(
              entry.updated, '%Y-%m-%dT%H:%M:%S.%fZ')
          # 前回確認日時
          last_checked = now - dt.timedelta(hours=6)
          # 更新日比較（ 前回確認日時 <= 更新日時 < 現在日時 )
          if last_checked <= jst.localize(entry_time) < now:
              updated_entries.append(
                  [entry.title, entry.link, str(jst.localize(entry_time))])
      # 更新が検知された場合...
      if len(updated_entries) > 0:
          print("Updated Entries")
          # CSV書き込み
          write_csv(updated_entries)
          # 通知内容生成
          msg = create_message(FROM_ADDR, TO_ADDR,
                               "Updated Entries Notification", updated_entries)
          # 通知
          send_mail(msg)
  
  
  # 定期実行
  while True:
      run_pending()
      time.sleep(1)
  
  ```

  

## 動作確認

* [Yahoo News RSS](https://news.yahoo.co.jp/rss/topics/top-picks.xml)で動作確認を行った。

* 実行

  ```python
  python run.py
  Updated Entries
  ['東京五輪きょう開幕 夜に開会式', 'https://news.yahoo.co.jp/pickup/6399458?source=rss', '2021-07-22 22:35:48+09:00']
  ['名誉最高顧問に森氏 組織委検討', 'https://news.yahoo.co.jp/pickup/6399463?source=rss', '2021-07-22 23:16:02+09:00']
  ['五輪雰囲気ない 冷めた街の声も', 'https://news.yahoo.co.jp/pickup/6399466?source=rss', '2021-07-22 23:42:40+09:00']
  ['1歳が熱中症で死亡か 母を逮捕', 'https://news.yahoo.co.jp/pickup/6399460?source=rss', '2021-07-22 22:31:03+09:00']
  ['開会式プログラム発売中止 角川', 'https://news.yahoo.co.jp/pickup/6399461?source=rss', '2021-07-22 22:51:59+09:00']
  ['ギニアが一転、東京五輪参加へ', 'https://news.yahoo.co.jp/pickup/6399459?source=rss', '2021-07-22 22:28:45+09:00']
  ['開会式に海老蔵 MISIAは君が代', 'https://news.yahoo.co.jp/pickup/6399462?source=rss', '2021-07-22 22:52:53+09:00']
  ...
  ```

  

* メール通知内容(Gmail本文)

  ```text
  Updated Entries:
  東京五輪きょう開幕 夜に開会式 https://news.yahoo.co.jp/pickup/6399458?source=rss 2021-07-22 22:35:48+09:00
  名誉最高顧問に森氏 組織委検討 https://news.yahoo.co.jp/pickup/6399463?source=rss 2021-07-22 23:16:02+09:00
  五輪雰囲気ない 冷めた街の声も https://news.yahoo.co.jp/pickup/6399466?source=rss 2021-07-22 23:42:40+09:00
  ...
  ```

  

* CSV保存結果(`updated_entries.csv`)

  ```csv
  東京五輪きょう開幕 夜に開会式,https://news.yahoo.co.jp/pickup/6399458?source=rss,2021-07-22 22:35:48+09:00
  名誉最高顧問に森氏 組織委検討,https://news.yahoo.co.jp/pickup/6399463?source=rss,2021-07-22 23:16:02+09:00
  五輪雰囲気ない 冷めた街の声も,https://news.yahoo.co.jp/pickup/6399466?source=rss,2021-07-22 23:42:40+09:00
  ...
  ```

  

## 参考情報

* [AWS Lambdaを使って、Yahooニュース!が更新されたらSlackに通知させる](https://lab.m-field.co.jp/2020/06/12/aws-lambda-yahoonews-to-slack/)
* [Pythonでメール送信](https://www.python.ambitious-engineer.com/archives/2034#smtplib)