southwest について
Southwestの運賃検索と予約済み便の値下がり確認に使うスキルです。Patchrightで現金・ポイント運賃や運賃クラス、節約機会を調べ、予約は変更しません。
主な機能
- 01現金・ポイント運賃の検索
- 02運賃クラス別の価格表示
- 03予約便の値下がり監視
- 04読み取り専用の変更運賃確認
- 05Dockerによるバックグラウンド実行
活用シーン
- 01Southwestの現金運賃とポイント運賃の比較
- 02Companion Pass対象運賃の確認
- 03予約済み便の値下がり機会の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ borski/ travel-hacking-toolkit --skill southwestインストール後、Claude Code で/southwestを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
Dockerfilescripts/check_change.pyentrypoint.shmonitor.pysearch_fares.py詳細
Southwest Airlines の検索
Patchright(検出を回避する Playwright のフォーク)を使って southwest.com を検索します。Southwest は、GDS、API、フライト検索ツールのいずれにも含まれていない、米国の大手航空会社として唯一の存在です。このスキルを使うことでのみ、運賃クラス、ポイント料金、コンパニオンパスの資格など、Southwest 固有のデータを取得できます。
Patchright(検出を回避する Playwright のフォーク)が必要です。 標準の Playwright と agent-browser は Southwest に検出され、ブロックされます。Patchright はブラウザーのフィンガープリントにパッチを適用し、検出を回避します。
ヘッド付きで実行する必要があります(headless=False)。 Southwest は Patchright を使っていてもヘッドレスブラウザーを検出します。macOS では Chrome のウィンドウが一瞬表示されてから閉じます。ポップアップなしでバックグラウンド実行するには、Docker を使います。
# Search new flights (default, no login needed)
docker run --rm ghcr.io/borski/sw-fares --origin SJC --dest DEN --depart 2026-05-15
docker run --rm ghcr.io/borski/sw-fares --origin SJC --dest DEN --depart 2026-05-15 --return 2026-05-18 --points --json
# Check existing reservation for price drops (requires SW login)
docker run --rm -e SW_USERNAME -e SW_PASSWORD \
ghcr.io/borski/sw-fares change --conf ABC123 --first Jane --last Doe --json
# List all upcoming trips
docker run --rm -e SW_USERNAME -e SW_PASSWORD \
ghcr.io/borski/sw-fares change --list --json
# Build locally
docker build -t sw-fares skills/southwest/
Docker イメージは xvfb を使ってコンテナー内に仮想ディスプレイを作成します。Patchright は xvfb に対して「ヘッド付き」で実行されるため、マシン上にウィンドウを開かずに Southwest のヘッドレス検出を回避できます。
# Install
pip install patchright && patchright install chromium
# Search cash fares
python3 scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15
# Search cash + points
python3 scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15 --return 2026-05-18 --points
# JSON output
python3 scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15 --points --json
試行錯誤の末に得られた、実装上の重要なポイント:
- まず southwest.com のホームページに移動してから、検索結果の URL に移動します。ホームページを事前に読み込まずに直接 URL にアクセスすると、ブロックされます。
- ポイント料金には、URL パラメーター
fareType=POINTSを使います。ページ上のポイント切り替えボタンをクリックしようとしないでください。 - フィンガープリントの蓄積を避けるため、実行ごとに新しい永続ブラウザーコンテキスト(一時ディレクトリ)を使います。
<main>ではなく[role='main']からテキストを抽出します。<main>要素には予約フォームが含まれています。[role='main']` にはフライトの検索結果が含まれています。- フライトのブロックは、
# \d+のパターンで分割し、View seatsで終わるように解析できます。 - 必ず最初に Cookie バナーを閉じてください。操作の妨げになります。
前提条件
pip install patchright && patchright install chromium
または Docker を使います(ローカルへのインストールは不要です)。
# Pre-built image (fastest)
docker pull ghcr.io/borski/sw-fares:latest
# Or build locally
docker build -t sw-fares skills/southwest/
使用する場面
- ユーザーが Southwest のフライトや運賃について尋ねている
- ポイント料金が必要(ほかでは取得できません)
- 運賃クラスの内訳が必要(Basic、Choice、Choice Preferred、Choice Extra)
- コンパニオンパスの価値を計算する
- Southwest のポイントと現金、または他社の航空会社を比較する
使用しない場面
- Southwest の現金価格だけが必要な場合:代わりに google-flights スキルを使います(より高速です)
- 購入手続きを完了する場合:フライトの検索のみを行います。予約はしないでください。
- Southwest 以外の航空会社の場合:duffel、ignav、google-flights、またはほかのスキルを使います
Southwest の運賃クラス
| 運賃 | ポイント | 現金 | コンパニオンパス | 変更/キャンセル | 手荷物 |
|---|---|---|---|---|---|
| Basic(Wanna Get Away) | 最低 | 最低 | 対象外 | クレジットのみ | 2個無料 |
| Choice(Wanna Get Away Plus) | 低 | 低 | 対象 | 譲渡可能なクレジット | 2個無料 |
| Choice Preferred(Anytime) | 高 | 高 | 対象 | 払い戻し可能 | 2個無料 |
| Choice Extra(Business Select) | 最高 | 最高 | 対象 | 払い戻し可能+優先対応 | 2個無料 |
コンパニオンパスに関する重要事項: 対象になるのは Choice 以上のみです。Basic は対象外です。これにより、CPP の計算は大きく変わります。
コンパニオンパスの CPP 計算式
CP が適用される場合、1 枚の航空券で 2 人分の旅行ができます。
Total cash value = qualifying_fare_cash x 2 passengers
CPP = total_cash_value / total_points x 100
「ポイントで 2 人分、$X 相当の旅行を購入できた」と表現してください。
検索:スクリプトを使う
SW の予約フォームを手動で自動化しようとしないでください。 不安定で、フォームの React の状態がブラウザー自動化とうまく連携しません。結果の URL に直接移動するスクリプトを代わりに使います。
# Local (opens a Chrome window briefly)
python3 skills/southwest/scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15
# With return date and points
python3 skills/southwest/scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15 --return 2026-05-18 --points
# JSON output for programmatic use
python3 skills/southwest/scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15 --points --json
# Docker (no Chrome window, no display needed)
docker run --rm ghcr.io/borski/sw-fares --origin SJC --dest DEN --depart 2026-05-15 --return 2026-05-18 --points
このスクリプトがすべて処理します。ホームページの事前読み込み、Cookie バナーの閉鎖、結果の抽出、ポイントへの切り替えです。
スクリプトの動作
- 新しいブラウザープロファイルを使用して、ヘッドありモードでPatchright(検出されないPlaywright)を起動します
- 通常のブラウジングセッションを確立するため、最初にsouthwest.comのホームページにアクセスします
- 現金の場合は
fareType=USD、ポイントの場合はfareType=POINTSを指定した結果ページのURLに移動します - SWのReactアプリがフライトデータをレンダリングするまで15秒待ちます
[role='main']からフライトのテキストを抽出します(予約フォームである<main>ではありません)# \d+パターンで分割してフライトブロックを解析します- ブラウザーと一時プロファイルをクリーンアップします
ポイント切り替えボタンをクリックせずにURLでfareTypeを指定する理由: 結果ページのPointsラベルは、フライトカードとは異なるフレームのコンテキストにあります。Playwrightでクリックしても安定しません。URLパラメーターを使用すると、ポイントの検索結果を直接読み込めます。ページを1回読み込んで切り替えボタンをクリックするより、ページを2回読み込む(現金+ポイント)方が確実です。
mainではなく[role='main']を使う理由: SWには2つのコンテンツ領域があります。<main> HTML要素には予約フォームが含まれています。[role='main'] ARIA属性はフライトの検索結果を示します。誤った方を使うと、フライトが0件になります。
出力形式
常にMarkdownの表を使用してください。 現金とポイントは別々の表に表示します。
現金価格
| フライト | 出発 | 到着 | 経由地 | 所要時間 | Basic | Choice | Ch Pref | Ch Extra |
|---|---|---|---|---|---|---|---|---|
| #104 | 6:00AM | 9:35AM | 直行 | 2h 35m | $277 | $317 | $387 | $432 |
| #1681 | 7:40PM | 11:05PM | 直行 | 2h 25m | $209 | $249 | $319 | $364 |
| #481/3780 | 6:35AM | 12:05PM | PHX経由1回 | 4h 30m | $219 | $259 | $329 | $374 |
ポイント価格
| フライト | 出発 | 到着 | 経由地 | 所要時間 | Basic | Choice | Ch Pref | Ch Extra |
|---|---|---|---|---|---|---|---|---|
| #104 | 6:00AM | 9:35AM | 直行 | 2h 35m | 23,500 +$5.60 | 27,000 +$5.60 | 33,000 +$5.60 | 36,500 +$5.60 |
表の後
- 各運賃クラスのCPPを計算します:
cash_price / points x 100 - コンパニオンパスの対象となる運賃(Choice以上)を示します
- CPが関係する場合は、2人分の価値を表示します
- 他の航空会社の選択肢がある場合は比較します
トラブルシューティング
- 「No flights found」: SWが自動リクエストの多さからIPにフラグを立てた可能性があります。数分待ってから再試行するか、Dockerを試してください(ネットワークスタックが異なります)。
- Chromeウィンドウがポップアップする: ローカルモードでは想定どおりです。ポップアップを表示しない場合はDockerを使用してください。
- スクリプトが停止する: SWのページのレンダリングには最大20秒かかることがあります。15秒の待機で通常は十分ですが、接続が遅い場合はさらに時間が必要になることがあります。
- 「Oops」エラーページ: 少し待ってから再試行してください。SWのサイトは不安定な場合があります。
制限事項
- ヘッドありモードが必要です。 Patchrightのステルスパッチを使用しても、SWはヘッドレスブラウザーを検出してブロックします。バックグラウンドで動作させるには、Docker+xvfbを使用します。
- コードシェアのデータはありません。 SWは他社とコードシェアしていません。
- 検索1回につき約20秒かかります。 ホームページのウォームアップとページのレンダリングにかかる時間です。APIソースより遅くなります。
- 価格は頻繁に変わります。 SWでは動的価格設定が使用されています。結果はその時点でのものです。
- 片道検索では往路のフライトのみが返されます。 往復の場合、スクリプトは往路の運賃を取得します。復路の運賃検索には2回目のURL読み込みが必要です(今後対応予定)。
Change Flight Price Monitor(check_change.py)
読み取り専用のスクリプトです。フライトの変更、修正、キャンセルは一切行いません。
southwest.comにログインし、既存の予約を検索して、Change Flightページを読み取り、価格が支払額を下回ったかどうかを確認します。
Dockerの使用方法(ポップアップウィンドウなし)
# Set your SW credentials however you like (env vars, 1Password, etc.)
export SW_USERNAME="your_username"
export SW_PASSWORD="your_password"
# List all upcoming trips (auto-discover confirmation numbers)
docker run --rm -e SW_USERNAME -e SW_PASSWORD \
ghcr.io/borski/sw-fares change --list --json
# Check a specific reservation for price drops
docker run --rm -e SW_USERNAME -e SW_PASSWORD \
ghcr.io/borski/sw-fares change --conf ABC123 --first Jane --last Doe --json
# Local (opens Chrome window, for debugging only)
SW_USERNAME=your_user SW_PASSWORD=your_pass \
python3 scripts/check_change.py --conf ABC123 --first Jane --last Doe --debug
重要:法的な氏名を使用してください
SWの予約では、ニックネームではなく予約に記載された法的な氏名を使用します。予約に記載されている正確な名と姓を使用してください。
動作の仕組み
- ヘッダーのログインフライアウトから southwest.com にログインします
--listの場合:My Account をクリックし、各旅行のアコーディオンを展開して、確認番号を抽出します--confの場合:Change Flight ページに移動し、検索フォームに入力して、両方の区間を選択し、「Explore options」をクリックします- 変更結果ページを読み込み、利用可能なすべてのフライトの運賃差額を確認します:
CURRENT FLIGHT= 予約済みのフライトです(Basic のセルには「CURRENT」と表示され、変更はありません)+2,000= このフライトは支払った運賃より2,000ポイント高くなります-2,000= このフライトは支払った運賃より2,000ポイント安くなります(節約になるため、再予約のチャンスです)Unavail= 運賃クラスは売り切れです
- 差額がマイナスのフライトを、節約のチャンスとして通知します
安全ガードレール
- DANGEROUS_LABELS のブロックリスト:「このフライトを変更」、「確認」、「キャンセル」、「予約」、「購入」など
- ログインと検索のフェーズ後、ボタンをクリックするたびに is_dangerous_click() でチェックします
- extract_results() は読み取り専用です。区間の選択と「Explore options」の手順以降、クリックは一切行いません
- スクリプトは区間を選択して代替便を表示しますが、代替フライトを選択したり、変更を確定したりすることは決してありません
動作確認済みのセレクター(2026年4月5日)
ログインフライアウト:
- トリガー:
button:has-text('Log in') - ユーザー名:
input[aria-label*='Account'] - パスワード:
input[name='password'] - 送信:
button[type='submit']:has-text('Log in')
フライト変更の検索フォーム:
- 確認番号:
input[id*='confirmationNumber'] - 名:
input#passengerFirstName - 姓:
input#passengerLastName - 送信:
button#form-mixin--submit-button(テキスト:「Continue」)
区間の選択:
- チェックボックス:
label:has(input[type='checkbox'])(表示されるラベル、非表示の入力欄) - 続行:
button#form-mixin--submit-button(テキスト:「Explore options」)
重要な制限:コンパニオンパスに紐づく予約
change コマンドは、コンパニオンパスに紐づく予約では動作しません。SW には次のバナーが表示されます:「コンパニオンの旅程が紐づいています。変更するには、先にコンパニオンのフライトをキャンセルしてください。」
コンパニオンパスに紐づく予約の場合(またはログインせずに監視したい場合)は、代わりに monitor サブコマンドを使用してください。公開されている運賃検索フローを使用し、予約済みのフライト番号を現在の検索結果と照合します。ログインは不要です。
運賃値下がりモニター(monitor.py)
現在の SW 運賃を、予約済みフライトで支払った金額と比較します。現在の価格が基準額を下回った予約済みフライトを報告します(再予約時に取り戻せる旅行クレジットに相当します)。
ログインは不要です。 公開されている運賃検索フローを使用し、完全一致するフライト番号で照合します。フライト変更フローがブロックされる、コンパニオンパスに紐づく予約でも動作します。
設定ファイル
旅行の配列を JSON 形式で指定します:
[
{
"name": "Trip name (display only)",
"confirmation": "ABC123",
"passenger": "Jane (free-text label, display only)",
"outbound": {
"origin": "SJC",
"dest": "SAN",
"date": "2026-04-30",
"flight_number": "4107",
"baseline_pts": 13000
},
"return": {
"origin": "SAN",
"dest": "SJC",
"date": "2026-05-03",
"flight_number": "4119",
"baseline_pts": 17500
}
}
]
flight_number は、検索結果に SW が表示する表記と完全に一致する必要があります。複数区間のフライトにはスペースが含まれます(例:"3657 / 4630")。片道旅行の場合は、return 区間を省略できます。
Docker の使い方
# Check all trips in the config
docker run --rm -v /path/to/trips.json:/app/trips.json:ro \
ghcr.io/borski/sw-fares monitor --config /app/trips.json
# Check just one trip by confirmation number
docker run --rm -v /path/to/trips.json:/app/trips.json:ro \
ghcr.io/borski/sw-fares monitor --config /app/trips.json --only ABC123
# JSON output
docker run --rm -v /path/to/trips.json:/app/trips.json:ro \
ghcr.io/borski/sw-fares monitor --config /app/trips.json --json
ローカルでの使い方(Chrome ウィンドウが一時的に開きます)
python3 scripts/monitor.py --config /path/to/trips.json
動作の仕組み
- 設定内の各旅行の各区間について:
- SW のホームページに移動します(区間ごとに SPA の状態をリセットします)
pointsの運賃タイプで、経路と日付を検索します- 結果の URL に指定された出発地と到着地が含まれていることを検証します(古いレンダリング結果を拒否します)
- 予約済みの特定のフライト番号を検索結果から探します
- 現在の Basic 運賃をポイント単位で読み取ります
- 設定の
baseline_ptsと比較します
- 区間ごとの節約額(プラス)または追加料金(マイナス)と、旅行全体の合計を報告します
再予約後
節約するために区間を再予約した場合は、その区間の baseline_pts を、支払い済みの新しい(より低い)金額に更新してください。更新しないと、次回のモニター実行でも同じ機会が誤って通知され続けます。
Change Flow より優れている点
| check_change | monitor | |
|---|---|---|
| ログインが必要 | はい | いいえ |
| CP 連携済みの予約に対応 | いいえ | はい |
| 常に代替便を表示 | はい | いいえ(予約済みの便のみ) |
| 脆弱な SW DOM への依存 | 高い(フォームのクリック操作) | 低い(URL パラメーター+テキスト解析) |
| 出力の詳細度 | すべての代替便の運賃クラスごとの差分 | 予約済みの便そのものの基本運賃の差分 |
定期的なベースラインの追跡には monitor が適切なツールです。すべての運賃クラスの代替便を確認したい場合は check_change が便利ですが、CP 連携されていない予約に限られます。