Pythonライブラリ(Win32 API):pywin32(Window/Office操作)
1.概要
Win32 APIにアクセスしてWindowsでできる操作をPythonで実行できるライブラリがpywin32です。
公式Docsの解読が難しすぎるため、使えそうなやつのみ紹介します。
2.Word
2-1.PDF化:doc.SaveAs(path, FileFormat=17)
Wordの一括PDF化を紹介します。docx(word2007以降)であれば別記事で紹介した「docx2pdf」が有効ですが、旧拡張子docには使用できません。
そこでpywin32を使用してPDF化操作を実行します。なおコードは下記記事を参考にしました。
2-1-1.docファイル(サンプル)の作成
まずはサンプルファイルを用意します。「python-docxライブラリ」を使用して適当なファイルを作成しました。
[IN]
import docx
def makewordfile(text, filename):
doc = docx.Document()
doc.add_paragraph(text)
doc.save(filename)
for i in range(1,4):
makewordfile(text=f'Hello world {i}'
, filename=f'hello{i}.doc')
[OUT]
2-1-2.処理コード:Wordの一括PDF化
処理の流れは下記の通りです。
[IN]
import glob, os
import win32com.client as win32
files = glob.glob('*.doc') #docファイルのみ抽出
print(files)
def doc2pdf(file):
app = win32.Dispatch("Word.Application") #Wordのインスタンスを生成
app.Visible = True #GUIでアプリを表示させる
app.DisplayAlerts = False #警告を表示させない
path_abs = os.path.abspath(file) #絶対パスでないとドキュメントに保存される
path_pdf = path_abs.replace('.doc', '.pdf') #拡張子をdoc->pdfにしたパス(text)を作成
doc = app.Documents.Open(path_abs)
doc.SaveAs(path_pdf, FileFormat=17) #17=PDF
doc.Close()
app.Quit()
for file in files:
doc2pdf(file)
[OUT]
['hello1.doc', 'hello2.doc', 'hello3.doc']
3.Excel
3-1.PDF化:book.ExportAsFixedFormat(0, path)
Excelの一括PDF化は下記記事で紹介しているためコピペを紹介します。
[In]
import win32com.client as win32
import os
#win32に渡すパスは絶対パスで指定
files = globfiles('note_PDF', ['.txt', '.xlsx', '.docx']) #output->['note_PDF.txt', 'note_PDF.xlsx', 'note_PDF.docx']
files_abs = [os.path.abspath(file) for file in files]
app = win32.Dispatch("Excel.Application") #Excelを起動
app.Visible = True
app.DisplayAlerts = False
path_excel = files_abs[2] #Excelの絶対パスを指定 output->'c:\\Users\\KIYO\\Desktop\\note\\02_pythonライブラリ\\2022年01月_note_PDF\\note_PDF.xlsx''c:\\Users\\KIYO\\Desktop\\note\\02_pythonライブラリ\\2022年01月_note_PDF\\note_PDF.xlsx'
path_pdf = path_excel.replace('.xlsx', '.pdf') #上のパスを拡張子.pdfに変更
book = app.Workbooks.Open(path_excel) #Excelファイルを開く
book.ExportAsFixedFormat(0, path_pdf) #PDFに変換※引数0はPDFファイルに変換を指定
app.Quit() #Excelを終了
3-2.Sharepointへファイル保存:book.SaveAs(url)
Office365の使用においてデータ保管をクラウド上のSharepointでしている場合、ローカルPCにあるExcelファイルはbook.SaveAs()を用いて可能です。この操作のポイントは下記の通りです。
[IN]
import win32com.client as win32
import os
url_share='https://<会社指定のやつ>.sharepoint.com/sites/<ホーム名>/Shared%20Documents/<指定ディレクトリ>'
filepath = 'Book1.xlsm' #追って絶対パスに変換
app = win32.Dispatch("Excel.Application") #Excelを起動
app.Visible = True #Excelを表示:エラー時にGUIで消せるようにする
app.DisplayAlerts = False #警告を表示させない
book = app.Workbooks.Open(os.path.abspath(filepath)) #Excelファイルを開く
book.SaveAs(os.path.join(url_share, filepath)) #保存
app.Quit() #Excelを終了book
[OUT]
4.Outlook
pywin32でOutlookを使用する場合は"win32.Dispatch("Outlook.
Application").GetNamespace("MAPI")"でインスタンスを作成して”GetDefaultFolder(<フォルダ番号>)”で使用するアイテムを選択します。
フォルダ番号は使用する分だけ辞書にすると選択しやすくなります。
[IN]
import win32com.client as win32
foldernames = {'Deleted': 3, #削除済みアイテム
'SentItems': 5, #送信済みアイテム
'Inbox': 6, #受信トレイ
'Calender': 9, #カレンダー
}
outlook = win32.Dispatch("Outlook.Application").GetNamespace("MAPI")
num_Folder = foldernames['Inbox'] #メールボックスを指定
folder = outlook.GetDefaultFolder(num_Folder) #指定のフォルダを取得
items = folder.Items #Itemsプロパティ:フォルダ内のアイテムを取得
print(items.Application)
[OUT]
Outlook
4-1.カレンダー:GetDefaultFolder(9)
Win32を使用することでOutlookのカレンダー情報を抽出できます。
自宅PCはOutlook2010しかなくまともに使っていないため、サンプルスケジュールを入れてみました。
上図の予定表から各予定の情報を抽出しました。
[IN]
import win32com.client
import datetime
import pandas as pd
def get_calender(num=9):
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
folder = outlook.GetDefaultFolder(num) # 9=Outlookの予定表
return folder
calender = get_calender(num=9)
print(calender)
print('データ型:', type(calender))
items = calender.Items # itemsは登録された予定データ
print(items)
print('予定データ数', len(items))
print('データ型:', type(items))
# 予定を抜き出したい期間を指定
start_date = datetime.date(2022, 6, 6)
end_date = datetime.date(2022, 6, 10)
def get_schedules(items, start_date, end_date):
select_items = [] # 指定した期間内の予定を入れるリスト
for item in items:
if start_date <= item.start.date() <= end_date:
select_items.append(item)
return select_items
schedules = get_schedules(items, start_date, end_date)
print(schedules)
print(dir(schedules[0]))
# 抜き出した予定の詳細を表示
columns = ['開始時刻', '終了時刻', 'タイトル', '場所', '本文']
datas = [[datetime.datetime.strftime(sch.start, '%Y/%m/%d'), datetime.datetime.strftime(sch.end, '%Y/%m/%d'),
sch.subject, sch.location, sch.body] for sch in schedules]
df = pd.DataFrame(datas, columns=columns)
df
[OUT]
予定表
データ型: <class 'win32com.client.CDispatch'>
<COMObject <unknown>>
予定データ数 6
データ型: <class 'win32com.client.CDispatch'>
[<COMObject <unknown>>, <COMObject <unknown>>, <COMObject <unknown>>, <COMObject <unknown>>, <COMObject <unknown>>, <COMObject <unknown>>]
['_ApplyTypes_', '_FlagAsMethod', '_LazyAddAttr_', '_NewEnum', '_Release_', '__AttrToID__', '__LazyMap__', '__bool__', '__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__int__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_builtMethods_', '_enum_', '_find_dispatch_type_', '_get_good_object_', '_get_good_single_object_', '_lazydata_', '_make_method_', '_mapCachedItems_', '_oleobj_', '_olerepr_', '_print_details_', '_proc_', '_unicode_to_string_', '_username_', '_wrap_dispatch_']
4-2.メール情報:GetDefaultFolder(3~6)
メールボックス(受信、送信、削除済み)の情報を取得するにはFolder No.を3~6で指定します。今回サンプルとして受信トレイ(6)を使用します。
本コードのポイントは下記のとおりです。
下記コードで受信トレイのインスタンス作成後のメール数を確認しました。結果として受信トレイには7個のメールが残っています。
[IN]
import os
import glob
import win32com.client as win32
import pandas as pd
import datetime
# GetDefaultFolderメソッド:Outlook既定のフォルダを取得| '3:削除済みフォルダ、5:送信済みフォルダ、:6:受信トレイ
foldernames = {'Deleted': 3, #削除済みアイテム
'SentItems': 5, #送信済みアイテム
'Inbox': 6, #受信トレイ
'Calender': 9, #カレンダー
}
idx2foldername = {v:k for k,v in foldernames.items()}
outlook = win32.Dispatch("Outlook.Application").GetNamespace("MAPI")
num_Folder = foldernames['Inbox'] #メールボックスを指定
folder = outlook.GetDefaultFolder(num_Folder) #指定のフォルダを取得
items = folder.Items #Itemsプロパティ:フォルダ内のアイテムを取得
print(f'メール数({idx2foldername[num_Folder]}):{items.Count}') #フォルダ内のメール数
[OUT]
メール数(Inbox):7
4-2-1.メールの各項目を取得(属性)
メールの情報抽出は"items(<mail番号>).属性"で取得できます。各項目の属性は下記のとおりです。
参考まで最新メール情報を取得します。"items.Count"を"1"に変更すれば最も古いメールの取得も可能です。
[IN]
sentOn = items(items.Count).SentOn #SentOnプロパティ:メールの送信日時を取得
subject = items(items.Count).Subject #Subjectプロパティ:メールの件名を取得
sentTo = items(items.Count).To #SentToプロパティ:メールの送信先を取得
sentCC = items(items.Count).CC #SentCCプロパティ:メールの送信先CCを取得
sendername = items(items.Count).SenderName #SenderNameプロパティ:メールの送信者名を取得
mailbocy = items(items.Count).Body #Bodyプロパティ:メールの本文を取得
num_attachment = items(items.Count).Attachments.Count #Attachmentsプロパティ:メールの添付ファイルを取得
names_attachment = []
for i in range(num_attachment):
names_attachment.append(items(items.Count).Attachments.Item(i+1).FileName) #FileNameプロパティ:添付ファイルの名前を取得
_name = ['sentOn', 'subject', 'sentTo', 'sentCC', 'sendername', 'mailbody', 'num_attachment', 'names_attachment']
_data = [sentOn.strftime('%Y-%m-%d %H:%M:%S'), subject, sentTo, sentCC, sendername, mailbocy, num_attachment, names_attachment]
[OUT]
sentOn:2022-10-26 15:01:37
subject:【連絡】 設置
sentTo:KIYO
sentCC:KIYO2
sendername:note
mailbody:
本文を記載
num_attachment:10
names_attachment:['image001.jpg', 'image002.png']
関数化すると下記のとおりです。
[IN]
def get_mailbox(items, mailnum=items.Count):
sentOn = items(mailnum).SentOn #SentOnプロパティ:メールの送信日時を取得
subject = items(mailnum).Subject #Subjectプロパティ:メールの件名を取得
sentTo = items(mailnum).To #SentToプロパティ:メールの送信先を取得
sentCC = items(mailnum).CC #SentCCプロパティ:メールの送信先CCを取得
sendername = items(mailnum).SenderName #SenderNameプロパティ:メールの送信者名を取得
mailbocy = items(mailnum).Body #Bodyプロパティ:メールの本文を取得
num_attachment = items(mailnum).Attachments.Count #Attachmentsプロパティ:メールの添付ファイルを取得
names_attachment = []
for i in range(num_attachment):
names_attachment.append(items(mailnum).Attachments.Item(i+1).FileName) #FileNameプロパティ:添付ファイルの名前を取得
return sentOn.strftime('%Y-%m-%d %H:%M:%S'), subject, sentTo, sentCC, sendername, mailbocy, num_attachment, names_attachment
sentOn, subject, sentTo, sentCC, sendername, mailbocy, num_attachment, names_attachment = get_mailbox(items, mailnum=items.Count)
for k, v in {'sentOn':sentOn, 'subject':subject, 'sentTo':sentTo, 'sentCC':sentCC, 'sendername':sendername, 'mailbocy':mailbocy, 'num_attachment':num_attachment, 'names_attachment':names_attachment}.items():
print(f'{k}: {v}')
[OUT]
同上
4-2-2.全コード:全メール取得×DataFrame化
上記のデータ取得を応用してFor文で全メール取得後にPandasでdf化しました。現状では一番下に最新メールが来るため気にならソート可能です。
[IN]
import os
import glob
import win32com.client as win32
import pandas as pd
import datetime
# GetDefaultFolderメソッド:Outlook既定のフォルダを取得| '3:削除済みフォルダ、5:送信済みフォルダ、:6:受信トレイ
foldernames = {'Deleted': 3, #削除済みアイテム
'SentItems': 5, #送信済みアイテム
'Inbox': 6, #受信トレイ
'Calender': 9, #カレンダー
}
idx2foldername = {v:k for k,v in foldernames.items()}
outlook = win32.Dispatch("Outlook.Application").GetNamespace("MAPI")
num_Folder = foldernames['Inbox'] #メールボックスを指定
folder = outlook.GetDefaultFolder(num_Folder) #指定のフォルダを取得
items = folder.Items #Itemsプロパティ:フォルダ内のアイテムを取得
print(f'メール数({idx2foldername[num_Folder]}):{items.Count}') #フォルダ内のメール数
def get_mailbox(items, mailnum=items.Count):
sentOn = items(mailnum).SentOn #SentOnプロパティ:メールの送信日時を取得
subject = items(mailnum).Subject #Subjectプロパティ:メールの件名を取得
sentTo = items(mailnum).To #SentToプロパティ:メールの送信先を取得
sentCC = items(mailnum).CC #SentCCプロパティ:メールの送信先CCを取得
sendername = items(mailnum).SenderName #SenderNameプロパティ:メールの送信者名を取得
mailbocy = items(mailnum).Body #Bodyプロパティ:メールの本文を取得
num_attachment = items(mailnum).Attachments.Count #Attachmentsプロパティ:メールの添付ファイルを取得
names_attachment = []
for i in range(num_attachment):
names_attachment.append(items(mailnum).Attachments.Item(i+1).FileName) #FileNameプロパティ:添付ファイルの名前を取得
return sentOn.strftime('%Y-%m-%d %H:%M:%S'), subject, sentTo, sentCC, sendername, mailbocy, num_attachment, names_attachment
def get_mailbox_df(items):
columns=['sentOn', 'subject', 'sentTo', 'sentCC', 'sendername', 'mailbocy', 'num_attachment', 'names_attachment']
for i in range(items.Count):
datas = get_mailbox(items, i+1)
if i == 0:
df = pd.DataFrame([datas], columns=columns)
else:
df = df.append(pd.DataFrame([datas], columns=['sentOn', 'subject', 'sentTo', 'sentCC', 'sendername', 'mailbocy', 'num_attachment', 'names_attachment']))
return df
get_mailbox_df(items).reset_index(drop=True)
[OUT]
5.File System Object(FSO)
VBAでも使用したFSOを紹介します。
5-1.ファイル情報の簡易取得
FSOを使用してファイル情報を取得します。参考ファイルとして"sample.pdf"を使用しました。
[IN]
import win32com.client as win32
fso = win32.Dispatch("Scripting.FileSystemObject") #FSO
filepath = 'sample.pdf' #参考用PDF
print('fso.GetBaseName(filepath):', fso.GetBaseName(filepath)) #ファイル名
print('fso.GetExtensionName(filepath):', fso.GetExtensionName(filepath))
print('fso.GetFile(filepath).DateCreated:', fso.GetFile(filepath).DateCreated)
print('fso.GetFile(filepath).DateLastAccessed:', fso.GetFile(filepath).DateLastAccessed)
print('fso.GetFile(filepath).DateLastModified:', fso.GetFile(filepath).DateLastModified)
print('fso.GetFile(filepath).Drive:', fso.GetFile(filepath).Drive)
print('fso.GetFile(filepath).ParentFolder:', fso.GetFile(filepath).ParentFolder)
print('fso.GetFile(filepath).Size:', fso.GetFile(filepath).Size)
print('fso.GetFile(filepath).Type:', fso.GetFile(filepath).Type)
[OUT]
fso.GetBaseName(filepath): sample
fso.GetExtensionName(filepath): pdf
fso.GetFile(filepath).DateCreated: 2022-10-27 08:43:34+00:00
fso.GetFile(filepath).DateLastAccessed: 2022-10-27 14:13:08+00:00
fso.GetFile(filepath).DateLastModified: 2022-10-27 08:43:34+00:00
fso.GetFile(filepath).Drive: c:
fso.GetFile(filepath).ParentFolder: C:\Users\KIYO\Desktop
fso.GetFile(filepath).Size: 106176
fso.GetFile(filepath).Type: Adobe Acrobat Document
5-2.ファイル情報の詳細:fso.GetFile()
fso.GetFile(path)でファイルオブジェクトを作成したうえで、各種プロパティを使用することで詳細情報の取得が可能です。
[IN]
filedata = fso.GetFile(filepath) #ファイル情報を取得
print('filedata.Attributes:' ,filedata.Attributes) #'ファイル属性
print('filedata.DateCreated:' ,filedata.DateCreated) #'ファイル作成日
print('filedata.DateLastAccessed:' ,filedata.DateLastAccessed) #'ファイルの最終アクセス日
print('filedata.DateLastModified:' ,filedata.DateLastModified) #'最終更新日
print('filedata.Drive:' ,filedata.Drive) #'ドライブのドライブ文字
print('filedata.Name:' ,filedata.Name) #'ファイル名
print('filedata.ParentFolder:' ,filedata.ParentFolder) #'親フォルダ
print('filedata.Path:' ,filedata.Path) #'ファイルパス
print('filedata.ShortName:' ,filedata.ShortName) #'ファイルの短い名前(8.3形式)
print('filedata.ShortPath:' ,filedata.ShortPath) #'ファイルの短いパス(8.3形式)
print('filedata.Size:' ,filedata.Size) #'ファイルサイズ [byte]
print('filedata.Type:' ,filedata.Type) #'ファイルの種類
[OUT]
filedata.Attributes: 32
filedata.DateCreated: 2022-10-27 08:43:34+00:00
filedata.DateLastAccessed: 2022-10-27 14:13:08+00:00
filedata.DateLastModified: 2022-10-27 08:43:34+00:00
filedata.Drive: c:
filedata.Name: sample.pdf
filedata.ParentFolder: C:\Users\KIYO\Desktop
filedata.Path: C:\Users\KIYO\Desktop\sample.pdf
filedata.ShortName: sample.pdf
filedata.ShortPath: C:\Users\KIYO\Desktop\sample.pdf
filedata.Size: 106176
filedata.Type: Adobe Acrobat Document
5-3.ファイルのコピー:fso.CopyFile()
ファイルをコピーする場合はfso.CopyFile(<コピー元>,<コピー先>, bool)メソッドを使用します。
[IN]
fso.CopyFile(filepath, "sample2.pdf", True) #ファイル名を指定してコピー
[OUT]
sample2.pdf ファイルが作成(コピー)される。
参考資料
あとがき
いろいろな操作はできると思いますがとりあえず公開します。
とりあえずさっさと「週報作成×Outlookカレンダー」を掛け合わせて「予定表から週報作ってみた」を作りたい。