PythonでGmailを自動送信する方法

最近、Pythonを始めるにあたり、自動化してみたかったGmailの送信処理の実装を行いました。
まだまだPython初心者に自分にとっては、学びになる点が多かったので共有したいと思います。

# 実装
実装は、多くの記事にあるように、Pythonのデフォルトのライブラリである`smtplib`と`MIMEText`の`email.mime.text`を使用して行います。

```python:mailer.py
import smtplib
from email.mime.text import MIMEText
from dotenv import load_dotenv
import os
import csv

load_dotenv()

class Mailer:
  """メールを送信するクラス
  """
  
  # 初期化
  def __init__(self, addr_to, subject, body):
    """Mailerクラスのコンストラクタ

    Args:
        addr_to (_type_): 送信元先情報
        subject (_type_): 件名
        body (_type_): 本文
    """
    # 送信元情報
    self.password = os.getenv('PASSWORD')
    self.addr_from = os.getenv('EMAIL')
    # 送信先
    self.addr_to = addr_to
    # 文字コード
    self.charset = "ISO-2022-JP"
    # 件名
    self.subject = subject
    # 本文
    self.body = body
  
  def send(self):
    """メールを送信する処理
    """
    # textのMIMEオブジェクトを作成する（メールの内容を作成する）
    msg = MIMEText(self.body.encode(self.charset), 'plain', self.charset)
    # 件名
    msg['Subject'] = self.subject
    # 送信元
    msg['Form'] = self.addr_from
    # 送信先
    msg['To'] = self.addr_to

    # gmailのsmtp経由で送信
    # 587番ポートを使用して送信する
    smtp = smtplib.SMTP('smtp.gmail.com', 587)
    # EHLOを利用し、ESMTPサーバに身元を明かす
    smtp.ehlo()
    # TLSモードでSMTP接続する
    smtp.starttls()
    smtp.ehlo()
    # SMTPサーバにログインする
    smtp.login(self.addr_from, self.password)
    # 指定したメールアドレスにメッセージを送信する
    smtp.send_message(msg)
    # サーバとの接続をクローズする
    smtp.close()
  
def create_mail_body(full_name):
  """メール本文の内容を作成する処理"""
  body = f"""
  {full_name}様
    
  お世話になっております。C-mindの柿です。
  ことらのメールはPythonによるGmailのテスト送信になります。
    
  よろしくお願いいたします。
    
  -----------------------
  パイソン 太郎
  株式会社Test
  Engineer
  Email: {os.getenv('EMAIL')}
  """
  return body

filename = './test.csv'

# CSVファイル読み込み
with open(filename, 'r') as f:
  reader = csv.reader(f)
  # 列名取得
  header = next(reader)
  
  for row in reader:
    # CSVにあるデータのemail分メール送信する
    # CSVからメール送信に必要な情報を抽出
    # 名前
    full_name = row[0] 
    # メールアドレス
    email = row[1]
    # メール内容
    addr_to = email
    # 件名
    subject = "Pythonによるテストメール"
    # 本文
    body = create_mail_body(full_name)
    # Mailerクラスのインスタンス生成
    mailer = Mailer(addr_to, subject, body)
    mailer.send()
```

初期情報として、送信元である自分のgmailアドレスとパスワードを設定します。
私の場合は、ライブラリとして`dotenv`をインストールして、環境変数を読み込み参照するようにしています。

dotenvを使用することで、`.env`ファイルに環境変数を設定し、`load_dotenv()関数`を使用することで環境変数を読み込むことができます。
参照する場合は、`os`ライブラリの`getenv()関数`の引数に参照したい環境変数名を渡すことで値の参照が可能となります。

そして、メールに必要となる項目を設定します。

次に、メールを送信する処理ですが、`MIMEText`のオブジェクトを作成し、実際にメールの内容となる部分を構築します。
```python
# textのMIMEオブジェクトを作成する（メールの内容を作成する）
msg = MIMEText(self.body.encode(self.charset), 'plain', self.charset)
# 件名
msg['Subject'] = self.subject
# 送信元
msg['Form'] = self.addr_from
# 送信先
 msg['To'] = self.addr_to
```

それを`SMTP通信`を用いて送信するようSMTPの設定をしていきます。
```python
# gmailのsmtp経由で送信
# 587番ポートを使用して送信する
smtp = smtplib.SMTP('smtp.gmail.com', 587)
# EHLOを利用し、ESMTPサーバに身元を明かす
smtp.ehlo()
# TLSモードでSMTP接続する
smtp.starttls()
smtp.ehlo()
# SMTPサーバにログインする
smtp.login(self.addr_from, self.password)
# 指定したメールアドレスにメッセージを送信する
smtp.send_message(msg)
# サーバとの接続をクローズする
smtp.close()
```

最後にCSVファイルから送信したいメールアドレスのデータ一覧を読み込み、`Mailer`インスタンスを作成しsend()メソッドを呼び出し、メールを送信します。

```python
filename = './test.csv'

# CSVファイル読み込み
with open(filename, 'r') as f:
  reader = csv.reader(f)
  # 列名取得
  header = next(reader)
  
  for row in reader:
    # CSVにあるデータのemail分メール送信する
    # CSVからメール送信に必要な情報を抽出
    # 名前
    full_name = row[0] 
    # メールアドレス
    email = row[1]
    # メール内容
    addr_to = email
    # 件名
    subject = "Pythonによるテストメール"
    # 本文
    body = create_mail_body(full_name)
    # Mailerクラスのインスタンス生成
    mailer = Mailer(addr_to, subject, body)
    mailer.send()
```

これでgmailの送信自動化を実装することができます。

# 補足
ここまで実装するとGoogle側から以下のセキュリティエラーが出ることがあります。
```
SMTPAuthenticationError: 
(535, b'5.7.8 Username and Password not accepted.
Learn more at\n5.7.8  https://support.google.com/mail/?p=BadCredentials
・・・
)
```
上記エラーが発生した場合は、Googleのセキュリティの設定が必要になります。

具体的には、以下の**2段階認証プロセス**を「オン」にし、**アプリパスワード**の設定が必要となります。
また、**アプリパスワードを設定したら、そのパスワードをPythonのパスワードを記述していた箇所のコードに記述する必要**があります。
![スクリーンショット 2022-06-11 19.34.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/78b11202-5506-4570-b057-1ef33b8f35e1.png)

# 参考文献
・[.env ファイルで環境変数を設定する (python-dotenv)](https://maku77.github.io/python/env/dotenv.html)

・[Pythonでメール(gmail)を送信できない場合の解決法](https://www.gocca.work/python-mailerror/)

・[21.17. smtplib --- SMTP プロトコルクライアント](https://docs.python.org/ja/3.6/library/smtplib.html)

・[email.mime: メールと MIME オブジェクトを一から作成](https://docs.python.org/ja/3/library/email.mime.html)