【Python】メール添付のExcelファイルをデータベースに自動インポートするPythonスクリプト

Excel操作

メールで受信したExcelファイルを手動で確認し、データベースにインポートする作業を効率化するために、Pythonスクリプトを利用した自動化システムを構築してみませんか?特に、Windows Server環境でOutlookがインストールされていない場合でも動作するシステムを想定しています。

本記事では、メールの受信、添付ファイルの保存、データのデータベースへの自動インポート、さらにはエラー処理を組み込んだPythonスクリプトについて解説します。


処理の概要

このスクリプトは以下の手順で動作します:

  1. メールの受信:
    • IMAPプロトコルを使用して、指定した件名のメールを検索します。
    • 添付ファイルが含まれるメールを検出します。
  2. 添付ファイルの保存:
    • 添付ファイルをローカルのディレクトリに保存します。
  3. データベースへのインポート:
    • 保存されたExcelファイルを解析し、そのデータをSQLデータベースにインポートします。
  4. エラー処理:
    • インポート処理中にエラーが発生した場合、エラーの詳細を記録し、該当のメールをIMAPサーバー上の「エラーフォルダ」に振り分けます。
  5. メールの整理:
    • 正常に処理が完了したメールは「処理済みフォルダ」に移動します。

プログラム本体

import imaplib
import email
from email.header import decode_header
import os
import pandas as pd
from sqlalchemy import create_engine

# 設定
IMAP_SERVER = 'imap.example.com'
EMAIL_ACCOUNT = 'your_email@example.com'
EMAIL_PASSWORD = 'your_password'
TARGET_SUBJECT = 'Target Subject'  # チェックしたいメールの件名
SAVE_DIR = 'attachments'  # 添付ファイル保存先ディレクトリ
PROCESSED_FOLDER = 'Processed'  # 処理済みメールのフォルダ名
ERROR_FOLDER = 'Error'  # エラーメールのフォルダ名
DB_URL = 'sqlite:///example.db'  # SQLiteの場合。適宜変更 (例: PostgreSQLの場合 'postgresql://user:password@localhost/dbname')

# データベースエンジン作成
engine = create_engine(DB_URL)

# メール受信と添付ファイル保存
def fetch_emails():
    with imaplib.IMAP4_SSL(IMAP_SERVER) as mail:
        mail.login(EMAIL_ACCOUNT, EMAIL_PASSWORD)
        mail.select('inbox')  # メインの受信トレイを選択

        # 対象の件名を持つ未処理メールを検索
        status, messages = mail.search(None, f'SUBJECT "{TARGET_SUBJECT}"')
        if status != 'OK':
            print("No messages found!")
            return

        for num in messages[0].split():
            # メールの内容を取得
            res, msg = mail.fetch(num, '(RFC822)')
            if res != 'OK':
                print(f"Failed to fetch email {num}")
                continue

            try:
                # メールを解析
                msg = email.message_from_bytes(msg[0][1])
                for part in msg.walk():
                    if part.get_content_disposition() == 'attachment':
                        filename = part.get_filename()
                        if filename:
                            filepath = os.path.join(SAVE_DIR, filename)
                            os.makedirs(SAVE_DIR, exist_ok=True)
                            with open(filepath, 'wb') as f:
                                f.write(part.get_payload(decode=True))
                            print(f"Attachment saved: {filepath}")
                            process_excel(filepath)
                            # 正常処理が完了した場合、処理済みフォルダに移動
                            move_to_processed(mail, num)
            except Exception as e:
                print(f"Error processing email {num}: {e}")
                # エラー発生時、エラーフォルダに移動
                move_to_error(mail, num)

# メールを処理済みフォルダに移動
def move_to_processed(mail, email_id):
    move_email(mail, email_id, PROCESSED_FOLDER)

# メールをエラーフォルダに移動
def move_to_error(mail, email_id):
    move_email(mail, email_id, ERROR_FOLDER)

# メールを指定フォルダに移動
def move_email(mail, email_id, folder_name):
    try:
        # フォルダが存在するか確認(なければ作成)
        mail.create(folder_name)
    except imaplib.IMAP4.error:
        pass  # 既にフォルダが存在する場合は無視

    # メールを移動
    result = mail.copy(email_id, folder_name)
    if result[0] == 'OK':
        # 移動後、元のメールを削除
        mail.store(email_id, '+FLAGS', '\\Deleted')
        mail.expunge()
        print(f"Email {email_id} moved to '{folder_name}'.")
    else:
        print(f"Failed to move email {email_id} to '{folder_name}'.")

