关于 amex-travel
amex-travel 是通过 Patchright 搜索 American Express 旅行门户航班和酒店的技能,面向需要比较现金、MR 积分及会员权益的 Amex 持卡人。它可识别 IAP 票价及 FHR、THC 酒店福利;需要有头浏览器,且不支持预订。
主要功能
- 01搜索航班与酒店报价
- 02比较现金价格与 MR 积分
- 03识别 IAP 折扣票价
- 04提取 FHR、THC 酒店福利
- 05支持邮箱验证码登录
适用场景
- 01比较门户积分与现金机票价格
- 02查找 Platinum 卡 IAP 折扣
- 03评估 FHR 或 THC 酒店权益
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ borski/ travel-hacking-toolkit --skill amex-travel安装后在 Claude Code 中输入/amex-travel确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
Dockerfilescripts/refresh_login.pysearch_flights.pytests/test_search_flights.py详细介绍
Amex Travel Portal 搜索
通过 Patchright 搜索 American Express 旅行门户的航班和酒店。返回现金价格、MR 积分价格、International Airline Program(IAP)折扣,以及 Fine Hotels & Resorts / The Hotel Collection 礼遇。
需要 Patchright(未检测到的 Playwright 分支)。Amex 会拦截标准 Playwright 和 agent-browser。
必须以有头模式运行(headless=False)。 Amex 能检测到无头浏览器。在 macOS 上,会短暂出现一个 Chrome 窗口。如需后台运行,请使用 Docker。
先决条件
pip install patchright && patchright install chromium
或者使用 Docker(无需本地安装):
docker pull ghcr.io/borski/amex-travel:latest
# or build locally:
docker build -t amex-travel skills/amex-travel/
适用场景
- 将 Amex 门户的 MR 价格与现金价格和奖励价格进行比较
- 查找 Platinum 卡的 IAP(International Airline Program)折扣票价
- 查找享有礼遇的 FHR 和 THC 酒店(100 美元额度、早餐、升级)
- 比较门户兑换价值与转点至航空公司的价值
不适用场景
- 完成购买。 仅查找航班和酒店。请勿预订。
- 非 Platinum 卡。 IAP 票价和 FHR 礼遇需要 Platinum Card。
用法
航班搜索
# Local (opens a Chrome window briefly)
python3 scripts/search_flights.py --origin SFO --dest CDG --depart 2026-08-11
# Round-trip business
python3 scripts/search_flights.py --origin SFO --dest CDG --depart 2026-08-11 --return 2026-09-02 --cabin business
# JSON output
python3 scripts/search_flights.py --origin SFO --dest CDG --depart 2026-08-11 --json
# Docker
docker run --rm \
-v ~/.amex-travel-profiles:/profiles \
-e AMEX_USERNAME -e AMEX_PASSWORD \
amex-travel script /app/search_flights.py \
--origin SFO --dest CDG --depart 2026-08-11 --cabin business --json
酒店搜索
# Local
python3 scripts/search_flights.py --hotel --dest "Oslo" --checkin 2026-08-13 --checkout 2026-08-15
# Docker
docker run --rm \
-v ~/.amex-travel-profiles:/profiles \
-e AMEX_USERNAME -e AMEX_PASSWORD \
amex-travel script /app/search_flights.py \
--hotel --dest "Oslo" --checkin 2026-08-13 --checkout 2026-08-15 --json
记录模式(API 探索)
在手动搜索时捕获网络流量:
python3 scripts/search_flights.py --record
离线调试(酒店)
保存并重新解析酒店结果,无需重新运行浏览器:
# Save page HTML after hotel search
python3 scripts/search_flights.py --hotel --dest "Paris" --checkin 2026-08-11 --checkout 2026-08-15 --save-html /tmp/amex-hotels.json
# Re-parse locally (instant, no browser)
python3 scripts/search_flights.py --parse-html /tmp/amex-hotels.json
两步验证流程
Amex 使用电子邮件 OTP 进行两步验证。首次登录时选择“Add This Device”后,同一配置文件后续运行将跳过两步验证。
工作原理: 触发两步验证时,脚本会向 stdout 输出 2FA_CODE_NEEDED,并向 stderr 输出 2FA REQUIRED,随后轮询验证码。最多等待 2 分钟。
对于 Agent: 当你在脚本输出中看到 2FA_CODE_NEEDED 时,请向用户索要 Amex 刚刚发送给他们的验证码。用户提供验证码后,将其写入验证码文件:
echo "123456" > /tmp/amex-2fa-code.txt
脚本会自动读取该文件并继续登录。
命令钩子(可选,用于完全自动化): 将 AMEX_2FA_COMMAND 设置为一个命令,该命令会阻塞运行直至获取验证码,然后将验证码输出到 stdout。脚本将运行此命令,而不是轮询文件。
首次登录时选择“Add This Device”后,同一配置文件的后续运行将跳过两步验证。
已知限制:travel-portal 登录验证码(2026 年 5 月)
截至 Amex 在 2026 年 5 月进行改版后,提交航班搜索会重定向到单独的 travel-portal 登录关卡(/account/travel/login)——即使已经登录 americanexpress.com 也一样——并受到基于风险的验证码层保护。脚本会自动填写并提交登录关卡,结果取决于 Amex 对该次会话的评分:
- 有时可以通过——自动重新验证会被接受,搜索结果从头到尾正常加载(2026 年 7 月经实时验证:完整的 Docker 运行返回了 50 个已解析的航班)。
- 有时会被静默拦截——提交后字段仍保留正确的值,表单自己的
#loginSubmit已被点击,没有显示错误,DOM 中存在验证码标记,但页面始终不会继续跳转。同一天使用相同凭据和容器进行的实时验证也确认了这一点。
travel 会话的有效期也很短(next-auth 令牌,约 1 小时),因此热会话很快就会过期,登录关卡也会频繁重新出现。登录关卡失败时,脚本会打印一行 Gate diag:(字段/按钮/警报状态),以便通过日志诊断故障,随后立即退出,而不是一直等到搜索结果超时。酒店搜索也可能受到类似影响。
此登录关卡只会出现在新登录时。 如果有已保存的热会话(有效 Cookie + 可信设备),登录关卡会自动通过,搜索也能从头到尾正常运行。因此,恢复方法只需人工操作一次,并非无路可走:
-
如果登录关卡拒绝自动登录(无论是验证码或其他原因),脚本会向 stdout 打印
AMEX_HUMAN_LOGIN_NEEDED(并将HUMAN_LOGIN_NEEDED写入/tmp/amex-2fa-status.txt)。**对于 Agent:**请停止重试,并告知用户运行刷新脚本。(另一个标记AMEX_BAD_CREDENTIALS表示凭据环境变量中包含尚未解析的密钥管理器引用,而非实际值——应修复凭据注入,而不是登录。) -
用户在本地运行,而非在 Docker 中运行:
python3 scripts/refresh_login.py系统会在 travel portal 中打开一个真正的 Chrome 窗口。用户自行登录(输入密码、完成验证码、输入邮件验证码、选择“添加此设备”),脚本会自动保存刷新后的 Cookie/配置文件,完成时打印
AMEX_SESSION_REFRESHED。 -
后续运行(包括挂载
~/.amex-travel-profiles的 Docker 运行)会复用热会话并跳过登录关卡。
为避免会话失效,每隔一两周运行一次简单搜索(或运行 refresh_login.py;它在检测到已登录页面后就会退出)。优先使用住宅 IP;数据中心和酒店 IP 会受到 Akamai 更严格的审查。
工作原理
航班搜索架构
- 身份验证: 从已保存的配置文件注入 Cookie。必要时会回退到新登录并通过邮件进行双重身份验证。
- 填写表单: 基于 DOM 的搜索表单自动化(机场自动补全、日历选择器、舱位选择器)
- 登录关卡: 提交表单后,Amex 会通过登录过渡页进行重定向。脚本会自动处理重新验证(取决于风险;请参阅“已知限制”)。
- 数据提取(新版 UI,2026 年 5 月及之后): 结果会加载到
travel.americanexpress.com/en-us/book/flights/search-results,这是一个无法从window.appData获取有用数据的 Next.js 应用(__NEXT_DATA__仅包含配置)。脚本会解析 DOM 中的[data-testid="offer-card-wrapper"]卡片,其中的航空公司、时间、机场、时长、中转次数、现金价格、积分价格以及原价/现价折扣均有专用的data-testid。 - 数据提取(旧版回退方案): 如果没有出现报价卡片,脚本会回退到旧版的
window.appDataRedux 存储提取方式(627KB JSON 数据块)。 - IAP 检测: 带有
private-fare-banner-PEP*横幅且显示“原价 $X,现价 $Y”现金折扣的卡片属于 IAP(白金会员机票),通常是国际航班前舱票价的 10% 至 15% 折扣。Alaska“Insider Fares”(仅限积分折扣)会通过insider_fare/points_discount单独标记。
酒店搜索架构
- **填写表单:**与航班相同,采用基于 DOM 的方式
- **登录拦截页:**自动处理
- **数据提取:**酒店页面是一个 Next.js 应用,没有
window.appData。脚本使用data-testid="hotel-offer-card"元素解析 DOM。 - **FHR/THC 识别:**通过
data-testid="offer-banner"文本(“Fine Hotels and Resorts” 或 “The Hotel Collection”)识别 - **礼遇提取:**FHR/THC 卡片通过
data-testid="offer-amenities-item"元素显示礼遇(早餐、额度、升级)
数据结构
航班结果(来自 window.appData.flightSearch.itineraries[]):
pricing_information[],其中fare_type=PEP(IAP)或PUB(公开票价)total_price.cents(现金),total_price_in_points(MR 点数,每点相当于 1 美分)segment.legs[],包含承运航司、时间、时长、舱位、机型、设施segment.seats_left、is_refundable、cancellation_policy
酒店结果(通过解析 DOM 获取):
- 酒店名称、星级、城市、距离
- TripAdvisor 评分和评论数
- 每晚价格和总价
- MR 点数成本
- FHR/THC 会员资格及具体礼遇
- 标准设施(Wi-Fi、早餐、停车)
国际航空计划(IAP)
白金卡礼遇。搭乘指定航空公司的国际航班时,可享受高端舱位优惠票价。显示为与 PUB(公开票价)并列的单独 PEP 票价类型。
- 商务舱/头等舱通常可节省 10%–15%
- 并非所有航线或航空公司都适用
- 仅限使用白金卡登录后查看
输出格式
始终使用 Markdown 表格。
航班
| # | 航空公司 | 航线 | 经停 | 时长 | 现金 | IAP 现金 | 点数 | 剩余座位 |
|---|---|---|---|---|---|---|---|---|
| 1 | Turkish | SFO-IST-CDG | 1 | 20h 10m | $5,044 | $4,381 | 438,113 | 3 |
酒店
| # | 酒店 | 计划 | 星级 | 每晚 | 总价 | 点数 | 礼遇 |
|---|---|---|---|---|---|---|---|
| 1 | Hotel Continental | FHR | 5 | $471 | $942 | 94,200 | 早餐、$100 额度、房型升级、延迟至 4pm 退房 |
表格之后
- 标示 IAP 节省金额(显示折扣百分比)
- 说明 FHR/THC 礼遇,以及这些礼遇如何抵扣房价
- 计算 MR 兑换的实际 CPP(在 Amex 门户网站中,1 点 = 1 美分)
- 与转点至航空公司的兑换价值进行比较
- 说明白金卡每年 $600 的酒店额度(每半年 $300,FHR 和 THC 共享)
舱位代码
| CLI 值 | Amex 代码 | 描述 |
|---|---|---|
economy | ECONOMY | 标准经济舱 |
premium | PREMIUM_ECONOMY | 超级经济舱 |
business | BUSINESS | 商务舱 |
first | FIRST | 头等舱 |
环境变量
| 变量 | 必需 | 描述 |
|---|---|---|
AMEX_USERNAME | 是 | Amex 在线账户用户名 |
AMEX_PASSWORD | 是 | Amex 在线账户密码 |
AMEX_PROFILE | 否 | 浏览器配置文件目录(默认:~/.amex-travel-profiles/default) |
AMEX_2FA_COMMAND | 否 | 在邮箱验证码就绪前阻塞并将其打印到标准输出的命令 |
故障排除
- **搜索后出现登录拦截页:**这是正常情况。Amex 在提交表单后总会跳转至登录中间页。脚本会自动处理。
- **未找到 appData(航班):**页面可能尚未完全加载。脚本会等待 Redux 存储填充。请检查是否已成功登录。
- **酒店结果为空:**酒店数据通过解析 DOM 获取,而非 appData。如果 DOM 结构发生变化,可能需要更新
data-testid选择器。 - **日历选择器失效:**Amex 使用
div[role="button"]表示日历日期(而非<button>)。脚本使用类名模式automation-date-picker-month-{year}-{month}查找对应月份的容器。 - **2FA 验证码被拒:**Amex 验证码过期很快。请确保使用的是新验证码(不是上次登录时的旧验证码)。
限制
- 必须使用有头模式。 Amex 会检测无头模式。可通过 Docker+xvfb 绕过。
- 每次搜索约需 45 秒。 登录 + 填写表单 + 登录验证 + 加载结果。
- 酒店结果仅通过 DOM 获取。 酒店无法拦截 API(Next.js 应用的
__NEXT_DATA__为空)。解析器依赖data-testid属性。 - 设备信任有帮助。 首次登录时选择“添加此设备”后,该配置文件可跳过 2FA。通过 Docker 卷挂载保持配置文件持久化。