Cursor/Claude CodeでPlaywright RPAを開発する効率的なワークフロー
#Cursor#Claude Code#Playwright#RPA#コーディングエージェント#TypeScript
はじめに
Playwrightを使ったRPA開発は、手動でコードを書くと時間がかかります。特に、UI操作の待機処理、エラーハンドリング、テストコードの作成など、細かい実装が多く、開発速度に影響します。
CursorやClaude Codeなどのコーディングエージェントを活用することで、Playwright RPAの開発を大幅に効率化できます。この記事では、実際のプロジェクトで活用した経験を基に、効率的な開発ワークフローを解説します。
コーディングエージェントでPlaywright RPAを開発するメリット
1. コード生成の高速化
UI操作のコードを自然言語で指示するだけで、適切なPlaywrightコードが生成されます。
2. エラーハンドリングの自動生成
よくあるエラー(要素が見つからない、タイムアウトなど)に対する処理を自動で提案してくれます。
3. テストコードの生成
RPAの動作を検証するテストコードも、エージェントが生成してくれます。
実践例1:基本的なRPAコードの生成
プロンプト設計
効果的なプロンプトの例:
PlaywrightでRPAコードを作成してください。
要件:
- 対象URL: https://example.com/admin
- 操作フロー:
1. ログインページにアクセス
2. メールアドレスとパスワードを入力
3. ログインボタンをクリック
4. ダッシュボードが表示されるまで待機
5. 売上レポートページに移動
6. CSVダウンロードボタンをクリック
7. ダウンロード完了を待機
- エラーハンドリング: 各ステップで要素が見つからない場合はログ出力して終了
- 待機時間: 各操作後に適切な待機を設定
生成されたコード例
import { chromium, Page } from '@playwright/test';
import * as fs from 'fs';
async function login(page: Page, email: string, password: string): Promise<void> {
await page.goto('https://example.com/admin/login');
await page.getByLabel('メールアドレス').fill(email);
await page.getByLabel('パスワード').fill(password);
await page.getByRole('button', { name: 'ログイン' }).click();
// ダッシュボードが表示されるまで待機
await page.waitForSelector('[data-testid="dashboard"]', { timeout: 10000 });
}
async function downloadSalesReport(page: Page): Promise<string> {
// 売上レポートページに移動
await page.getByRole('link', { name: '売上レポート' }).click();
await page.waitForURL('**/sales-report');
// CSVダウンロード
const [download] = await Promise.all([
page.waitForEvent('download'),
page.getByRole('button', { name: 'CSVダウンロード' }).click(),
]);
const path = await download.path();
if (!path) {
throw new Error('ダウンロードに失敗しました');
}
return path;
}
async function runRPA() {
const browser = await chromium.launch();
const page = await browser.newPage();
try {
await login(page, process.env.EMAIL!, process.env.PASSWORD!);
const csvPath = await downloadSalesReport(page);
console.log(`CSV downloaded: ${csvPath}`);
} catch (error) {
console.error('RPA実行エラー:', error);
await page.screenshot({ path: 'error-screenshot.png' });
throw error;
} finally {
await browser.close();
}
}
実践例2:エラーハンドリングの改善
エージェントに既存のコードとエラー情報を提示すると、改善案を提案してくれます。
改善前のコード
async function clickButton(page: Page, buttonText: string) {
await page.getByRole('button', { name: buttonText }).click();
}
問題点:
- ボタンが見つからない場合の処理がない
- タイムアウトの設定がない
- エラーログがない
エージェントへの指示
この関数を改善してください。
要件:
- 要素が見つからない場合はエラーログを出力して終了
- タイムアウトは10秒に設定
- スクリーンショットを保存
- エラー情報を詳細に記録
改善後のコード
async function clickButton(
page: Page,
buttonText: string,
timeout: number = 10000
): Promise<void> {
try {
const button = page.getByRole('button', { name: buttonText });
await button.waitFor({ state: 'visible', timeout });
await button.click();
} catch (error) {
const screenshotPath = `error-${Date.now()}.png`;
await page.screenshot({ path: screenshotPath, fullPage: true });
console.error(`ボタンクリック失敗: ${buttonText}`, {
error: error instanceof Error ? error.message : String(error),
screenshot: screenshotPath,
url: page.url(),
});
throw new Error(`ボタン "${buttonText}" のクリックに失敗しました: ${error}`);
}
}
実践例3:テストコードの自動生成
RPAの動作を検証するテストコードも、エージェントが生成してくれます。
エージェントへの指示
このRPAコードのテストコードを作成してください。
要件:
- ログイン処理のテスト
- CSVダウンロード処理のテスト
- エラーケースのテスト(不正な認証情報など)
- モックを使ったテストも含める
使用ライブラリ: @playwright/test
生成されたテストコード例
import { test, expect } from '@playwright/test';
import { login, downloadSalesReport } from './rpa';
test.describe('RPA Tests', () => {
test('正常系: ログインとCSVダウンロード', async ({ page }) => {
// ログイン
await login(page, '[email protected]', 'password123');
// ダッシュボードが表示されていることを確認
await expect(page.getByTestId('dashboard')).toBeVisible();
// CSVダウンロード
const csvPath = await downloadSalesReport(page);
// ファイルがダウンロードされたことを確認
expect(csvPath).toBeTruthy();
const fs = require('fs');
expect(fs.existsSync(csvPath)).toBe(true);
});
test('異常系: 不正な認証情報でログイン失敗', async ({ page }) => {
await page.goto('https://example.com/admin/login');
await page.getByLabel('メールアドレス').fill('[email protected]');
await page.getByLabel('パスワード').fill('wrongpassword');
await page.getByRole('button', { name: 'ログイン' }).click();
// エラーメッセージが表示されることを確認
await expect(page.getByText('ログインに失敗しました')).toBeVisible();
});
test('異常系: 要素が見つからない場合', async ({ page }) => {
await page.goto('https://example.com/admin');
// 存在しないボタンをクリックしようとする
const button = page.getByRole('button', { name: '存在しないボタン' });
await expect(button).toBeVisible({ timeout: 1000 }).catch(() => {
// タイムアウトが発生することを確認
});
});
});
実践例4:リファクタリング支援
既存のRPAコードを、エージェントの支援でリファクタリングする例です。
リファクタリング前
// 重複が多いコード
async function runJobA() {
const browser = await chromium.launch();
const page = await browser.newPage();
try {
await page.goto('https://site-a.com/login');
await page.fill('#email', '[email protected]');
await page.fill('#password', 'pass123');
await page.click('button[type="submit"]');
await page.waitForSelector('.dashboard');
// ...
} finally {
await browser.close();
}
}
async function runJobB() {
const browser = await chromium.launch();
const page = await browser.newPage();
try {
await page.goto('https://site-b.com/login');
await page.fill('#email', '[email protected]');
await page.fill('#password', 'pass123');
await page.click('button[type="submit"]');
await page.waitForSelector('.dashboard');
// ...
} finally {
await browser.close();
}
}
エージェントへの指示
このコードをリファクタリングしてください。
要件:
- 共通処理を関数に抽出
- 設定を外部化(環境変数や設定ファイル)
- 型安全性を向上
- エラーハンドリングを統一
リファクタリング後
interface RPAConfig {
baseUrl: string;
credentials: {
email: string;
password: string;
};
selectors: {
emailInput: string;
passwordInput: string;
submitButton: string;
dashboardSelector: string;
};
}
class RPARunner {
constructor(private config: RPAConfig) {}
async login(page: Page): Promise<void> {
await page.goto(`${this.config.baseUrl}/login`);
await page.fill(this.config.selectors.emailInput, this.config.credentials.email);
await page.fill(this.config.selectors.passwordInput, this.config.credentials.password);
await page.click(this.config.selectors.submitButton);
await page.waitForSelector(this.config.selectors.dashboardSelector);
}
async runWithBrowser<T>(
operation: (page: Page) => Promise<T>
): Promise<T> {
const browser = await chromium.launch();
const page = await browser.newPage();
try {
return await operation(page);
} finally {
await browser.close();
}
}
}
// 使用例
const jobAConfig: RPAConfig = {
baseUrl: 'https://site-a.com',
credentials: {
email: process.env.EMAIL_A!,
password: process.env.PASSWORD_A!,
},
selectors: {
emailInput: '#email',
passwordInput: '#password',
submitButton: 'button[type="submit"]',
dashboardSelector: '.dashboard',
},
};
const runner = new RPARunner(jobAConfig);
await runner.runWithBrowser(async (page) => {
await runner.login(page);
// その他の処理
});
効率的な開発ワークフロー
1. 要件定義からコード生成まで
1. 要件を自然言語で整理
↓
2. エージェントにプロンプトで指示
↓
3. 生成されたコードをレビュー
↓
4. 必要に応じて改善指示
↓
5. テストコードを生成
↓
6. 実際の環境で動作確認
2. 段階的な開発
一度にすべてを生成させるのではなく、小さな単位で段階的に開発します。
- 基本的なUI操作(ログインなど)
- メイン処理(データ取得など)
- エラーハンドリング
- ログ出力と通知
- テストコード
3. コードレビューのポイント
エージェントが生成したコードは、必ず以下の点を確認します。
- セレクタの適切性: 変更に強いセレクタか
- 待機処理: 適切な待機が設定されているか
- エラーハンドリング: 想定外のエラーに対応できているか
- パフォーマンス: 不要な待機がないか
注意点と制限事項
エージェントの限界
- 最新のAPI情報: Playwrightの最新機能を理解していない場合がある
- サイト固有の仕様: 各サイトの特殊な動作は理解できない
- セキュリティ: 認証情報の扱いに注意が必要
必ず確認すべきこと
- 環境変数の管理: 認証情報は環境変数で管理
- エラーログ: 十分な情報が記録されているか
- 冪等性: 同じ処理を複数回実行しても問題ないか
- 監視と通知: 失敗時の通知が設定されているか
まとめ
CursorやClaude Codeなどのコーディングエージェントを活用することで、Playwright RPAの開発を大幅に効率化できます。
効果的な活用方法:
- 具体的で構造化されたプロンプトを使用
- 段階的に開発を進める
- 生成されたコードを必ずレビュー
- テストコードも自動生成
改善前:
- RPAコードの作成に数日かかる
- エラーハンドリングの実装が不十分
- テストコードの作成に時間がかかる
改善後:
- エージェントの支援で開発時間を大幅短縮
- 堅牢なエラーハンドリングが自動生成
- テストコードも効率的に作成
コーディングエージェントは、Playwright RPA開発の「強力なパートナー」として活用することで、より効率的で保守性の高いコードを実現できます。
関連リンク: