fum125’s diary

IT全般に関する技術メモなど

Raspberry Pi に挑戦

ラズパイを買ったはいいが

Python つながりという訳でないのですが、出張で秋葉原に寄った際にフラッと『Raspberry Pi Zero WH』を衝動買いしてしまいました。
安いし、Linux が入る小型コンピュータということで、何か色々と遊べるかなーという軽い気持ちで購入。
しかし、OSブートさせてしばらく遊んでみると、普段使っている Linux と代り映えせず、早くもちょっと飽きが。

そこで、折角なので、ラズパイに何か繋げてみよーかなーと思い、少しだけ電子工作に足を踏み込んでみました。

知識ゼロ

とはいえ、文系プログラマーにとって、電気の知識は中学校の理科程度しかありません。
覚えているのは "E=IR" ぐらいかな。
書籍や Web サイトに紹介されている通りのものは組めますが、自力で解決したり、応用を利かせるなんてことは全くできません。

そんな訳で、電気系に詳しい方から見ると「なんじゃそりゃ」的なことをたくさんやらかすかもしれませんが、失敗談も含めて記録を残せたらなーと思います。

さっそく Lチカ (Lピカ) から

詳細説明は不要と思いますが、早速「プログラムの Hello World」的な、初歩の初歩であるLチカからやってみました。
ここで早くも壁が。。。

  • LEDって何種類かあるけど、どれ買えばいいの?
  • 抵抗って、どれ買えばいいの?

出張の機会に秋月電子に行ってみたのですが、初心者ゆえ、どれを買えばよいのか迷いました。
結局、無駄買いかもしれませんが、数種類まとめて買っておきました。
地元だと、電子パーツを売っているお店が全くありませんので、こういう機会じゃないと買えません。通販は送料がかかるし。

出張から帰って、早速実験。
Lチカは無事に成功しました。
(壊して ラズパイが動かなくなったらどうしよう、と初めての接続はドキドキものでした)

Outlook2016 を Python から制御してみる (7) - 最終回

次に作ってみたいもの

学習開始からここまでたどり着くまでに2年かかってました。。。
次に食指が動くのにどれぐらいかかるか分かりませんが、今のところ、挑戦してみようかなぁと思っているものを挙げておきます。

Outlook 外部エディタ

むかしから Emacs 使いだったため、Outlook でメールを書く際に以下のようなイライラを感じています。

  • 等幅フォントで字を揃えたいのに、微妙にずれる。
  • 左側に2文字分のインデントを自動で付けて、行頭を揃えたい。
  • 80文字ぐらいで行を自動折り返ししたい。ただし、エディタ任せではなくて自分で制御しながら。

そこで、メール作成中は Emacs を外部エディタとして使いたい。

プログラムの構想はこんな感じです。

  1. メール作成画面は、通常通り Outlook で起動する。
  2. ボタンか、またはショートカットキーを押すと、Python スクリプトが動き始める。
  3. Python スクリプトでは、メール作成画面の Body や To, Cc を読み取って、tmp テキストファイルに書き出す。そのあと、tmp テキストファイルを引数にして、Emacs を起動する。
  4. Emacs 上でメール編集を行い、終わったら書き込み終了する。
  5. PythonEmacsの終了を検知したら、tmp ファイルの内容を Outlook メール作成画面にコピーする。
メール重複削除

Outlook 内に重複して存在するメールを削除するツールです。
自作する利点は、「重複の判定ルールを、自分自身のお好みで決められる」ことかと。

※ 2018/6月現在、自分用として使えるレベルにはなっていますが、まだ試験中。
大事なメールをバグで削除するわけにはいきませんので、公開はもう少し先に。。。

今のところは以下のような処理で動かしています。

(1) メールヘッダから Message-ID を取得します。
  メールヘッダの取得には、PropertyAccessor を使ってヘッダ全体を取得した後に、改行区切りで "Message-ID" の部分を拾っています。
  また、大文字小文字が混在することがあるので、全て小文字に変換かけてから処理しています。
(2) Message-ID をキーにして、辞書を作ります。
(3) 既に辞書に同一キーがあれば、「それは重複メールである」と判断しています。
(4) いきなりメール削除するのではなく、一度「重複メールフォルダ」に移動しています。
  Python で処理するのはここまで。
  本当に削除するのは、手動にしています。

自動タグ付

メール内容から、自動でタグ (分類項目) をつけていくようにします。

もしくは、一度手動でつけたタグを覚えておき、そのメールの Message-ID を References に持つメールを受け取ると、同じタグをつけていきます。

google 連携

