Claude CodeでRPAを置き換える手順 — Pythonスクリプトへの段階移行
Power Automate DesktopなどのRPAフローを、操作手順の棚卸し、種類別のPython化、新旧の出力比較、並行運転の順でスクリプトに置き換える手順です。
RPAフローをPythonに置き換えるとき、いちばん危ないのは「見た目が似たスクリプトができたので移行完了」と判断することです。フローは動いているのに、出力が少しずつ違う。そのずれは、本番に切り替えた後で気づくのでは遅すぎます。
Claude Codeに向く進め方は、フローを1本ずつ棚卸しし、種類ごとに移行先を決め、旧フローの出力と新スクリプトの出力を比べる確認を先に用意することです。この記事は、Power Automate Desktopのデスクトップフローを例に、その流れを順に示します。例のフローは手順を示すために作った架空のもので、実在の業務や出力ではありません。
前提
- pywinautoはWindows上でしか動きません。Playwrightの部分はOSを問いません
- PythonとpipがPCで使えること
- Claude Codeが導入済みであること。入れ方はClaude Codeのインストールガイドにあります
- 旧フロー(Power Automate Desktop)を、固定の入力で実行できる権限があること
まず1フロー分の操作手順を表に起こす
置き換えの最初の作業は、コードを書くことではなく、フローが何をしているかの一覧づくりです。Power Automate Desktopのデスクトップフローは、Excelやメールのような日常のツールから、レガシーなアプリ、ターミナルエミュレーターまでを対象にし、画面のUI要素、画像、座標のいずれかで操作します。同じ「クリック」でも、何を手がかりにしているかで移行のしやすさがまるで違います。
フロー1本につき、次の列を持つCSVを作ります。
| 列 | 内容 |
|---|---|
| step | 内容操作の順番 |
| action | 内容元のアクション名(「Webページのボタンをクリック」など) |
| target | 内容操作対象(URL、ウィンドウ名、ファイルパス) |
| locator | 内容UI要素 / 画像 / 座標のどれで指しているか |
| input | 内容外から渡す値(変数、ファイル、資格情報の名前) |
| output | 内容次の手順へ渡す値、書き出すファイル |
この表はClaudeに一から作らせてもかまいません。ただし、読ませる材料は手順書、画面のスクリーンショット、フローを書き出したファイルなど、手元にあるものです。RPAツールの書き出し形式は製品とバージョンで違うため、読み取れた手順と読み取れなかった手順を分けて返すよう頼みます。
flows/invoice_download/ の手順書とスクリーンショットを読んで、
steps.csv を作って。列は step,action,target,locator,input,output。
読み取れなかった手順は、推測で埋めずに action に UNKNOWN と書いて。
スクリプトはまだ書かないで。UNKNOWNの行は、フローの作者か画面を見ながらの確認で埋めます。ここを推測で埋めたスクリプトは、動いても何を再現しているのか誰にも説明できません。
操作表の行は、こう埋まる
例として、取引先の請求書を画面から取得してExcelに転記する架空のフローを考えます。steps.csvは次のような行になります。
step,action,target,locator,input,output
1,Webページを開く,https://portal.example.com/login,url,,
2,ログイン,portal 請求書画面,UI要素,PORTAL_USER/PORTAL_PASS,
3,期間を指定,請求書一覧,UI要素,対象年月,検索結果
4,PDFをダウンロード,請求書一覧の各行,座標,,downloads/*.pdf
5,金額を転記,請求台帳.xlsx,UI要素,downloads/*.pdf,請求台帳.xlsx4行目だけが座標です。ここは「ボタンの位置」で指しているため、ページの構成が変わると壊れます。Playwrightへ移すときは、座標でなく文言や役割で要素を指す形に直す箇所として、最初に印を付けます。入力列のPORTAL_USERのように、資格情報は名前だけを書き、値は表にもスクリプトにも置きません。
暗黙の動作を表に足す
画面に出ない動作も、置き換えで抜け落ちやすい部分です。フローのエラー時の再試行、失敗したときに送るメール、ログの書き出し先は、操作の一覧に出てきません。手順書にも書かれていないことが多いため、フローの設定画面を見ながら次の項目を埋めます。
- 失敗したときの動作(再試行の回数、通知先、後続を止めるか)
- 実行の契機(定時、ファイルの到着、人が起動)
- 同時に動かしてはいけない相手(同じExcelを他のフローが開く、など)
- 実行するアカウントと、その権限
この4項目は、スクリプトでは例外処理、スケジューラーの設定、ロック、環境変数として別々に実装することになります。表にしておけば、新旧の差分を比べるときの項目一覧にもなります。
locatorの種類で移行先を振り分ける
表ができたら、locatorとtargetの組で移行先を決めます。
| 元の操作対象 | Python側の候補 | 難しさの見立て |
|---|---|---|
| ファイル・フォルダの整理 | Python側の候補標準ライブラリで足りる | 難しさの見立て低い |
| Webページの操作・データ抽出 | Python側の候補Playwright for Python | 難しさの見立て中 |
| Windowsのデスクトップアプリ | Python側の候補pywinauto | 難しさの見立て中〜高 |
| 画像や座標で指している操作 | Python側の候補手がかりを別のものに替える必要あり | 難しさの見立て高い |
| ターミナルエミュレーター等のレガシー画面 | Python側の候補個別に調べる | 難しさの見立て高い |
Playwrightは、ブラウザーを起動して操作するPythonのライブラリです。pip install playwrightの後にplaywright installを実行すると、Chromium、Firefox、WebKit用のブラウザーが入ります。pywinautoは、Microsoft WindowsのGUIを自動操作するためのPythonモジュール群で、ウィンドウやコントロールにマウスとキーボードの操作を送れます。
一方、座標や画像で指している操作は、そのまま移せません。画面の解像度やテーマが変わると壊れる作りなので、スクリプト化の前に「その操作は結局、何のためか」を確認します。ファイルを開くためのクリックなら、パスを直接指定する形に変えられることが少なくありません。
表の振り分けを済ませたら、全フローを並べて簡単なものから移します。操作がすべてファイルとWebに収まるフローを最初の1本に選ぶと、確認の仕組みを小さく作れます。
完了の基準は「旧フローと同じ出力」に置く
成果物の合否をClaudeが自分で読める形にしておくと、仕上がりが安定します。確認が無いと、Claudeは見た目が終わったところで止まり、間違いを見つける役が人間になるからです。テストファイル、ビルドの終了コード、出力を固定データと比べるスクリプトが、確認の例に並びます。
RPAの置き換えでは、この確認が「旧フローが出したファイルとの差分」になります。次のようにします。
出力比較の3手順
- 1
旧フローの出力を保存する
旧フローを固定の入力で実行し、出力ファイルを
expected/に保存します。実行は人が行います。 - 2
新スクリプトを実行する
新しいスクリプトを同じ入力で実行し、出力を
actual/に書きます。 - 3
2つのフォルダを比べる
比べるスクリプトを用意し、合否を終了コードで返します。
比較スクリプトは、たとえば次のような形です。完全一致を求めるか、許す差(出力日時など)を決めるかは、フローごとに変わります。
import csv
import sys
from pathlib import Path
IGNORE = {"processed_at"} # 毎回変わる列だけ許す
def load(path):
with open(path, newline="", encoding="utf-8") as f:
return [
{k: v for k, v in row.items() if k not in IGNORE}
for row in csv.DictReader(f)
]
def main(expected_dir, actual_dir):
bad = 0
for exp in sorted(Path(expected_dir).glob("*.csv")):
act = Path(actual_dir) / exp.name
if not act.exists():
print(f"MISSING: {exp.name}")
bad += 1
elif load(exp) != load(act):
print(f"DIFF: {exp.name}")
bad += 1
sys.exit(1 if bad else 0)
if __name__ == "__main__":
main(*sys.argv[1:3])旧フローの出力がExcelやPDFの場合は、CSVに書き出せる列だけでも比べます。見た目の比較はスクリーンショットになります。ブラウザーのスクリーンショットをモックアップと並べる使い方も、確認の例に並びます。
固定入力は、業務データのコピーにしない
fixtures/inに本番のデータをそのまま入れると、取引先名や金額がリポジトリとClaudeの作業ログに残ります。氏名や口座番号のような項目は、桁数と形式を保ったまま架空の値に置き換えたものを使います。
そのうえで、固定入力には平常のデータに加えて次の行を混ぜます。RPAのフローは、こうした境界で人が手で直していた形跡を持っていることがあるためです。
- 空の行、先頭や末尾に空白のある値
- 全角と半角が混在した文字列
- 件数が0件の日、1件だけの日、通常の10倍の日
- 日付の書式が揺れている行
旧フローをこの入力で動かしてexpected/を作れば、境界での挙動も新旧の比較に入ります。旧フローが境界で誤った出力をしていた場合は、そのまま再現するのか、直すのかを人が決めます。決めた内容はsteps.csvの備考欄に書き残します。
指示は「確認が通るまで直す」まで書く
確認を用意したら、Claudeに渡す指示に実行コマンドを入れます。リポジトリにすでにある確認コマンドをCLAUDE.mdに1行書くことが、いちばん効く設定です。
# Commands
- 比較: python tools/compare.py expected actual
- 実行: python flows/invoice_download/main.py --input fixtures/in --out actual
# Conventions
- 1フロー = flows/<名前>/ に main.py と steps.csv を置く
- 認証情報はコードに書かず、環境変数名だけを steps.csv に残す
- 本番のシステムには、このリポジトリのスクリプトから接続しない最後の1行は、移行中の事故を防ぐためのものです。ただしCLAUDE.mdの指示は、「ほぼ守られるが保証はない」ものです。接続させたくない先がある場合は、権限ルールやサンドボックスなど、強制力のある仕組みで制限します。
作業の指示は、最初の1本について次のように出します。
flows/invoice_download/steps.csv の手順を Playwright の Python で
main.py に実装して。fixtures/in を入力にして actual/ に出力し、
python tools/compare.py expected actual が 0 で終わるまで直して。
終わったら、実行したコマンドとその出力を見せて。「コマンドと出力を見せる」までが指示の一部です。完了の報告だけでなく、実行したコマンドと出力まで出させると、確認を人が肩代わりせずに済みます。
終了を止める: Stopフックで比較を強制する
「通るまで直して」という指示は、Claudeの判断に任されます。確実に止めたいときは、Stopフックを使います。タイプチェックを走らせて失敗なら終了コード2を返す例があり、標準エラー出力の内容がClaudeへ戻されます。比較スクリプトに置き換えると次の形です。比較の前に新スクリプトを実行してactual/を作り直す順にしておきます。前回の出力が残ったまま比べると、直した結果を見ずに合否が決まってしまうためです。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "(python flows/invoice_download/main.py --input fixtures/in --out actual && python tools/compare.py expected actual) 1>&2 || exit 2"
}
]
}
]
}
}注意点が1つあります。ブロックが8回続くと、Claude Codeは次のブロックを無効にしてターンを終えます。上限はCLAUDE_CODE_STOP_HOOK_BLOCK_CAPで上げられますが、8回直しても通らないなら、指示かフローの理解のほうを疑う合図です。Pesterを完了条件にしてPowerShellを書く手順は、Claude CodeでPowerShellのPesterテストを完了条件にしてスクリプトを書くにまとめています。完了条件を決める考え方は、言語が違っても共通です。
複数のフローは1本ずつ、同じ型で回す
最初の1本で型ができたら、残りのフローは同じ手順で回します。ドキュメントの例には、claude -pをシェルのループに入れ、ファイルごとに実行する形があります。この形は、フローごとの移行にそのまま使えます。
for flow in $(cat flows.txt); do
claude --bare -p "flows/$flow/steps.csv を main.py に実装し、
compare が 0 になるまで直して。OK か FAIL で答えて。" \
--allowedTools "Read,Edit,Write,Bash(python *)" \
--permission-mode dontAsk \
--max-turns 30
done--bareを付けると、CLAUDE.mdやフック、MCPサーバーが読み込まれません。そのため、上のループでは「Stopフック」と「CLAUDE.mdの規約」が効かない実行になります。必要な内容は、プロンプトや--append-system-prompt、--settingsで渡し直します。
最初に3ファイルほどで試し、何が起きるかを見て指示を直してから残りを回す進め方が向いています。RPAの置き換えでも、まず3本で試します。ワークフローが数十本あっても、いきなり全件には回しません。
別々のフローを同時に進めるなら、フローごとに作業ツリーを分けます。claude -w <名前>で新しい作業ツリーにセッションを開けます。
並行運転で、本番の切り替えを段階にする
ここまでで「固定データでは同じ出力になる」ことは確かめられます。本番データで同じかどうかは、別の確認です。切り替えは次の順にします。
本番切り替えの4段
- 1
影運転
旧フローを本番として動かしたまま、同じ入力でスクリプトも動かし、出力だけを比べます。
- 2
差異の分類
差分が出たら、スクリプトのバグ、旧フローの癖、許してよい差(日時など)のどれかに分けます。
- 3
逆転
数回続けて差が出なくなったフローから、スクリプトを本番にし、旧フローを予備にします。
- 4
撤去
予備を使わないまま一定期間が過ぎたら、旧フローを止めます。
影運転で差分が出たときは、Claudeに「直して」と頼む前に、差分の中身を一緒に読みます。たとえば、金額の列が1円ずれているなら、旧フローがExcelの表示上の丸めを使い、スクリプトが元の数値のまま書いている、といった原因が考えられます。この種のずれはスクリプトのバグではなく、旧フローの癖です。どちらを正とするかは業務側で決める事柄なので、判断が出るまでは比較スクリプトの許容差に入れず、差分として残し続けます。判断が出たら、その内容をsteps.csvの備考に書いてから、実装とexpected/の両方を直します。
「一定期間」の長さは業務の周期で決まります。月次の締め処理なら、少なくとも1か月分の周期を通さないと、出力の差を確かめる機会がありません。
影運転や本番の定時実行は、旧フローが動いているのと同じ端末のタスクスケジューラーなど、実行する側のスケジューラーで組みます。デスクトップアプリの操作や社内ネットワークのデータには、その端末からしか届かないためです。Claude Codeの/scheduleで作る定期タスクはクラウドで動くので、差分レポートの集計やスクリプト修正のPR作成のような、リポジトリ上の作業に使います。どちらの場合も、mainを保護して直接書き込めないようにすること、新しいタスクは最初の数回の実行を見てから無人にすることが注意点です。人の確認なしで動く仕組みは、この順で信頼を積みます。
Claudeに任せる範囲と、人が決める範囲
RPAの置き換えでは、Claudeに頼めることと、人が決めるべきことが分かれます。移行の途中で迷ったときの目安を表にします。
| 作業 | Claude | 人 |
|---|---|---|
手順書・画面からsteps.csvの下書きを作る | Claude向く | 人UNKNOWNの行を埋める |
固定入力で旧フローを実行してexpected/を作る | Claude向かない(旧ツールの実行環境が必要) | 人実行して保存する |
| Playwright / pywinautoでの実装 | Claude向く | 人差分の結果を読む |
| 許してよい差(日時、並び順)の決定 | Claude候補の洗い出しまで | 人決める |
| 本番への接続・認証情報の取り扱い | Claude触らせない | 人管理する |
| 旧フローの停止 | Claude提案まで | 人決める |
「許してよい差」を人が決める理由は、比較スクリプトの甘さがそのまま移行の甘さになるからです。Claudeに比較を緩めさせると、通るまで直す指示の下では、通すために比較のほうを緩める方向にも動けます。比較スクリプトとexpected/は、編集させない扱いにするのが安全です。権限ルールでtools/compare.pyとexpected/への書き込みを拒否しておけば、確認そのものの改ざんを防げます。
つまずきやすい点
待ち時間の扱いが違う
RPAのフローでは、固定の待機アクションを置いて画面の変化を待つ作りがよくあります。pywinautoは、ウィンドウやコントロールの解決を最大5秒(ドキュメントの記載)まで自動で待ち直します。長い処理の後に出るダイアログは、waitやwait_notで状態を明示して待ちます。Playwrightも自動待機を備えているため、time.sleepを使わず、どうしても待つ場合はpage.wait_for_timeoutを使います。time.sleepは内部の非同期処理を妨げ、古い状態を返す原因になります。
pywinautoは対象の探し方が属性の曖昧一致
ドキュメントの例では、Application(backend="uia")でアプリを起動し、myapp.PageSetup.OKのようにウィンドウ名とコントロール名の近い一致で対象を探します。ウィンドウのタイトルが似た別画面を拾うと、違うボタンを押しかねません。操作の前後でウィンドウ名や表示内容を確かめる行を、スクリプトに入れておきます。
ダウンロードは一時フォルダーに入る
Playwrightでページからファイルを取るとき、ダウンロードは一時フォルダーに置かれ、ブラウザーのコンテキストを閉じると消えます。RPAのフローが特定のフォルダーに保存していたなら、expect_downloadで受けてsave_asで保存先を指定する形に直します。
with page.expect_download() as download_info:
page.get_by_text("ダウンロード").click()
download = download_info.value
download.save_as("out/" + download.suggested_filename)マルチスレッドで共有しない
PlaywrightのAPIはスレッドセーフではありません。複数のフローを並列にさばくときは、スレッドごとにPlaywrightのインスタンスを作ります。
置き換えない選択も残す
Power Automateのデスクトップフローは、画面の要素に加えて画像や座標で操作できるため、レガシーなアプリやターミナルエミュレーターを自動化の対象にできます。スクリプト化の費用に見合わないフローは、棚卸しの表に「現状維持」と書いて残します。移行の対象に入れないという判断も、表があるから説明できます。
よくある質問
UiPathのプロジェクトも同じ手順で移せますか
操作表の作り方と、出力の比較で完了を判断する流れは共通です。違うのは、プロジェクトの書き出しの読み取りやすさで、ここは製品とバージョンに依存します。読み取れない部分をUNKNOWNとして残し、スクリーンショットと実際の画面操作で補う進め方は、どの製品のフローにも使えます。
スクリプトにすると保守は楽になりますか
差分が見えるようになる点では楽です。Gitで変更履歴を追え、レビューもできます。ただし、画面の要素を指す部分は、画面が変わるたびに直す対象のままです。置き換えで消えるのはRPAツールの運用であり、画面変更への追従ではありません。
進め方のまとめ
置き換えの成否は、スクリプトの出来より、旧フローの出力を基準にした確認が先にあるかで決まります。順番は、操作表の作成、locatorによる振り分け、固定入力での出力比較、フックによる強制、影運転での本番確認です。なお、Perlの古いスクリプトを同じ発想で読み解く手順は、Claude CodeでPerlの古いスクリプトを読解し、仕様と移行テストを作るにあります。段階的な移行の考え方は、Claude CodeでReduxからZustandへ移すの「単位ごとに移す」進め方と同じです。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。