# Excelファイルの処理
def process_excel(filepath):
    try:
        # ExcelデータをPandas DataFrameとして読み込み
        if filepath.endswith('.xlsx') or filepath.endswith('.xls'):
            df = pd.read_excel(filepath)
        else:
            print(f"Unsupported file format: {filepath}")
            raise ValueError("Unsupported file format")

        # データをデータベースにインポート
        table_name = 'imported_data'
        df.to_sql(table_name, engine, if_exists='append', index=False)
        print(f"Data imported to table '{table_name}' from {filepath}")
    except Exception as e:
        print(f"Failed to process Excel file: {e}")
        raise  # エラーを親関数に再送

if __name__ == '__main__':
    fetch_emails()

導入のメリット

1. 作業時間の短縮

手動でメールを確認し、添付ファイルをダウンロードしてデータベースにインポートする作業は非常に手間がかかります。本スクリプトを導入することで、このプロセスを完全に自動化できます。

2. エラーのトレーサビリティ

データの取り込み時にエラーが発生した場合、問題のあったメールを「エラーフォルダ」に自動で振り分けます。これにより、どのメールに問題があったかを簡単に特定できます。

3. メール管理の効率化

処理済みメールと未処理メールを分けることで、メールボックスの整理整頓が容易になります。重複処理の防止や作業ミスの軽減にもつながります。


プログラムの個別説明

1. メールの受信と検索

IMAPプロトコルを使用し、サーバーに接続して受信トレイから指定の条件に一致するメールを検索します。

status, messages = mail.search(None, f'SUBJECT "{TARGET_SUBJECT}"')

この部分では、件名が TARGET_SUBJECT に一致するメールを検索しています。


2. 添付ファイルの保存

受信したメールから添付ファイルを検出し、ローカルディスクに保存します。

for part in msg.walk():
if part.get_content_disposition() == 'attachment':
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))

get_payload(decode=True) を使い、添付ファイルの内容をデコードして保存します。


3. データベースへのインポート

pandas を利用してExcelファイルをデータフレーム形式で読み取り、SQLデータベースにインポートします。

df = pd.read_excel(filepath)
df.to_sql(table_name, engine, if_exists='append', index=False)

データベース接続は sqlalchemy を利用しており、複数のデータベースタイプ(SQLite, MySQL, PostgreSQLなど)に対応しています。


4. エラー処理

Excelファイルの解析やインポート時にエラーが発生した場合、その例外をキャッチし、該当メールを「エラーフォルダ」に移動します。

except Exception as e:
print(f"Failed to process Excel file: {e}")
raise # エラーを親関数に再送

この部分では、問題のあったファイルやメールを後から調査できるようにしています。


5. メールの整理

処理が成功した場合は「処理済みフォルダ」に、エラーが発生した場合は「エラーフォルダ」にメールを移動します。

def move_to_processed(mail, email_id):
move_email(mail, email_id, PROCESSED_FOLDER)

def move_to_error(mail, email_id):
move_email(mail, email_id, ERROR_FOLDER)

move_email 関数はIMAPコマンドを利用して、メールを指定フォルダに移動します。


実行環境のセットアップ

必要なライブラリ

以下のライブラリをインストールします:

pip install imapclient email openpyxl pandas sqlalchemy

Python環境

Windows Server環境を想定しており、Python 3.7以降を推奨します。

フォルダ構成

ローカルディスクには添付ファイルを保存するためのディレクトリ(例: attachments)を用意してください。


まとめ

本スクリプトは、メールの添付ファイルを自動でデータベースにインポートし、エラーが発生した場合でもメールを整理できるように設計されています。運用の効率化だけでなく、問題発生時の調査も容易に行える点が特徴です。

日常の業務でメール添付のデータ処理にお困りの方は、ぜひこのスクリプトを参考に自動化を検討してみてください。さらなる改善やカスタマイズについてのご相談もお気軽にどうぞ!

おすすめ教材

コメント

タイトルとURLをコピーしました