学習めも。

Python、Anaconda学習中🔰 ブログ引っ越ししました😄よろしくお願いします!https://noeiganolife.com/

記録用🔰

プログラミング以外の記事はこちら

メールを自動送信する(失敗!)

フォルダ構成

(やりたい事) 複数のメールをまとめて送信する

f:id:Moriane:20220414064522p:plain ・メール本文にしようする文章はテキストファイルに保存
・顧客の名前やメールアドレスはExcelファイルから参照
・添付ファイルはフォルダに保存しておく
・該当する顧客にメール送信するプログラムを作成する!!

メール送信工程

①メール送付リストを作成 フォルダーから請求書のPDFファイルを1つ取得する
(PDFファイルごとに繰り返す) 顧客IDをPDFファイル名から把握する
該当する顧客データをファイル「顧客マスタ」から検索
メール送付リストに「顧客データ」と「PDFファイル」を登録
②メールを一斉送信 SMTPサーバーに接続
(メール送付リストの顧客ごとに繰り返す) メッセージを準備
件名、メールアドレスを設定
メール本文を追加
添付ファイルにPDFファイルを追加
メッセージを送信
SMTPサーバーと接続を閉じる

メールを送信するには「メール送付リスト」に登録した顧客を1つずつ取り出して、顧客ごとにメッセージデータを作成する
メッセージデータには、まず件名とメールアドレスを設定し、本文を追加して。請求書のPDFファイルを添付ファイルとして加えて送信する

メール送付リストの作成

次のように[顧客データ、請求書PDFファイルのパス]を要素に持つリストにする
このようにリストを作成すれば、顧客データの中のメールアドレスに、PDFファイルを添付して、メール送信すれば良いので、一斉に送信する処理を作りやすくなる

mailing_list = [
  [[顧客ID, 顧客名,部署名,担当者名,メールアドレス,・・・],
  請求書PDFファイルパス],
  [[顧客ID, 顧客名,部署名,担当者名,メールアドレス,・・・],
  請求書PDFファイルパス],
  ・・・
]

・最初に必要なモジュールを全てインポートしておく

openpyxl Excelファイルの読み込み
Path フォルダ内のファイル検索
smtplib SMTPサーバーと更新する
MIMEMultipart メッセージを作成する
MIMEText 本文を作成する
MIMEApplication 添付ファイルを作成する
sys プログラムを中止する
import sys
import openpyxl
from pathlib import Path
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication

# 顧客マスタの読み込み
wb_master = openpyxl.load_workbook("顧客マスタ.xlsx")
ws_master = wb_master["Sheet1"]

customer_list = []
for row in ws_master.iter_rows(min_row=2):
    if row[0].value is None:
        break
    value_list = []
    for c in row:
        value_list.append(c.value)
    customer_list.append(value_list)

# 請求書PDFのフォルダー
pdf_dir = "請求書PDF_202007"

# メーリングリスト
mailing_list = []

# フォルダーから請求書のPDFファイルを1つずつ取得する
for invoice in Path(pdf_dir).glob("*.pdf"):
    # 「顧客ID」 は、 PDFファイルの拡張子を除いた部分
    customer_id = invoice.stem
    # 該当する顧客データを「顧客マスタ」から検索
    for customer in customer_list:
        if customer_id == customer[0]:
            # メーリングリストに「顧客データ」と
            # 「PDFファイル」のパスを追加
            mailing_list.append([customer, invoice])
            # 顧客ID、顧客名、メールアドレス、
            # PDFファイルのパスを表示
            print(customer[0], customer[1], customer[4], invoice)

結果

(検索)
⇨PDFファイルが対象なので、glob()の括弧の中には「*.pdf」を指定して、拡張子でファイルを検索

(取得)
⇨取得したPDFを送信する顧客はファイル名が顧客IDとなっている。拡張子を除いた顧客IDの部分はstemで取得できる

(送信リストに追加)
検索した顧客データは送付する請求書のPDFファイルのパスと一緒にメール送信リストに追加。これから送付する顧客とPDFファイルが確認できるようprint()で表示 f:id:Moriane:20220414073651p:plain

メールを一斉に送信

間違えて送信しないようにテストをする
プログラムにテストモードを追加する
変数test_modeがTrieの場合は自分のアドレス宛てに送信するようにする
※テストモードではtest、本番モードではrealと入力してもらうようにするが、本番モードのreal以外が入力された場合はテストモードにする

また、テストモードの切り替えはinput()を用いて、画面から行うようにする
次に送信を開始する前に、どちらのモードが設定されているかを明示して送信しても大丈夫かを確認する
input()を用いて、続行して送信する場合はyes、中止する場合はnoを入力してもらい、入力した値がyes以外の場合は、sys.exit()でプログラムを中止する
プログラムを続行する場合は、メッセージの送信に備えて、メール本文をmail_body.txtから読み込んでおく

# モード選択
print()  # 1行空ける
mode = input("モード選択(テスト=test、本番=real):")
# 本番以外はテスト
if mode != "real":
    test_mode = True
else:
    test_mode = False

# 送信確認
if test_mode:
    result = input("テストモードで自分宛てに送信します(続行=yes、中止=no):")
else:
    result = input("本番モードで送信します(続行=yes、中止=no):")

# 続行以外は中止
if result != "yes":
    print("プログラムを中止します")
    sys.exit()

# メール本文をファイルから読み込んでおく
text = open("mail_body.txt", encoding="utf-8")
body_temp = text.read()
text.close()

