关于 southwest
用于查询 Southwest 票价并监控现有预订降价的技能。它通过 Patchright 获取现金与积分价格、票价等级和潜在优惠,且不会更改预订。
主要功能
- 01查询现金和积分票价
- 02展示各票价等级价格
- 03监控预订价格下降
- 04只读检查改签价格
- 05通过 Docker 后台运行
适用场景
- 01比较 Southwest 现金与积分票价
- 02检查符合 Companion 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 航班搜索
通过 Patchright(一个可绕过检测的 Playwright 分支)搜索 southwest.com。Southwest 是唯一一家不接入任何 GDS、API 或航班搜索工具的美国主要航空公司。此技能是获取 Southwest 专属数据的唯一方式,包括票价等级、积分定价和 Companion Pass 资格。
需要 Patchright(一个可绕过检测的 Playwright 分支)。标准 Playwright 和 agent-browser 会被 SW 检测并拦截。Patchright 会修改浏览器指纹以绕过检测。
必须以有头模式运行(headless=False)。 即使使用 Patchright,SW 也能检测到无头浏览器。在 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 以“有头”模式运行,从而绕过 SW 的无头检测,且不会在你的机器上打开任何窗口。
# 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 会被拦截。
- 使用
fareType=POINTSURL 参数查询积分定价。不要尝试点击页面上的积分切换按钮。 - 每次运行都使用全新的持久化浏览器上下文(临时目录),避免指纹累积。
- 从
[role='main']而不是<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)
- 计算 Companion Pass 的价值
- 比较 SW 积分、现金与其他航空公司的价格
不适用场景
- 只需要 SW 的现金价格:改用 google-flights 技能(速度更快)
- 完成购买:只查找航班,不要预订。
- 非 Southwest 航空公司:使用 duffel、ignav、google-flights 或其他技能
Southwest 票价等级
| 票价 | 积分 | 现金 | Companion Pass | 改签/取消 | 行李 |
|---|---|---|---|---|---|
| Basic (Wanna Get Away) | 最低 | 最低 | 不符合 | 仅可获抵用金 | 免费 2 件 |
| Choice (Wanna Get Away Plus) | 较低 | 较低 | 符合 | 可转让抵用金 | 免费 2 件 |
| Choice Preferred (Anytime) | 较高 | 较高 | 符合 | 可退款 | 免费 2 件 |
| Choice Extra (Business Select) | 最高 | 最高 | 符合 | 可退款 + 优先服务 | 免费 2 件 |
Companion Pass 的关键点: 只有 Choice 及以上等级符合资格。Basic 不符合。这会显著改变 CPP 的计算方式。
Companion Pass CPP 公式
启用 CP 时,一张机票可供两人出行:
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访问现金票价的结果 URL,或使用fareType=POINTS访问积分票价的结果 URL - 等待 15 秒,让 SW 的 React 应用渲染航班数据
- 从
[role='main']提取航班文本(不是<main>,后者是预订表单) - 按
# \d+模式拆分航班文本块并进行解析 - 清理浏览器和临时配置文件
为什么使用 URL 中的 fareType,而不是点击 Points 切换项: 结果页上的 Points 标签与航班卡片处于不同的框架上下文。通过 Playwright 点击并不可靠。使用 URL 参数可以直接加载积分结果。加载两次页面(现金票价 + 积分票价)比加载一次再点击切换项更可靠。
为什么用 [role='main'] 而不是 main: SW 有两个内容区域。<main> HTML 元素包含预订表单。[role='main'] ARIA 属性标记的是航班结果。使用错误的区域会导致找不到任何航班。
输出格式
始终使用 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 - 标明哪些票价符合 Companion Pass 资格(Choice 及以上)
- 如果涉及 CP,显示 2 人出行的价值
- 如果有其他航空公司的选项,则进行比较
故障排查
- “No flights found”:SW 可能因自动请求过多而标记了该 IP。等待几分钟后重试,或尝试 Docker(网络栈不同)。
- 弹出 Chrome 窗口:本地模式下属于预期行为。要避免弹窗,请使用 Docker。
- 脚本卡住:SW 页面最多可能需要 20 秒才能渲染。通常等待 15 秒就足够,但网络较慢时可能需要更久。
- 出现 “Oops” 错误页面:稍等片刻后重试。SW 网站有时不太稳定。
限制
- 必须使用有头模式。 SW 会检测并阻止无头浏览器,即使使用 Patchright 的隐身补丁也一样。后台运行时可使用 Docker+xvfb 作为替代方案。
- 没有代码共享数据。 SW 不与其他航空公司进行代码共享。
- 每次搜索约需 20 秒。 包括预热首页和页面渲染时间。速度比 API 数据源慢。
- 价格经常变动。 SW 使用动态定价。结果仅反映查询时的价格。
- 单程搜索只会返回出发航班。 对于往返行程,脚本会获取去程票价。搜索返程航班票价需要额外加载一次 URL(未来增强功能)。
Change Flight 票价监控(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 页面,填写查询表单,选择两个航段,然后点击“查看选项” - 读取更改结果页面,其中会显示所有可选航班的票价差额:
CURRENT FLIGHT= 你已预订的航班(Basic 单元格显示“CURRENT”,没有变化)+2,000= 此航班比你已支付的价格多 2,000 点-2,000= 此航班比你已支付的价格少 2,000 点(节省,可重新预订)Unavail= 此票价舱位已售罄
- 将任何差额为负的航班标记为节省机会
安全防护措施
- DANGEROUS_LABELS 屏蔽列表:“更改此航班”、“确认”、“取消”、“预订”、“购买”等
- 在登录和查询阶段之后,每次点击按钮都会执行
is_dangerous_click()检查 extract_results()仅供读取。在选择航段并点击“查看选项”之后,不会再执行任何点击操作- 脚本会选择航段并查看备选项,但绝不会选择替代航班或确认任何更改
已验证可用的选择器(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(文本:“继续”)
航段选择:
- 复选框:
label:has(input[type='checkbox'])(可见标签,隐藏输入框) - 继续:
button#form-mixin--submit-button(文本:“查看选项”)
重要限制:关联的 Companion Pass 预订
change 命令不适用于关联 Companion Pass 的预订。SW 会显示横幅:“已关联同伴行程。若要更改,请先取消同伴的航班。”
对于关联 CP 的预订(或任何不希望登录的监控场景),请改用 monitor 子命令。它使用公开票价搜索流程,并将你已预订的航班号与当前搜索结果进行匹配。无需登录。
票价下降监控器(monitor.py)
将 SW 当前票价与已预订航班的已付金额进行比较。报告当前价格低于基准价的具体已预订航班(重新预订后可获得可收回的旅行抵用金)。
无需登录。 使用公开的票价搜索流程,并按航班号精确匹配。适用于更改航班流程受限的关联 CP 预订。
配置文件
提供一个 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 fareType 搜索航线和日期
- 验证结果 URL 包含指定的出发地和目的地(拒绝过期渲染结果)
- 在结果中查找特定的已预订航班号
- 读取该航班当前的 Basic 票价(点数)
- 与配置中的
baseline_pts进行比较
- 按航段报告节省金额(正数)或额外费用(负数),并给出行程总计
重新预订后
如果你重新预订某个航段以获得节省,请将配置中该航段的 baseline_pts 更新为新的(较低的)已付金额。否则,下次运行监控器时会继续错误地标记同一机会。
为何它优于变更流程
| check_change | monitor | |
|---|---|---|
| 是否需要登录 | 是 | 否 |
| 是否适用于与 CP 关联的预订 | 否 | 是 |
| 是否始终显示备选方案 | 是 | 否(仅显示已预订的航班) |
| 对 SW DOM 的脆弱依赖 | 高(表单点击) | 低(URL 参数 + 文本解析) |
| 输出详情 | 所有备选方案的完整票价舱位差异 | 你所预订航班的基础票价差异 |
monitor 是日常基线跟踪的合适工具。如果你想查看所有票价舱位的所有备选方案,check_change 很有用,但仅适用于非 CP 预订。