Google タスクや Google カレンダーの内容と Outlook の内容を同期させるツールです。

Outlook2016 を Python から制御してみる (6)

さて、今回は作成した python スクリプトを呼び出してみたいと思います。

作成するスクリプトについて

次のような動きをするスクリプトを作ってみます。

(1) まず、outlook 上でメールを選択しておきます。
(2) リボン上に用意したボタンをクリックします。
(3) ボタンに割り付けたアクションとして、「マクロ」を実行します。
(4) マクロから、今回作成する Phthon スクリプトを実行します。
(5) ここから Python 本体の処理。
  (5-1) Outlook 上で選択されているメールの一覧を調べます。
  (5-2) 各メールの送信日付を調べます。
  (5-3) "アーカイブ" というフォルダを作り、年月ごとにファイルを移動します。

f:id:fum125:20180615233820p:plain

では、ステップバイステップで作ってみます。

アーカイブ場所を作る

アーカイブ場所はどこでも良いのですが、今回は "Archive.pst" を作って、ここに保存するようにします。
あらかじめ、Outlook から [ファイル] - [情報] - [アカウント設定] と進み、 "Archive" という名前のデータファイルを新規に作っておきました。

f:id:fum125:20180616084548p:plain

Outlook のフォルダに "Archive" が作られました。

f:id:fum125:20180616110553p:plain

python スクリプトを作る

次に、pythonスクリプトを先に作ってみます。

まずはお決まりの宣言から
import win32com.client

win32 = win32com.client.Dispatch('Outlook.Application')
mapi = win32.GetNamespace('MAPI')

これで、Outlook にアクセスするための MAPI を取りました。

選択されているメールの一覧を取得する

次に、現在選択されているメールの一覧を取得してみます。
対象は1個のメールだけではなく、複数選択されていても大丈夫です。

デバッグとして、取得できたメール一覧のサブジェクトも表示してみます。

# 選択されているメールの一覧を、リスト形式で取得する。
items = win32.ActiveExplorer().Selection

# 順番にサブジェクトを表示してみる。
for item in items:
    print(item.Subject)

「いま選択されているメール」は、MAPI ではなく、win32 の方から取得できました。

対象メールの送信日付を調べる。

送信日付は、CreationTIme で取得できました。
先ほどの for ループ内で、作成年月を取ってきます。

for item in items:
    ctime = item.CreationTime
    print('create time => %d / %d' % (ctime.year, ctime.month))
アーカイブフォルダ配下に、年月フォルダを作成する。

アーカイブフォルダは、事前に MAPI を使って取得しておきます。

# メール保存先を用意しておく。
archFolder = mapi.Session.Folders['Archive']

先ほどの for ループの延長です。
アーカイブフォルダ配下に "年_月" という名前のフォルダを作ります。

for item in items:
    ctime = item.CreationTime
    print('create time => %d / %d' % (ctime.year, ctime.month))
    
    # メール保存先を決定する。なければ作成する。
    des = '%d_%d' % (ctime.year, ctime.month)
    try:
        desFolder = archFolder.Folders[des]
    except:
        desFolder = archFolder.Folders.Add(des)
メールを移動する

対象メールを、アーカイブ配下の "年_月" フォルダに移動します。

    item.Move(desFolder)
ちょっと修正

ひとまずこれで動くようになりましたが、もしかしたら items はリストの後ろから処理するほうがいいのかもしれません。

for item in reversed(items):
完成

ここまでで、python スクリプトは完成です。
まとめると、こんな感じです。

import win32com.client

win32 = win32com.client.Dispatch('Outlook.Application')
mapi = win32.GetNamespace('MAPI')

# メール保存先を用意しておく。
archFolder = mapi.Session.Folders['Archive']


# 選択されているメールの一覧を、リスト形式で取得する。
items = win32.ActiveExplorer().Selection

# 順番にサブジェクトを表示してみる。
for item in reversed(items):
    ctime = item.CreationTime
    print('create time => %d / %d' % (ctime.year, ctime.month))
    
    # メール保存先を決定する。なければ作成する。
    des = '%d_%d' % (ctime.year, ctime.month)
    try:
        desFolder = archFolder.Folders[des]
    except:
        desFolder = archFolder.Folders.Add(des)

    item.Move(desFolder)

pythonを実行するマクロを用意する

Outlook でマクロが使えるようになるには、自己証明書を用意したり、いろいろと面倒な手続きがあります。
そのあたりの手順は別の機会で。

今回は、自作したPythonスクリプトを呼び出す Outlook マクロを呼び出すところから紹介します。

Outlook マクロを "Public Sub ~" で宣言して、適当な名前の関数を用意します。
関数の引数はありません。

