Last active
January 25, 2025 12:58
-
-
Save gamebox777/61a5ffd460e727f709b2b3a7d036076f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| このプログラムの概要: | |
| このプログラムは、Postfixメールログファイルを解析し、メールアドレス、日時、ステータス、およびステータスコードを抽出し、Excelファイルに保存するツールです。 | |
| 主にメール送信状況の監視やトラブルシューティングを目的としています。 | |
| 【プログラムの主な機能】 | |
| 1. ファイル選択: ユーザーがGUIファイルダイアログを使用してPostfixログファイル(.log)を選択。 | |
| 2. データ抽出: | |
| - メールアドレスの抽出(例: `[email protected]`) | |
| - 日付・時間の抽出(Postfixログ形式 "Nov 6 22:07:23" など) | |
| - メールステータス(例: "status=sent", "status=bounced" など) | |
| - ステータスコード(例: 450, 421 など) | |
| 3. 抽出データをリストに格納し、Excelファイルに出力。 | |
| 4. エラー処理: | |
| - ログファイルのエンコーディングエラー処理 | |
| - ファイルの読み込みエラー処理 | |
| - データの欠損に対する処理 | |
| 【プログラムの利用方法】 | |
| 1. Pythonをインストールしていない場合は、[公式サイト](https://www.python.org/downloads/)からインストールしてください。 | |
| 2. 必要なライブラリをインストールします。以下のコマンドを実行してください: | |
| 3. プログラムを実行します(ターミナルまたはコマンドプロンプトで実行) | |
| 4. 表示されたファイル選択ダイアログで解析するログファイルを選択します。 | |
| 5. 抽出されたデータがExcelファイル(`email_status_list_YYYYMMDD_HHMMSS.xlsx`)として保存されます。 | |
| 【注意点・留意点】 | |
| - 本スクリプトはPostfixのログフォーマットに基づいています。他のMTA(Sendmail, Exim等)には適用できない可能性があります。 | |
| - ログファイルは UTF-8 エンコーディングである必要があります。 | |
| - 日付フォーマットが「Nov 6 22:07:23」形式であることを前提としています。 | |
| - "sent" のステータスを持つログは内容を省略する仕様になっています。 | |
| - ファイル選択後、処理には時間がかかる場合があります。 | |
| - 出力フォーマット: メールアドレス、日時(M/D H:M:S)、ステータス、ステータスコード、ログ内容。 | |
| 【ライブラリについて】 | |
| - `pandas`: データの処理・操作に使用。 | |
| - `openpyxl`: Excelファイルの作成と書き込みに使用。 | |
| - `tkinter`: ファイル選択のGUIインターフェースを提供(Python標準ライブラリ)。 | |
| """ | |
| import re | |
| import os | |
| from datetime import datetime | |
| from tkinter import Tk, filedialog | |
| import pandas as pd | |
| # メールアドレスの正規表現 | |
| email_pattern = r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' | |
| # 日付と時間の正規表現(例: Nov 6 22:07:23 形式を想定) | |
| datetime_pattern = r'([A-Za-z]{3} {1,2}\d{1,2} \d{2}:\d{2}:\d{2})' | |
| # メールステータスの正規表現(例: status=sent, status=bounced, status=5.1.1 など) | |
| status_pattern = r'status=([\w\.\-]+)' | |
| # ステータスコードの正規表現(例: 450, 421, 550 などのエラーコード) | |
| status_code_pattern = r'\b(4[0-9]{2}|5[0-9]{2})\b' | |
| # 月名を数値に変換する辞書 | |
| month_map = { | |
| "Jan": 1, "Feb": 2, "Mar": 3, "Apr": 4, "May": 5, "Jun": 6, | |
| "Jul": 7, "Aug": 8, "Sep": 9, "Oct": 10, "Nov": 11, "Dec": 12 | |
| } | |
| def convert_datetime_format(log_date): | |
| """ログの日付フォーマットを M/D H:M:S 形式に変換""" | |
| try: | |
| parts = log_date.split() | |
| month = month_map[parts[0]] # 月を数値に変換 | |
| day = int(parts[1]) # 日 | |
| time = parts[2] # 時間(H:M:S) | |
| formatted_date = f"{month}/{day} {time}" | |
| return formatted_date | |
| except (ValueError, IndexError, KeyError): | |
| return "N/A" | |
| # ファイルダイアログの設定 | |
| Tk().withdraw() # GUIウィンドウを非表示にする | |
| script_directory = os.path.dirname(os.path.abspath(__file__)) # スクリプトのディレクトリ | |
| initial_dir = os.path.join(script_directory, 'log') # 初期フォルダを設定 | |
| print("ログファイルを選択してください。") | |
| selected_files = filedialog.askopenfilenames(initialdir=initial_dir, title="ログファイルを選択", filetypes=[("ログファイル", "*.log"), ("すべてのファイル", "*.*")]) | |
| if not selected_files: | |
| print("ログファイルが選択されませんでした。") | |
| else: | |
| print(f"処理するログファイル: {selected_files}") | |
| # 抽出結果を格納するリスト | |
| extracted_data = [] | |
| for log_file in selected_files: | |
| try: | |
| with open(log_file, 'r', encoding='utf-8') as file: | |
| for line in file: | |
| email_match = re.findall(email_pattern, line) | |
| datetime_match = re.search(datetime_pattern, line) | |
| status_match = re.search(status_pattern, line) | |
| status_code_match = re.search(status_code_pattern, line) | |
| if email_match and status_match: | |
| raw_timestamp = datetime_match.group(0) if datetime_match else "N/A" | |
| formatted_timestamp = convert_datetime_format(raw_timestamp) if raw_timestamp != "N/A" else "N/A" | |
| status = status_match.group(1) | |
| # ステータスコードを抽出(見つからない場合は "N/A") | |
| status_code = status_code_match.group(0) if status_code_match else "N/A" | |
| # "sent" の場合はログ内容を追加しない | |
| log_content = line.strip() if status != "sent" else "" | |
| for email in email_match: | |
| extracted_data.append([email, formatted_timestamp, status, status_code, log_content]) | |
| print(f"{email}, {formatted_timestamp}, {status}, {status_code}, {log_content if log_content else 'ログ省略'}") | |
| except UnicodeDecodeError as e: | |
| print(f"エラー: {log_file} のデコード中に問題が発生しました。エンコーディングを確認してください。 - {e}") | |
| except IOError as e: | |
| print(f"エラー: {log_file} を開けませんでした - {e}") | |
| # データをExcelに出力 | |
| if extracted_data: | |
| df = pd.DataFrame(extracted_data, columns=['メアド', '日時', 'ステータス', 'ステータスコード', 'ログ内容']) | |
| current_time = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| output_file = os.path.join(script_directory, f"email_status_list_{current_time}.xlsx") | |
| try: | |
| df.to_excel(output_file, index=False) | |
| print(f"抽出したデータは {output_file} に保存されました。") | |
| except Exception as e: | |
| print(f"エラー: Excelファイルに保存できませんでした - {e}") | |
| else: | |
| print("該当するデータが見つかりませんでした。") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment