1. はじめに
前任者がPAD(Power Automate Desktop)で構築した業務フローを、保守性と安定性の観点からPythonへ移行しています。
しかし、いざ実装を始めると現場特有の「地雷」が次々と爆発します。
本記事では、特に手強かった 「ガチガチに作り込まれた既存Excelフォーマット」 へのデータ流し込みと、 「APIが使えない制約下でのOutlook操作」 をどう攻略したか、泥臭くも実践的な解決策を全記録として公開します。
2. 遭遇した課題と解決のプロセス
① 「ファイルがあるのに見つからない」パス解決の罠
現象: FileNotFoundError: [Errno 2] No such file or directory が発生。
data/templates/ 配下にファイルは確実に存在するのに、VS Codeの実行環境(カレントディレクトリ)のズレによりファイルが特定できない現象に悩まされました。
解決策: 相対パスやカレントディレクトリに頼るのをやめ、__file__ を基点にプロジェクトルートを自動特定する「盤石な配線」を実装しました。
# src/automation/excel_task.py
from pathlib import Path
class ExcelAutomator:
def __init__(self, config):
self.config = config
# 実行ファイルの位置からプロジェクトルートを絶対パスで特定
# 例: src/automation/ から2階層上がればルートディレクトリ
self.project_root = Path(__file__).resolve().parents[2]
def create_from_format(self, format_key):
# 設定ファイルの相対パスを、プロジェクトルートからの絶対パスに結合
f_info = self.config["excel_formats"][format_key]
template_path = self.project_root / f_info["template"]
# これで、どこからスクリプトを実行しても確実にファイルを特定可能
② 結合セルへの書き込みエラー(Pandasの限界)
現象: pandas の to_excel で既存フォーマットに書き込もうとした際、AttributeError: 'MergedCell' object attribute 'value' is read-only が発生。
原因: 実務の帳票で多用される「悪魔の結合セル」に対し、データ分析ツールのPandasが構造保護のために拒絶反応を示したためです。
解決策: Pandasでの一括処理を諦め、Excel操作に特化した openpyxl で1セルずつ丁寧に「ピンポイント注入」する方式に変更しました。
# openpyxlによる結合セルへの精密書き込み
from openpyxl import load_workbook
def write_to_template(self, template_path, output_path, mapping_data):
wb = load_workbook(template_path)
ws = wb.active
# 結合セルであっても、その範囲の「左上のアドレス(B2など)」を指定すれば
# openpyxlはエラーを出さずに値を保持してくれる
for cell_address, value in mapping_data.items():
ws[cell_address] = value
wb.save(output_path)
③ 組織アカウント環境でのOutlook(Web)操作
現象: Microsoft GraphなどのAPIが組織のセキュリティポリシーで使用不可。残された道はブラウザ(RPA的)操作のみ。
解決策: Playwright を採用し、DOM要素(ボタンのクリックなど)ではなく 「ショートカットキー」のエミュレート に全振りしました。
SaaSのUI(ボタンの位置やクラス名)は予告なく変更されますが、ショートカットキー(Nキーで新規作成など)は不変です。
これにより、UI変更に強い堅牢な自動化が実現しました。
# Playwrightによるキーボード操作のエミュレート
def send_report_mail(page, recipient, subject, body):
page.goto("https://outlook.office.com/mail/")
# 読み込み完了の目安として要素を待機
page.wait_for_selector("text=新規メール")
# DOMをクリックせず、ショートカットキーを駆使して操作
page.keyboard.press("n") # 新規作成
page.keyboard.type(recipient) # 宛先
page.keyboard.press("Tab")
page.keyboard.press("Tab")
page.keyboard.type(subject) # 件名
page.keyboard.press("Tab")
page.keyboard.type(body) # 本文
page.keyboard.press("Control+Enter") # 送信
3. 運用を楽にする「安全設計」のアイディア
1. セル住所の外部定義 (JSON Mapping)
「どのデータをどのセルに書くか」をPythonコード内に直書きせず、JSONで外部管理しました。
「フォーマットが更新されてB2からC2にズレた」という現場あるあるな変更にも、非エンジニアでもJSONの書き換えだけで即応できます。
{
"excel_formats": {
"activity_registration": {
"template": "data/templates/format_v1.xlsx",
"mapping": {
"date": "B2",
"sales_rep": "D2"
}
}
}
}
2. ログ管理を「メール送信」へ昇華
「ログファイルを書き出しても、エラーが起きるまで誰も見ない」という実態を踏まえ、実行結果を自分宛にメール送信する 機能を実装しました。
- 即時性: 処理の成功・失敗がスマホやPCにプッシュ通知で届く。
- 証跡管理: メール履歴がそのまま「いつ何を処理したか」のエビデンスになる。
- 開発効率: VS Codeの全体検索でログファイルの中身が「ノイズ」として引っかからなくなる。
4. おわりに
モノづくりの現場と同じく、ソフトウェア開発においても「堅牢性の確保(エラーハンドリング)」と「ディレクトリ構造の抽象化(パス管理の集約)」が欠かせません。
PADからの移行は、単にツールを置き換える作業ではなく、Pythonの柔軟性を活かして「よりメンテナンスしやすく、壊れにくい仕組み」へと業務フローを再設計するプロセスです。
同じように社内の自動化で戦っている方の参考になれば幸いです!
技術スタック:
- Python 3.10
- PySide6 (GUI)
- Playwright (Browser Automation)
- openpyxl (Excel Engine)