机票价格抓取的四个固定输入
把货币、区域设置、销售点和设备固定住,票价差异才是价格差异;并在每次抓取时把这四项与报价一起记录下来。
BotBrowser Team
只有当每个请求背后的环境保持恒定时,机票价格抓取才会产生可用的序列。票价是按货币、区域设置、销售点和设备报出的,改动其中任何一项都可能改变数字,而市场本身毫无变化。BotBrowser 文档说明了由配置文件驱动的区域设置、时区与语言输入,以及地理对齐的出口,因此一次抓取可以声明它所代表的环境。它不提供票价、不订票、不控制航空公司的定价逻辑,也不保证报价会再次出现。存在持牌票价数据源或代理 API 时,应当使用那条路径。
摘要
- 票价是按特定的货币、区域设置、销售点和设备报出的。四项都要固定,否则测到的差异有一部分是环境差异。
- 货币是请求或账户设置,不是浏览器配置文件的取值。区域设置与语言来自配置文件。销售点是站点参数,必须与出口区域和账户一致。
- 把四项随每一次报价一起记录,而不是每次运行记一次。
- 按哪一项发生变化来给差异分类。货币变化不是票价变动,无论数字变化多大。
- 存在持牌票价数据源或代理 API 时优先使用。一次浏览器运行既不会产生监控许可,也无法订票。
目录
会改变票价的四项输入
一次航班搜索携带的不只是出发地、目的地和日期。它还携带货币、语言偏好、行程所面向的市场,以及可能选中另一组报价的设备类别。这四项不能互相替代,一组让它们变化的监控序列,测量自己的配置和测量票价一样多。
| 输入 | 在哪里设置 | 可能改变什么 |
|---|---|---|
| 货币 | 请求参数或账户设置 | 报价金额及其取整方式 |
| 区域设置 | 配置文件中的区域设置与语言列表 | 页面语言、格式,有时还有报价集合 |
| 销售点 | 站点参数、出口区域与账户三者共同决定 | 适用哪一套库存与规则 |
| 设备 | 视口与配置文件中的设备取值 | 布局,有时是仅移动端可见的报价 |
schema.org 的 Offer 类型在这里给出了有用的词汇形状。它定义了 price,并建议搭配 priceCurrency,使用 ISO 4217 代码而不是含糊的符号,同时提醒不要在数值内部使用分隔符。这条指引谈的是发布报价,却直接对应到抓取问题:记录成 $412 的票价不是事实,而 412 USD 是可比较的观察。
第二项与第三项最容易被混为一谈。区域设置是浏览器声明的东西。销售点是站点为哪个市场定价,通常由站点参数、请求看起来来自的区域以及账户自身的设置共同推导。让浏览器声明德语,本身并不会让票价变成德国销售点的票价;从德国地址出口,本身也不会设定货币。
货币值得单独一段,因为它是最常被隐含掉的输入。站点可能接受显式货币参数、从销售点推导,或从账户偏好读取,这三条路径可能互相矛盾。矛盾时页面通常显示一种货币而结账显示另一种,或者同一货币套用了不同汇率。请记录页面实际显示的货币,而不是请求索要的货币,并把销售点记录在旁边,因为正是这一对才让金额可复现。
取整是更小但持续存在的噪声源。从一种货币换算到另一种的票价按该货币特有的精度取整,有些货币取整到整数单位,另一些保留两位小数。因此同一张票价在同一货币下的两次抓取应当完全一致;若相差不足一个单位,差异通常来自换算而非票价变化,记录应当这样标注,而不是把这个零头当作变动。
税费属于同一类问题。显示的金额可能含税、不含税,或只含取决于销售点的某个子集,而包含范围一变,看起来就和价格变化一模一样。页面说明时,请记录显示金额包含什么,并把该字段与金额放在一起。一组从含税显示切换到不含税显示的序列,会呈现一个与票价毫无关系的台阶。
配置文件究竟固定了哪几项
BotBrowser 的时区、区域设置与语言文档描述了在配置代理时从出口 IP 自动推导,以及对手动设置时区、区域设置与语言的覆盖,其中手动覆盖需要 ENT Tier1 许可。GeoIP 文档描述了把出口地址翻译成地理默认值,并直白说明了精度边界:国家级映射通常可靠,城市级映射可能落到邻近城市或区域。
这覆盖了表格中区域设置那一半,以及销售点的一部分。它不覆盖货币,也没有完全覆盖销售点,因为站点可以在与所服务语言无关的情况下接受货币参数,这属于站点行为,而不是有文档的浏览器输入。一个假定「配置文件会处理一切」的监控设计,实际只固定了四项中的两项,却以为固定了四项。
| 输入 | 配置文件是否固定 | 需要显式检查 |
|---|---|---|
| 货币 | 否 | 请求参数或账户设置 |
| 区域设置 | 配置后是 | 页面实际报告的语言列表 |
| 销售点 | 部分 | 站点自己给出的市场指示 |
| 设备 | 配置后是 | 视口与页面报告的设备取值 |
实际后果是:检查必须按每次抓取做,而不是按配置做。一开始配置正确的运行,只要有人改了请求参数、加了覆盖项,或为某个区域换了代理,就会漂移。在报价那一刻记录四项,是唯一能知道究竟哪几项生效的办法。
设备类别是最会隐藏的一项。桌面视口与移动视口可能返回不同报价集合,有些航空公司会放出仅移动端存在的票价,桌面路径上根本不存在。那不是同一报价两次观察之间的价格变化,而是另一个报价。请把视口与设备类别一起记录,让后来的读者能判断两次抓取是否用了同一个视口,并把报价集合的变化当作设备效应而不是票价变动。
环境内部还有时间维度。促销期内的一次抓取与一小时后的另一次抓取可能不同,因为促销结束了;这是来源侧变化,四项输入正确地报告为未变。这是正确的结果:记录正在发挥作用,排除了抓取作为解释。要避免的是反向错误,把货币切换能解释的差异归咎于来源。
最后一条习惯成本很低,值得写出。请在票价页面加载之前记录四项,而不是之后,并把加载前的值留在记录里。页面可能在自身客户端初始化期间改掉区域设置或货币,因此报价之后读到的值描述的是最终状态,而不是请求携带的状态。两者不同时都记下来,就把一处令人困惑的差异变成了有据可查的转变。
每次抓取都记录这四项
票价监控的抓取记录很小。它需要行程、带货币的报价金额、四项输入以及观察时间。其余都是辅助细节。
| 字段 | 内容 | 为什么在记录里 |
|---|---|---|
itinerary_key | 请求的航线、日期与舱位 | 定义比较的对象 |
amount | 带小数点的数值 | 只有配合货币才可比较 |
currency | ISO 4217 代码 | 避免把符号读成金额 |
accept_language | 抓取时声明的语言列表 | 显示生效的区域设置 |
point_of_sale | 站点指示的市场 | 把市场与语言分开 |
device_class | 桌面或移动,附视口 | 把布局与票价分开 |
observed_at | 抓取时间(UTC) | 排序观察并暴露排期变化 |
alignment | aligned 或发生变化的那一项 | 阻止不可比的行进入序列 |
表中有两处很容易记错。amount 应当是带小数点的数值而不是格式化字符串,因为字符串携带所在区域的分隔符习惯,一旦序列跨两个区域就变得含糊。point_of_sale 应当是站点指示的内容,而不是操作方的意图:站点没有兑现的意图,正是最值得抓出来的情形。
下面的记录构造器比较同一行程的两次抓取,并报告哪些输入保持不变。用例是构造的。
// Constructed fixture: two captures of one itinerary, one input changed.
const captureA = {
itineraryKey: 'YYZ-LHR 2026-12-10 Y',
amount: 812.4,
currency: 'CAD',
acceptLanguage: 'en-CA,en',
pointOfSale: 'CA',
deviceClass: 'desktop',
observedAt: '2026-11-10T09:00:00Z',
};
const captureB = {
itineraryKey: 'YYZ-LHR 2026-12-10 Y',
amount: 512.1,
currency: 'EUR',
acceptLanguage: 'en-CA,en',
pointOfSale: 'CA',
deviceClass: 'desktop',
observedAt: '2026-11-10T09:00:30Z',
};
const FIELDS = ['currency', 'acceptLanguage', 'pointOfSale', 'deviceClass'];
const changed = FIELDS.filter(f => captureA[f] !== captureB[f]);
const alignment = changed.length === 0 ? 'aligned' : `input_changed:${changed.join(',')}`;
const lines = [
`itinerary=${captureA.itineraryKey}`,
`a=${captureA.amount} ${captureA.currency} b=${captureB.amount} ${captureB.currency}`,
`currency=${captureA.currency}->${captureB.currency} locale=${captureA.acceptLanguage}`,
`pos=${captureA.pointOfSale} device=${captureA.deviceClass}`,
`changed=${changed.join(',') || 'none'} alignment=${alignment}`,
`observed_at=${captureA.observedAt}->${captureB.observedAt}`,
];
console.log(lines.join('\n'));
下面的构造输出正是这个练习的目的。金额下降了约 37%,而分类说明这次下跌根本不是票价变动。
itinerary=YYZ-LHR 2026-12-10 Y
a=812.4 CAD b=512.1 EUR
currency=CAD->EUR locale=en-CA,en
pos=CA device=desktop
changed=currency alignment=input_changed:currency
observed_at=2026-11-10T09:00:00Z->2026-11-10T09:00:30Z
按发生变化的输入来读价格差异
差异的大小说明不了原因。有用的问题是哪些输入保持不变,因为这决定了这对抓取能支持什么。
| 保持不变的输入 | 观察 | 差异能支持什么 |
|---|---|---|
| 四项完全相同 | 金额不同 | 该环境下可比的价格变化 |
| 只有货币不同 | 金额不同 | 换算或重新定价,不是票价变动 |
| 只有销售点不同 | 金额不同 | 两个市场;彼此都不是对方的基线 |
| 只有设备类别不同 | 金额或报价集合变化 | 依赖设备的报价,不是票价变化 |
只有第一行能支持票价序列。第二行是实践中最常见的错误,因为货币切换会把数字改动一个很大的倍数,得到的图表看起来像剧烈波动。第三行是市场比较,必须建成两条序列而不是一条。第四行是仅移动端报价冒充价格变化的地方。
还有两点提醒属于读法而不是记录。第一,报价不是可订价格:税费与可售情况可能在报价与结账页之间变化。第二,环境未变的重复抓取仍可能不同,因为航空公司自身的库存或票价规则变了;这是真实的观察,应当标注为来源侧变化,而不是抓取错误。
价格监控架构 覆盖一般价格序列的调度与变化检测。旅行场景增加的是:这四项输入彼此之间的独立性比零售更强,因为零售中单个商品页通常隐含一种货币和一种区域设置。配置文件与路由一致性记录 是销售点出口那一半的恰当补充。 可用性日历需要按日期为键而不是按商品为键,可用性模型 说明了如何让这份日历在多次抓取之间保持可比。
何时应改用持牌票价数据源
多数航空公司与大型代理都提供持牌路径:票价分发数据源、代理 API,或约定的数据导出。这些路径以已定义的 schema、已声明的更新频率,以及一份说明数据可用于什么的许可,提供同样的行程。存在这样的路径时,浏览器运行通常是错误的工具,因为它为一个已经提供受支持路径的来源,产出更不稳定的序列并带来更多法律暴露。
不存在这类路径时,诚实的问题是:究竟是什么授权了这次观察。一个可用的浏览器会话不是答案。适用的航空公司或代理条款说了算,而它们经常限制自动访问、票价数据转售和缓存价格的展示。请把运行的依据与抓取一起记录,让后来的读者继承答案而不是假定。
schema.org 的 FlightReservation 类型从另一侧提醒同一条界线。它为预订定义了词汇,含 bookingTime、乘客字段,以及指向航班的 reservationFor。它描述预订可以如何表示;它不说明票价是否可被监控,也不说明结账时的报价会是多少。
这套词汇仍然有助于命名抓取保持分离的东西。预订不是报价,报价也不是票价规则。当一份记录把三者混在一起,后来的比较就无法判断航空公司改的是价格、行程,还是附带条件。请让行程键描述所请求的内容,让金额与货币描述所报出的内容,让四项环境输入描述报价是如何取得的。
常见问题
货币变化是价格变化吗?
不是。它改变金额的单位,通常也改变取整方式。中途切换货币的序列测量的是这次切换,因此每条序列只保留一种货币,或用已声明的汇率换算并记录换算。
浏览器配置文件能固定销售点吗?
只能部分固定。配置文件控制区域设置、语言,以及通过代理体现的表面区域。站点的销售点常常是单独的参数或账户设置,因此请核实站点指示的内容,而不是操作方的意图。
同一行程总是返回同一价格吗?
不是。库存与票价规则会变化,报价也不是结账总额。环境未变的重复抓取仍可能不同,而那个差异属于来源。
BotBrowser 提供票价或订票吗?
不提供。它提供有文档的浏览器输入,让一次抓取可以声明自己的环境。它不提供票价数据、不订票,也不保证报价。
来源
- schema.org FlightReservation
- schema.org Offer
- MDN Accept-Language
- BotBrowser 时区、区域设置与语言
- BotBrowser GeoIP 数据库
实践结论
在第一次抓取之前,先决定这组序列代表哪种货币、区域设置、销售点与设备。每次抓取都核实这四项,因为配置文件完全固定其中两项、部分固定一项,并把它们与报价金额、观察时间记录在一起。按发生变化的输入给每个差异分类,并把货币切换当作环境变化而不是票价变动。存在持牌票价数据源或代理 API 时,使用它,并把浏览器运行排除在设计之外。
本文属于指南: 代理配置总览:浏览器网络决策图