1.この記事について
主目的は、
日次メール作成作業や月次定期メールなど
定型的な作業を効率化したかったこと。
第二の目的は、
Pythonにおけるjsonファイルの扱いや
Outlookの連携を学習したかった。
(率直なところOutlookをいじるだけなら
Excelにデータを載せてVBAで作成するのが
一番正着なんではないかと思うけど)
※ツールとソースコードはGithubに上げてます(後述)
2.やりたいこと
・jsonファイルの項目を考える
・jsonファイルをハンドリングするクラス作成
・Office Outlookをハンドリングするクラス作成
・上記クラスを連携してメール作成するmainメソッドの開発
3.使用したツール・環境
・Windows10
・Python 3.7.0
・office2016
・その他下記コードに記載のライブラリ各種
4.作成したコードと解説
クラス図
jsonファイルの中身
要件としては、
・フォーマット(テキストメールかHTMLメールかリッチテキストか)
・差出人
・送り先(複数指定可)
・CC(複数指定可)
・BCC(複数指定可)
・件名
・本文
・添付ファイル
を設定できるものが望ましかったため、
このようになった。
{
"mailFormat": 3,
"senderAddress": "foo@gmail.com",
"receiverAddressSet": ["bar@Test.jp","bar2@Test.jp","bar3@Test.jp"],
"CC_AddressSet": ["bar@Test.jp","bar4@Test.jp"],
"BCC_AddressSet": ["bar@Test.jp","bar5@Test.jp"],
"title":"あいうえお",
"attachedFileFolderPath": "C:\\Users\\UserName\\Test",
"body": "〇〇様\n\nお疲れさまです。〇〇です。\n\n〇〇を送付します。\n\n以上。\n"
}
mailFormatは1~3の数字から選び、
それぞれが[テキストメールかHTMLメールかリッチテキストか]に該当しているようだ。
jsonハンドリングクラス
import json
class JsonHandler:
def __init__(self, jsonPath):
# jsonファイル読み込み
self.file = open(jsonPath, 'r', encoding="utf-8_sig")
self.jsonData = json.load(self.file)
# 一次ネストのjsonデータ取得
def getParam_OneNest(self, args):
return self.jsonData[args]
def __del__(self):
# ファイル閉じる
self.file.close()
引っ掛かった点①:エンコーディングについて
ファイル取込の際のエンコードの方法を検索しましたが、
こちらの記事を参考にさせていただいた。
WindowsでCP932(Shift-JIS)エンコード以外のファイルを開くのに苦労した話 - Qiita
どうやらBOMの処理のためにencoding="utf-8_sig"という方法を採用する必要があるようだ。
outlookハンドリングクラス
import glob
import os
import win32com.client
class OutlookHandler:
def __init__(self):
self.outlook = win32com.client.Dispatch("Outlook.Application")
self.newMail = None
def create(self):
self.newMail = self.outlook.CreateItem(0)
def setting(self, argMailformat, argSenderaddress,
argReceiveraddressset, argCc_Addressset,
argBcc_Addressset, argTitle,
argAttachedfilefolderpath, argBody):
# ///メールアイテムに情報設定
self.newMail.BodyFormat = argMailformat
try:
self.newMail._oleobj_.Invoke(*(64209, 0, 8, 0, self.outlook.Session.Accounts(argSenderaddress)))
except:
pass
self.newMail.To = argReceiveraddressset
self.newMail.cc = argCc_Addressset
self.newMail.Bcc = argBcc_Addressset
self.newMail.Subject = argTitle
# 本文に署名を付与(Outlook起動時のみ)
try:
self.newMail.Body = argBody \
+ os.linesep \
+ self.newMail.GetInspector.WordEditor.Bookmarks('_MailAutoSig').Range.Text
except:
self.newMail.Body = argBody
# 添付ファイル取得
if os.path.isdir(argAttachedfilefolderpath):
for file in glob.glob(argAttachedfilefolderpath + '\\*'):
if os.path.isfile(file):
self.newMail.Attachments.Add(file)
def display(self):
self.newMail.Display(False)
def release(self):
self.newMail = None
self.outlook = None
引っ掛かった点②:差出人メールアドレス設定について
こんなへんてこなやり方をしている。
self.newMail._oleobj_.Invoke(*(64209, 0, 8, 0, self.outlook.Session.Accounts(argSenderaddress)))
VBAの場合に定石だった
「SentOnBehalfOfName」フィールドがなぜか使えなかった(outlook側の設定の問題かもしれないけど)。
調べたらこんな記事がヒットしたため、それを採用。
https://stackoverflow.com/questions/35908212/python-win32com-outlook-2013-sendusingaccount-return-exception
ただしこの方法は、jsonで設定したアドレスがoutlookにユーザーのアドレスとして登録されていることが前提。
もし登録されていなかったら例外が発生するのでtrycatchで処理している。
SentOnBehalfOfNameのように登録していないものでも使えるというわけではない。
そのため、改良の余地はある。
(デフォルトのアドレスしか使わない場合は特に問題もない)
引っ掛かった点③:本文に署名を乗せる方法について
self.newMail.Body = argBody \
+ os.linesep \
+ self.newMail.GetInspector.WordEditor.Bookmarks('_MailAutoSig').Range.Text # 本文に署名を付与
このようにWordEditor.Bookmarksのフィールドを使用した方法だと、
「※outlookアプリケーション起動時にしか署名が付与されない」という大きな問題がある。
もし起動していない状態でmainメソッドを実行したら例外が発生する。
そのため、今回はtrycatchで処理して、
もし起動してないなら署名は諦めることにしている。
これも改良の余地がある部分だけど、仕方ない(outlook起動処理はそれはそれでけっこうめんどくさいので)。
mainメソッド
import datetime
import sys
# モジュール検索パスを再設定
# ダブルクリックによる起動にも対応できるようにする
sys.path.append(r"C:\Users\UserName\PycharmProjects\CreateToolAndTest")
import os
import tkinter
from tkinter import messagebox
from PrivateTool.OutlookMailCreater.JsonHandler import JsonHandler
from PrivateTool.OutlookMailCreater.OutlookHandler import OutlookHandler
import traceback
if __name__ == '__main__':
try:
# messageウィンドウの準備
root = tkinter.Tk()
root.withdraw()
root.attributes("-topmost", True)
# outlook起動
outlookHandler = OutlookHandler()
outlookHandler.create()
# jsonファイル操作オブジェクト
jsonHandler = JsonHandler(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'mailSetting.json'))
# jsonから値を取得しリストに格納
settingList = [jsonHandler.getParam_OneNest('mailFormat'), ]
settingList.append(jsonHandler.getParam_OneNest('senderAddress'))
tempSet = jsonHandler.getParam_OneNest('receiverAddressSet')
settingList.append('; '.join(tempSet))
tempSet = jsonHandler.getParam_OneNest('CC_AddressSet')
settingList.append('; '.join(tempSet))
tempSet = jsonHandler.getParam_OneNest('BCC_AddressSet')
settingList.append('; '.join(tempSet))
settingList.append(jsonHandler.getParam_OneNest('title'))
settingList.append(jsonHandler.getParam_OneNest('attachedFileFolderPath'))
settingList.append(jsonHandler.getParam_OneNest('body'))
# ◆◆◆リストデータになにか処理を加える場合はここに記述◆◆◆
# myDate = datetime.date.today()
# if myDate.day < 16:
# myDate -= datetime.timedelta(days=15)
# settingList[5] = settingList[5].replace('$date', myDate.strftime('%Y%m'))
# ◆◆◆◆◆◆
# outlookの新規メールアイテム作成
outlookHandler.setting(*settingList)
# メールアイテムの表示
outlookHandler.display()
outlookHandler.release()
# アナウンス
messagebox.showinfo("", "正常終了")
except:
print("Error", messagebox.showinfo("Error", "エラー発生"))
traceback.print_exc()
finally:
pass
引っ掛かった点④:messageウィンドウを最前面に出す方法について
次のように設定してあげればいいらしい。
root.attributes("-topmost", True)
5.終わりに
ツールとソースコードはこちら↓
CreateToolAndTest/Tool_Python/OutlookMailCreater at master · dede-20191130/CreateToolAndTest
なにか補足がありましたらコメントください。