Public Sub Archive_Mail()

End Sub

この関数の中に、python を実行するコマンド行を記述します。

Public Sub Archive_Mail()
    With CreateObject("Wscript.Shell")
        .Run "C:\annaconda3\python.exe D:\work\Archive_Mail.py", 5
    End With
End Sub

WSH の Run() の引数に "5" を指定していますので、上記の場合は動作確認用に、コマンドプロンプトの黒い画面が現れます。
この WSH のメソッド解説や引数の意味は atmarkit さんの記事が詳しかったので、ご参照ください。
http://www.atmarkit.co.jp/ait/articles/0407/08/news101_2.html

マクロを呼び出すボタンを作る

いよいよ最後のステップです。

Outlook 画面のリボンの適当な場所を右クリックし、[リボンのユーザ設定...] を選びます。

f:id:fum125:20180617215030p:plain

リボンのユーザー設定画面が表示されたら、コマンドの選択で [マクロ] に絞り込み、先ほど作成したマクロを選択します。

次に リボンのユーザ設定から、ボタンを配置したい場所を選びます。

最後に [追加>>] をクリックして終了です。

f:id:fum125:20180617220138p:plain

最後に

以上ですべての準備が終わりました。

Outlook 上のメールを選択 (複数選択も可) し、リボン上のボタンをクリックすると、
メールが Archive フォルダに年月に分類保存されます。

Outlook2016 を Python から制御してみる (5)

きっと、知っている人は知っている。知らない素人の私は意味が分からなくて苦労した PropertyAccessor によるアクセスです。

PropertyAccessor って?

自分なりの解釈では、「MailItem クラスにあらかじめ用意されていないプロパティにアクセスしたい場合に使用するもの」、「法則がよく分からないけど16進数コードで指定する」というものです。

とにかく使ってみましょう

https://msdn.microsoft.com/ja-jp/vba/outlook-vba/articles/referencing-properties-by-namespace

上記に仕様が公開されているのですが、やっぱり読んでも使い方がよく分かりません。
Outlook マクロとして公開されているサンプルを参考に、ちょっとやってみます。

PROPNAME = "http://schemas.microsoft.com/mapi/proptag/0x007D001E"

for item in items:
    pa = item.PropertyAccessor
    header = pa.GetProperty(PROPNAME)
    print(header)                               #-> メールのヘッダが表示される。

上記のソースで、メールヘッダが全て取得できます。

0x007D001E 以外の指定可能な値に何があるか? が分からず、
サイトで何気に見つけた例 "0x39FE001E" を指定してもエラーとなったため、
この全ヘッダー出力の例以外には、まだ成功していません。

Outlook2016 を Python から制御してみる (4)

前回は、MailItem のサブジェクト (Subject) を表示してみました。
今回は、MailItem の色々なプロパティにアクセスしてみます。

単純に値を参照したり設定すれば良いものもあれば、お作法が必要だったりするものもあったため、実験してみて気づいたものを記載しておきます。

ちなみに、MailItem のクラス説明は以下のサイトを参考。
https://msdn.microsoft.com/ja-jp/vba/outlook-vba/articles/mailitem-object-outlook

送信者 (from)

for item in items:
    print(item.Sender)                         #-> "ほげほげ太郎"
    print(item.SenderEmailAddress)             #-> "aaa@bbb.ne.jp" 

"aaa@bbb.ne.jp" のようなメールアドレスは "SenderEmailAddress" で参照できました。
もう一つの "Sender" は、メールアドレスではなく表示名が表示されました。

宛先 (to/cc)

送信先・宛先を調べるために To や Cc を使ってみたところ、表示名は取れますが、送信先メールアドレスが取得できません。

for item in items:
    print(item.To)                       #-> "ほげほげ次郎"

送信先メールアドレスは、 Recipients に入っていました。
複数の宛先に送られることもありますので、Recipients はリストになっています。

for item in items:
    print(item.To)                      #-> "ほげほげ次郎"

    for rcpt in item.Recipients:
        print(rcpt.Address)             #-> "xxx@yyy.com"

To なのか Cc なのかは、Recipient の Type から調べるようです。

件名 (Subject)

    print(item.Subject)

件名は、Subject で参照できます。

分類 (タグ)

Outlook で、gmail の "ラベル" のような使い方をしたい場合は、"Categories" を使用します。

    print(item.Categories)

複数の分類項目が指定されているときは "," (カンマ) 区切りになっていました。
新たに分類項目を追加したい場合、Categories に「 "," + 項目名」を追加すれば良いです。

