ウェブサイトの更新を定期的に確認してメールで送る方法（Heroku+SendGridの利用）


<h4>1.はじめに</h4>
ウェブサイトの更新を定期的にチェックしてメールで送って欲しい、そんなニーズはたくさんあると思います。既にそういったサービスも存在するので、わざわざ自前で作る必要もないのでは？と思われる方もいらっしゃると思いますが、カスタマイズがしにくかったり、有料だったりとそれなりに痒いところに手が届かないということもあるので、HerokuとSendGridを使って実現する方法を紹介します。ちなみに、Heroku＋Googleメールという手法も試しましたが、頻繁に失敗してしまい、安定性が低いのでSendGridがオススメです。なお、毎度のことですが、あくまで参考例のプログラムを紹介であり、ご自身の責任で実行するようにしてください。

<h4>2.全体の流れ</h4>
（１）SendGridの準備
<a href ="https://sendgrid.com/">SendGrid</a>にあらかじめ登録が必要です。送信数にもよりますが、個人でトライアルする範囲であれば、無料のものでOKでしょう。アカウントの登録が終わってサインインした後、SettingsのAPIKEYという項目から入ってCreate API KeyでAPIキーを発行しておきます。

（２）プログラムの作成
Herokuで定時にメールを送信する関数を実行させるためのファイル（clock.py）、指定された内容のメールを送る関数と、ウェブサイトから特定の情報をスクレイピングするプログラムを記述したPythonファイル（email_sendgrid.py）を作ります。

（３）Herokuへのデプロイ
これについては、解説がたくさんあるのでこの記事での解説は省略します。（一般的な方法でOKです）公式のHerokuスターターガイドも良いですがQiitaの他の方の記事でわかりやすいものを参考にされるのが良いと思います。

<h4>3. プログラム１ー①ウェブサイトからのスクレイピング</h4>

```python:email_sendgrid-1.py
import lxml.html
import requests
import pprint
import json

URL = "https://www.xyz.xyz"

#requestsモジュールを使って指定したURLのウェブサイトを取得します。
r = requests.get(URL)

#textメソッドを使ってテキスト化します。確認のため、htmlをprint関数で出力して確認
#しても良いでしょう。
html = r.text

#xpathを使って検索できるよう、lxmlモジュールを使います
root = lxml.html.fromstring(html)    

#データベースに保存することを想定し、辞書形式の空箱を容易します。
news = {}

#ここからはXPathの内容にもよるので、Chromeの検証ツールを使って、FULL XPATHを
#にらみながら、必要な箇所が抽出できるようコードを書いていきます。
#この部分のコードはウェブサイトの内容にもよります。
for count in range(1,5):
    path_string = 'FULL XPATH'
    path_string_span = 'FULL XPATH'
    dl = root.xpath(path_string)
    dl_span = root.xpath(path_string_span)
    news[dl_span[0].text.rstrip('\r\n')] =  dl[0].text

#emailを送る関数に渡せるようタイトルと中身（body）を定義しておきます。
#bodyはデータベースに入れやすいようここではjsonにしていますが、ここの部分は工夫してください。
title = 'News from website'
body = json.dumps(news, ensure_ascii=False) 
```

<h4>4. プログラム１ー②SendGridからメールを送る関数</h4>

SendGridを使ってメールを送る関数を記述します。sendgridにはモジュールがあるので、pip3 install sendgridコマンドであらかじめモジュールをインストールしておきましょう。

```python:email_sendgrid-2.py
import ssl
import os
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

def send_mail(title, body):
    message = Mail(
    from_email = ‘aaa@gmail.com',
    to_emails = ‘bbb@gmail.com',
    subject = title,
    html_content = body)
    try:
        sg = SendGridAPIClient(‘KEY') 
        response = sg.send(message)
        print(response.status_code)
        print(response.body)
        print(response.headers)
    
    except Exception as e:
        print(e)
```

なお、実際にプログラムを作成するときは、上記のemail_sendgrid-1.pyとemail_sendgrid-2.pyは１つのファイルにしてくださいね。

<h4>5. Herokuで定時にメールを送信する関数を実行させるためのファイル</h4>
今回、定時にHerokuを動かして関数を実行させることになりますが、その技術の解説は、以下の本家本元の<a href = "https://devcenter.heroku.com/ja/articles/clock-processes-python">Herokuのウェブサイトの解説</a>が実は最もわかりやすかったりするので、そこを読んでみてください。ここにサンプルプログラムがあるのでそれを真似て、プログラムをつくってみます。

```python:clock.py
from apscheduler.schedulers.blocking import BlockingScheduler
from email_sendgrid import send_mail
import email_sendgrid

sched = BlockingScheduler()

@sched.scheduled_job('cron', day_of_week='mon-fri', hour=10)
#@sched.scheduled_job('interval', minutes=1)
def timed_job():
   print('This job is run mon-fri 19 oclock.')
   send_mail(email_sendgrid.title, email_sendgrid.body)

sched.start()
```
<h4>6. その他Herokuで動作させるために必要なファイル</h4>

Herokuお決まりのrequirements.txtとProcfileを作成し、Herokuにデプロイします。

```requirements.txt
APScheduler==3.0.0
lxml
requests
sendgrid
```


```python:Procfile
clock: python clock.py
```
<h4>7.最後に</h4>
半月ほど運用をしてみて、問題なく定時にメールが送られてきましたので、安定して動くのではないかと思います。ウェブサイトが変更になった場合など、スクレイピングのところでエラーが出る可能性があるので、try: exceptを使ってエラー処理をするなど、動作が確認できたら、さらに工夫をしてみてください。