▼結果
プログラムを実行すると次のように画面モードを選択できる。
ここでは、送信するかどうかの確認でNoを入力して、プログラムを中止している ・SMTPサーバに接続したら、メール送信リストの顧客への送信を始める。メール送付リストの1件文のデータは[顧客データ, 請求書PDFファイルのパス]のようなリストになっている。
⇨そこでメール送信リストをリープして一つの要素を変数dataに代入してインデックスを用いてdata[0]で顧客データ、data[1]で請求書PDFファイルのパスを取得する
⇨この顧客データから顧客メールアドレスを取得して、件名や自分のアドレスと一緒にメッセージ設定する。
さらに、会社名や担当者名も取得して、メール本文にformat()で埋め込む。
・添付ファイルのデータは請求書PDFファイルのパスにあるPDFファイルからバイナリで読み取る
※ここでヘッダーにふあするファイル名には、パスに.nameをつけて「フォルダーを除いたファイル名」を指定する
・メール本文と添付ファイルの追加が終わったら、メッセージをsend_message()で送信する。
・送信中は進捗がわかるように、print()で送信している顧客IDと会社名を表示
・リストの全ての顧客に送信を完了したら、最後にSMTPサーバーとの接続を閉じる ▼全てのコード(mail_invice_sender.py)

import sys
import openpyxl
from pathlib import Path
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication

# 顧客マスタの読み込み
wb_master = openpyxl.load_workbook("顧客マスタ.xlsx")
ws_master = wb_master["Sheet1"]

customer_list = []
for row in ws_master.iter_rows(min_row=2):
    if row[0].value is None:
        break
    value_list = []
    for c in row:
        value_list.append(c.value)
    customer_list.append(value_list)

# 請求書PDFのフォルダー
pdf_dir = "請求書PDF_202007"

# メーリングリスト
mailing_list = []

# フォルダーから請求書のPDFファイルを1つずつ取得する
for invoice in Path(pdf_dir).glob("*.pdf"):
    # 「顧客ID」 は、 PDFファイルの拡張子を除いた部分
    customer_id = invoice.stem
    # 該当する顧客データを「顧客マスタ」から検索
    for customer in customer_list:
        if customer_id == customer[0]:
            # メーリングリストに「顧客データ」と
            # 「PDFファイル」のパスを追加
            mailing_list.append([customer, invoice])
            # 顧客ID、顧客名、メールアドレス、
            # PDFファイルのパスを表示
            print(customer[0], customer[1], customer[4], invoice)

# モード選択
print()  # 1行空ける
mode = input("モード選択(テスト=test、本番=real):")
# 本番以外はテスト
if mode != "real":
    test_mode = True
else:
    test_mode = False

# 送信確認
if test_mode:
    result = input("テストモードで自分宛てに送信します(続行=yes、中止=no):")
else:
    result = input("本番モードで送信します(続行=yes、中止=no):")

# 続行以外は中止
if result != "yes":
    print("プログラムを中止します")
    sys.exit()

# メール本文をファイルから読み込んでおく
text = open("mail_body.txt", encoding="utf-8")
body_temp = text.read()
text.close()

my_address = "自分のメールアドレス"

# SMTPサーバー(今回はGmailで送信)
smtp_server = "smtp.gmail.com"
port_number = 587

# ログイン情報(今回はGmailのアカウントを入力する)
account = "自分のメールのアカウント"
password = "自分のメールのパスワード"

# SMTPサーバーに接続
server = smtplib.SMTP(smtp_server, port_number)
server.starttls()
server.login(account, password)

# メーリングリストの顧客に1つずつメール送信
for data in mailing_list:
    customer = data[0]
    pdf_file = data[1]

    # メッセージの準備
    msg = MIMEMultipart()
    # 件名、メールアドレスの設定
    msg["Subject"] = "ご請求書送付のご案内[株式会社エクセルパイソン]"
    msg["From"] = my_address
    if test_mode:
        msg["To"] = my_address
    else:
        msg["To"] = customer[4]

    # メール本文の追加
    body_text = body_temp.format(
        company=customer[1],
        department=customer[2],
        person=customer[3]
    )
    body = MIMEText(body_text)
    msg.attach(body)
    # 添付ファイルの追加
    pdf = open(pdf_file, mode="rb")
    pdf_data = pdf.read()
    pdf.close()
    attach_file = MIMEApplication(pdf_data)
    attach_file.add_header("Content-Disposition", "attachment", filename=pdf_file.name)
    msg.attach(attach_file)

    # メール送信
    print("メール送信:", customer[0], customer[1])
    server.send_message(msg)

# SMTPサーバーとの接続を閉じる
server.quit()

▼結果(失敗!)
なぜ!ここまできて😭
Emailポリシーに引っかかったらしいです💦

解決できなかったので、ここまでにしましょうw f:id:Moriane:20220415073107p:plain

Pythonで自動送信するときに気をつけること

メール送信を自動化するときには「送信ミス」と「迷惑メールに間違われること」に特に気をつけないといけない。
まずはテストモードを実装して、自分のアドレス宛てにテストで送信して、本文と添付ファイルの内容を確認できるようにする
また、本当に一斉送信を開始しても大丈夫か、一時停止して確認するステップも実装する
さらに、送信中は進捗がわかるように、送信している相手をprint()で随時表示し、送信中にミスに気がついた場合は、キー「Ctrl+C」を押すことで強制終了できる。