既に指定済の項目を重複して追加した場合、分類項目が重複して表示される場合と、指定済項目が考慮されて重複しない場合があり、まだその違いが分かりません。

(例)
item.Categories = "AAA, BBB, CCC"           #-> いま、AAA, BBB, CCC の3つの分類項目がある。
item.Categories = item.Categories + ", CCC"
print(item.Categories)                               #-> CCC が重複になる場合と、ならない場合があった。

本文

メール本文は "Body" で参照できます。

    print(item.Body)

フラグ

フラグがついていると IsMarkedAsTask が True になります。
この場合は、TaskDueDate (期限) や TaskCompletedDate (完了日) を参照/設定することができます。

IsMarkedAsTask は読み込み専用です。
フラグを付けたいときは MarkAsTask() メソッドを使うようですが、これはまだ試していません。

そのほか

あらかじめ用意されているもの以外にアクセスしたい場合、PropertyAccessor を使います。
PropertyAccessor の実験は次回に。

Outlook2016 を Python から制御してみる (3)

2年ぶりの更新です。
前回、フォルダにアクセスするところまで記載しました。
今回は、フォルダ配下にあるメールにアクセスしてみます。

フォルダ配下のメールにアクセスする

前回の実験で、デフォルトの受信フォルダにはアクセスできました。
今回は、その他のフォルダへのアクセスを試してみます。

f:id:fum125:20180607231607p:plain

いま、"folderA" の下には2件のメールがあるという想定です。
(テスト用に、 "下書きメール" 2件を置いています)

まずは "folderA" にアクセスする。

前回までのおさらいで、まずは "folderA" にアクセスします。

import win32com.client

object = win32com.client.Dispatch('Outlook.Application')
mapi = object.GetNamespace('MAPI')
inbox = mapi.GetDefaultFolder(6)

foldera = inbox.Folders['folderA']
print(foldera.Name)                          # -> "folderA"

これで、変数 foldera は "folderA" を指していることが確認できました。

次に、Items を使って、folderA 配下にあるアイテムを取得してみます。

items = foldera.Items
print(len(items))                # -> 2

folderA 配下には2個のアイテムがあるぞ、ということが分かりました。

フォルダの下には何がある?

フォルダの下にあるもの。
今回は "メール" であることは明白ですが、もしかしたらフォルダには "会議出席依頼" が入っているかもしれません。
会議出席依頼を "メール" (MailItem) クラスとして扱うと異常終了することがあったので、念のためにメールであることを確認しておきます。

アイテムの種別の説明に2つのサイトが見つかりましたが、どっちの見方をすればいいのでしょう?

(a) https://msdn.microsoft.com/ja-jp/vba/outlook-vba/articles/olitemtype-enumeration-outlook
(b) https://msdn.microsoft.com/ja-jp/VBA/Outlook-VBA/articles/olobjectclass-enumeration-outlook

実験では、"メール" のアイテムに対して "43" という数値が得られたので、(b) の方法を利用するようにしました。

foldera = inbox.Folders['folderA']
items = foldera.Items

for item in items:
    print(item.Class)                #-> メールの場合は 43 が得られた。
    
    if (item.Class == 43):
        print("これはMailItemです")
    else:
        print("それ以外")

MailItem のプロパティを表示してみる

フォルダの下にあるアイテムが "MailItem" であることが分かったら、次は MailItem の色々なプロパティを表示してみたり、値を上書きしてみたりします。

MailItem のプロパティ一覧は、ここを参考にしました。
https://msdn.microsoft.com/ja-jp/vba/outlook-vba/articles/mailitem-object-outlook

試しに、サブジェクト一覧を表示してみます。

for item in items:
    print(item.Subject)     #-> 各メールのサブジェクトが表示されます。

※ 事前のクラスチェックは省略。すべて MailItem だという前提にしています。

「参照しかできないもの」「上書きできるもの」などプロパティごとに異なるので、注意が必要ですね。

次回は、MailIem のプロパティを色々を触ってみたいと思います。

Outlook2016 を Python から制御してみる (2) - 旧ページ

ご訪問いただきありがとうございます。

google 検索で、この URL が上位にヒットしているようですが、Hatena Blog に慣れない頃に作ったページで、HTMLが崩れて修復不能になってしまいました。

新たに以下のページに記事をコピーしています。

「Outlook2016を Python から制御してみる」は、以下のリンクをご覧ください。

 

fum125.hatenablog.com

 

fum125.hatenablog.com

fum125.hatenablog.com

fum125.hatenablog.com

fum125.hatenablog.com

fum125.hatenablog.com

fum125.hatenablog.com