#1.この記事について
主目的は、
日次メール作成作業や月次定期メールなど
定型的な作業を効率化したかったこと。
第二の目的は、
Pythonにおけるjsonファイルの扱いや
Outlookの連携を学習したかった。
(率直なところOutlookをいじるだけなら
Excelにデータを載せてVBAで作成するのが
一番正着なんではないかと思うけど)
######※ツールとソースコードはGithubに上げてます(後述)
#2.やりたいこと
・jsonファイルの項目を考える
・jsonファイルをハンドリングするクラス作成
・Office Outlookをハンドリングするクラス作成
・上記クラスを連携してメール作成するmainメソッドの開発
#3.使用したツール・環境
・Windows10
・Python 3.7.0
・office2016
・その他下記コードに記載のライブラリ各種
#4.作成したコードと解説
##クラス図
pycharmで作成したクラス図はこのようになる。
##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
なにか補足がありましたらコメントください。