3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

python3.7でThunderbirdから保存したEMLファイルの添付ファイル他を抜き出す

Last updated at Posted at 2020-02-05

私は小企業(30名ほど)の総務をしています。
当然、勤怠管理システムなんか導入する金はない、そもそも勤怠管理システムって何?という会社なので、エクセル方眼紙で作ったフォーマットに入力された有休届や休日出勤届などが毎月大量に送られてきます。
そこで、thuderbird(強制)からメールを日付で検索し、それをドラッグ&ドロップで保存し添付ファイルをまとめて抜き出す方法を、勉強がてら考えました。

emlファイルの読み込み

https://qiita.com/denzow/items/a42d344fa343cd80cf86
を参考に、日付を抜き出せるよう下記のように変更

eml_read.py
import sys
import email
from email.header import decode_header
import datetime

class MailParser(object):
    """
    メールファイルのパスを受け取り、それを解析するクラス
    """

    def __init__(self, mail_file_path):
        self.mail_file_path = mail_file_path
        # emlファイルからemail.message.Messageインスタンスの取得
        with open(mail_file_path, 'rb') as email_file:
            self.email_message = email.message_from_bytes(email_file.read())
        self.subject = None
        self.to_address = None
        self.cc_address = None
        self.from_address = None
        self.body = ""
        self.date = None
        # 添付ファイル関連の情報
        # {name: file_name, data: data}
        self.attach_file_list = []
        # emlの解釈
        self._parse()

    def get_attr_data(self):
        """
        メールデータの取得
        """
        result = """\
DATE: {}
FROM: {}
TO: {}
CC: {}
-----------------------
BODY:
{}
-----------------------
ATTACH_FILE_NAME:
{}
""".format(
            self.date,
            self.from_address,
            self.to_address,
            self.cc_address,
            self.body,
            ",".join([ x["name"] for x in self.attach_file_list])
        )
        return result


    def _parse(self):
        """
        メールファイルの解析
        __init__内で呼び出している
        """
        self.subject = self._get_decoded_header("Subject")
        self.to_address = self._get_decoded_header("To")
        self.cc_address = self._get_decoded_header("Cc")
        self.from_address = self._get_decoded_header("From")
        # 変更したところ
        self.date = datetime.datetime.strptime(
            self._get_decoded_header("Date"),
            "%a, %d %b %Y %H:%M:%S %z"
        )

        # メッセージ本文部分の処理
        for part in self.email_message.walk():
            # ContentTypeがmultipartの場合は実際のコンテンツはさらに
            # 中のpartにあるので読み飛ばす
            if part.get_content_maintype() == 'multipart':
                continue
            # ファイル名の取得
            attach_fname = part.get_filename()
            # ファイル名がない場合は本文のはず
            if not attach_fname:
                charset = str(part.get_content_charset())
                if charset:
                    self.body += part.get_payload(decode=True).decode(charset, errors="replace")
                else:
                    self.body += part.get_payload(decode=True)
            else:
                # ファイル名があるならそれは添付ファイルなので
                # データを取得する
                self.attach_file_list.append({
                    "name": attach_fname,
                    "data": part.get_payload(decode=True)
                })

    def _get_decoded_header(self, key_name):
        """
        ヘッダーオブジェクトからデコード済の結果を取得する
        """
        ret = ""

        # 該当項目がないkeyは空文字を戻す
        raw_obj = self.email_message.get(key_name)
        if raw_obj is None:
            return ""
        # デコードした結果をunicodeにする
        for fragment, encoding in decode_header(raw_obj):
            if not hasattr(fragment, "decode"):
                ret += fragment
                continue
            # encodeがなければとりあえずUTF-8でデコードする
            if encoding:
                ret += fragment.decode(encoding)
            else:
                ret += fragment.decode("UTF-8")
        return ret

if __name__ == "__main__":
    result = MailParser(sys.argv[1]).get_attr_data()
    print(result)

フォルダ内のemlファイルの添付ファイルを抜き出すコードを作成

素人なので変数のつけ方などの書き方が変なのはお許しください。

save_attachmentfile.py
import eml_read
import glob
import os
from email.header import decode_header
import datetime

PATH = r"C:\Users\toshi\***" + "\\"  # emlファイルを保存した場所
save_file_path = r"C:\Users\***" + "\\"  # 添付ファイルを保存したい場所


def save_attachmentfile(file_path_list):
    # search_emlからリストを受け取って添付ファイルをsave_pathに保存
    for file_path in file_path_list:
        obj_eml = eml_read.MailParser(file_path)
        from_adderss = obj_eml.from_address[0:3]
        eml_date = obj_eml.date
        print(eml_date)
        # 日付
        str_year = eml_date.strftime("%y")
        str_month = eml_date.strftime("%m").lstrip("0")
        str_day = eml_date.strftime("%d").lstrip("0")
        str_date = str_year + "." + str_month + "." + str_day
        for a in obj_eml.attach_file_list:
            print(type(decode_header(a["name"])[0][0]))
            if type(decode_header(a["name"])[0][0]) == bytes:
                file_name = str_date + from_adderss + decode_header(a["name"])[0][0].decode(decode_header(a["name"])[0][1])
            else:
                file_name = str_date + from_adderss + decode_header(a["name"])[0][0]
            file_name = (
                    file_name
            ).translate(str.maketrans(
                {'<': '', '>': '', '!': '', '/': '', ':': '', '*': '', '"': '', '|': ''}
            ))
            with open(save_file_path + file_name,
                      mode="bw") as f:
                f.write(a["data"])


def search_eml(file_path):
    # emlファイル名のリストを返す
    emlPATHS = []
    filepaths = glob.glob(os.path.join(file_path, '*.eml'))
    for filepath in filepaths:
        emlPATHS.append(filepath)
    filepaths = None
    return emlPATHS


if __name__ == "__main__":
    lst = search_eml(PATH)
    save_attachmentfile(lst)

参考リンク

https://qiita.com/denzow/items/a42d344fa343cd80cf86
https://stackoverflow.com/questions/21711404/how-to-get-decode-attachment-filename-with-python-email
※添付ファイル名のデコードで困ったので

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?