当前位置: 首页 > news >正文

携程国际机票查询API逆向分析:从sign参数到完整数据抓取的避坑指南

携程国际机票查询API逆向工程实战:从加密参数破解到数据采集全流程

1. 逆向工程入门:理解携程国际机票查询的核心机制

逆向工程在数据采集领域一直是个充满挑战又极具价值的技术方向。携程作为国内领先的在线旅游服务平台,其国际机票查询接口的设计尤其精妙,包含了多层防护机制。要成功获取数据,我们需要先理解整个查询流程的运作原理。

国际机票查询的核心接口是/api/search/batchSearch,这个POST请求需要两个关键参数:transactionidsign。与国内机票查询不同,国际机票查询在参数验证和反爬策略上更为严格。

请求流程分解

  1. 首先获取transactionid:通过一个预查询接口获得
  2. 然后生成sign参数:基于transactionid和航班信息本地计算
  3. 最后携带这两个参数发起正式查询
// 典型请求头示例 headers: { 'accept': 'application/json', 'content-type': 'application/json;charset=UTF-8', 'transactionid': '8c12c83315fb42fe8a73b3dd9afab405', 'sign': '4e4ca18f296fc7742e262f116a742f3c' }

注意:直接复制这些参数是无效的,因为它们具有时效性和唯一性,必须按照正确算法实时生成。

2. 关键参数解析:transactionid的获取与验证

transactionid是一个32位的唯一标识符,由服务器生成并返回。获取它的接口设计十分巧妙:

GET /international/search/api/flightlist/oneway-{出发地}-{目的地}?_=1&depdate={日期}&cabin=y_s&adult=1&containstax=1

URL参数说明

参数名示例值说明
出发地bjs北京首都机场的三字码(小写)
目的地sel首尔仁川机场的三字码(小写)
depdate2023-08-15出发日期(YYYY-MM-DD格式)
cabiny_s舱位类型,固定值
adult1成人数量
containstax1是否含税,1表示含税

这个接口的响应是一个JSON结构,其中data.transactionID字段就是我们需要的值:

{ "status": 0, "msg": "success", "data": { "transactionID": "a24b0ed8040540bab8cea5f1c7dced62", // 其他字段... } }

常见问题排查

  • 返回403错误:检查请求头是否包含accept: application/json
  • 返回空transactionID:确认日期格式和机场代码是否正确
  • 请求超时:注意接口有频率限制,过快的请求会被暂时屏蔽

3. 签名算法破解:sign参数的生成逻辑

sign参数是整个逆向工程中最具挑战性的部分。经过分析,我们发现它是通过以下方式生成的:

sign = MD5(transactionID + 出发地三字码(大写) + 目的地三字码(大写) + 出发日期)

以北京到首尔为例,如果:

  • transactionID = "a24b0ed8040540bab8cea5f1c7dced62"
  • 出发地 = "BJS"
  • 目的地 = "SEL"
  • 日期 = "2023-08-15"

那么待加密字符串为:

a24b0ed8040540bab8cea5f1c7dced62BJSSEL2023-08-15

MD5加密后得到32位小写哈希值就是最终的sign参数。

Python实现示例

import hashlib def generate_sign(transaction_id, dep_code, arr_code, dep_date): raw_str = f"{transaction_id}{dep_code.upper()}{arr_code.upper()}{dep_date}" return hashlib.md5(raw_str.encode('utf-8')).hexdigest() # 使用示例 sign = generate_sign( "a24b0ed8040540bab8cea5f1c7dced62", "bjs", "sel", "2023-08-15" ) print(sign) # 输出:4e4ca18f296fc7742e262f116a742f3c

技术细节:携程使用的是标准的MD5算法,没有加盐或额外的变形处理,这大大降低了逆向难度。但要注意所有字母都必须大写,日期格式必须严格匹配。

4. 完整请求构建与数据采集

掌握了两个关键参数后,我们就可以构建完整的查询请求了。以下是使用Python的requests库实现的完整示例:

import requests import json # 第一步:获取transactionID dep_code = "bjs" # 出发地代码 arr_code = "sel" # 目的地代码 dep_date = "2023-08-15" # 出发日期 tx_url = f"https://flights.ctrip.com/international/search/api/flightlist/oneway-{dep_code}-{arr_code}?_=1&depdate={dep_date}&cabin=y_s&adult=1&containstax=1" tx_response = requests.get(tx_url, headers={"accept": "application/json"}) tx_data = tx_response.json() transaction_id = tx_data["data"]["transactionID"] # 第二步:生成sign sign = generate_sign(transaction_id, dep_code, arr_code, dep_date) # 第三步:构建查询参数 query_params = { "flightWayEnum": "OW", "arrivalProvinceId": 0, # 其他必要参数... "flightSegments": [ { "departureDate": dep_date, "departureCityCode": dep_code.upper(), "arrivalCityCode": arr_code.upper(), # 其他航班段信息... } ], "transactionID": transaction_id } # 第四步:发送查询请求 search_url = "https://flights.ctrip.com/international/search/api/search/batchSearch" headers = { "accept": "application/json", "content-type": "application/json;charset=UTF-8", "transactionid": transaction_id, "sign": sign } response = requests.post( search_url, headers=headers, data=json.dumps(query_params) ) flight_data = response.json() print(flight_data)

响应数据结构关键字段

  • flightItineraryList: 航班列表
    • flightSegments: 航段信息
    • priceList: 价格信息
      • adultPrice: 成人票价
      • adultTax: 税费
      • baggage: 行李额信息
      • penalty: 退改签规则

5. 高级技巧与反反爬策略

在实际操作中,直接使用上述代码可能会遇到各种反爬措施。以下是几个经过验证的有效策略:

  1. 请求头优化
    • 必须包含完整的headers,特别是User-AgentReferer
    • 模拟真实浏览器的headers组合
headers = { 'authority': 'flights.ctrip.com', 'accept': 'application/json', 'accept-language': 'zh-CN,zh;q=0.9', 'cache-control': 'no-cache', 'content-type': 'application/json;charset=UTF-8', 'origin': 'https://flights.ctrip.com', 'pragma': 'no-cache', 'referer': 'https://flights.ctrip.com/', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' }
  1. 请求频率控制

    • 每个IP每分钟不超过5次查询
    • 在请求间加入随机延迟(1-3秒)
    • 对重要查询使用代理IP轮换
  2. 参数动态生成

    • 每次请求都重新获取transactionID
    • 实时计算sign参数
    • 模拟真实用户操作流程
  3. 错误处理机制

    • 捕获429状态码(请求过多)
    • 自动重试机制(最多3次)
    • 异常情况下的IP切换
from time import sleep import random def safe_request(url, method='GET', headers=None, data=None, max_retries=3): for attempt in range(max_retries): try: sleep(random.uniform(1, 3)) # 随机延迟 if method == 'GET': response = requests.get(url, headers=headers) else: response = requests.post(url, headers=headers, json=data) if response.status_code == 429: sleep(10) # 遇到限流等待10秒 continue return response except Exception as e: print(f"Attempt {attempt + 1} failed: {str(e)}") if attempt == max_retries - 1: raise

6. 数据解析与存储优化

获取到原始数据后,如何高效解析和存储也是关键。以下是几个实用建议:

数据结构优化

  • 提取核心字段,去除冗余信息
  • 将嵌套的JSON结构扁平化
  • 为常用查询字段建立索引

Python解析示例

def parse_flight_data(raw_data): flights = [] for itinerary in raw_data.get('data', {}).get('flightItineraryList', []): for segment in itinerary.get('flightSegments', []): for flight in segment.get('flightList', []): flight_info = { 'flight_no': flight.get('flightNo'), 'departure': { 'city': flight.get('departureCityName'), 'airport': flight.get('departureAirportName'), 'time': flight.get('departureDateTime') }, 'arrival': { 'city': flight.get('arrivalCityName'), 'airport': flight.get('arrivalAirportName'), 'time': flight.get('arrivalDateTime') }, 'duration': flight.get('duration'), 'aircraft': flight.get('aircraftName'), 'prices': [] } for price in itinerary.get('priceList', []): flight_info['prices'].append({ 'type': price.get('eligibility'), 'price': price.get('adultPrice'), 'tax': price.get('adultTax'), 'cabin': price.get('cabin'), 'baggage': price.get('baggage', {}).get('dataList', [{}])[0].get('adultBaggage', {}) }) flights.append(flight_info) return flights

存储方案对比

存储类型优点缺点适用场景
MySQL事务支持完善,查询能力强需要预先定义schema结构化数据存储
MongoDB灵活,适合JSON数据占用空间较大原始数据存储
Elasticsearch搜索性能优异维护成本高需要全文检索的场景
CSV/JSON文件简单易用查询效率低小规模临时存储

7. 实战经验与避坑指南

在实际项目中,我们积累了一些宝贵经验:

  1. 机场代码问题

    • 携程使用的是三字码(如PEK),不是IATA的两字码
    • 部分城市有多个机场(如上海有SHA/PVG),需要明确指定
    • 国际机场代码大小写敏感,必须与服务器端完全一致
  2. 日期格式陷阱

    • 请求参数中的日期格式为YYYY-MM-DD
    • 响应中的日期时间格式为YYYY-MM-DD HH:MM:SS
    • 时区问题:所有时间都是当地时间,没有UTC转换
  3. 价格显示规则

    • adultPrice是基础票价,不含税
    • adultTax是税费,实际支付金额为两者之和
    • 不同舱位(Y/S/M等)价格差异可能很大
  4. 反爬升级应对

    • 定期检查接口变化(大约每3-6个月会有小调整)
    • 准备多套请求头轮换使用
    • 监控成功率,低于90%时需要检查算法是否变更

常见错误排查表

错误现象可能原因解决方案
返回"status":-1sign计算错误检查MD5算法和输入参数顺序
空结果集日期太近/太远尝试调整查询日期
连接超时IP被封更换IP,增加延迟
数据不全参数缺失检查是否传递了所有必需参数
http://www.jsqmd.com/news/921741/

相关文章:

  • AMD锐龙平台避坑指南:VMware 16安装macOS BigSur完整流程(附常见错误解决)
  • 最新南通市贵金属全品类黄金回收白银回收铂金回收 黄金变现避坑,专业回收全程透明:实力口碑排行榜门店及联系方式推荐 - 前途无量YY
  • WeaveMuse多代理协同架构与量化部署解析
  • ShaderGraph数学节点避坑指南:DDX/DDY、矩阵、向量操作,新手最容易犯的5个错误
  • 2026最新呼伦贝尔市黄金+K金+铂金+白银回收,五家正规靠谱实力排行榜门店推荐及联系方式 - 亦辰小黄鸭
  • 模拟IC设计避坑指南:电流镜负载差分放大器那些容易忽略的设计细节
  • Kubernetes控制器的通用工作模式(Reconcile Loop)【20260530】004篇
  • 2026最新酒泉市黄金+K金+铂金+白银回收,五家正规靠谱实力排行榜门店推荐及联系方式 - 亦辰小黄鸭
  • 2026最新开封市黄金+K金+铂金+白银回收,五家正规靠谱实力排行榜门店推荐及联系方式 - 亦辰小黄鸭
  • Ubuntu 22.04下用Snap一键安装CloudCompare,附点云裁剪与标注实战
  • 最新南阳市贵金属全品类黄金回收白银回收铂金回收 黄金变现避坑,专业回收全程透明:实力口碑排行榜门店及联系方式推荐 - 前途无量YY
  • Kubernetes控制器的通用工作模式(Reconcile Loop)【20260530】005篇-真实生产 Controller 的坑位图
  • 基于CircuitPython与舵机的可穿戴鲨鱼头盔制作指南
  • 2026最新湖州市黄金+K金+铂金+白银回收,五家正规靠谱实力排行榜门店推荐及联系方式 - 亦辰小黄鸭
  • 告别混乱布局!Unity编辑器窗口自定义全攻略(附高效工作流配置)
  • 用Python+Word批量生成骰子游戏教具:从A4卡纸到幼儿课堂的完整避坑指南
  • 告别手动!为你的Unity团队定制一个‘图片导入设置预设’与批量应用工具
  • Kubernetes控制器的通用工作模式(Reconcile Loop)【20260530】006篇-真实生产 Controller 的坑位图
  • 保姆级教程:在VMware Workstation上安装Ubuntu 22.04 LTS虚拟机(附国内镜像源和避坑指南)
  • 告别双击安装失败!在统信UOS上通过命令行搞定Citrix客户端(arm64架构实测)
  • 最新内江市贵金属全品类黄金回收白银回收铂金回收 黄金变现避坑,专业回收全程透明:实力口碑排行榜门店及联系方式推荐 - 前途无量YY
  • 解放双手!Alas碧蓝航线自动化脚本全面指南:从零开始实现游戏全托管
  • 2026最新怀化市黄金+K金+铂金+白银回收,五家正规靠谱实力排行榜门店推荐及联系方式 - 亦辰小黄鸭
  • 2026最新昆明市黄金+K金+铂金+白银回收,五家正规靠谱实力排行榜门店推荐及联系方式 - 亦辰小黄鸭
  • 2026最新抚州市黄金+K金+铂金+白银回收,五家正规靠谱实力排行榜门店推荐及联系方式 - 亦辰小黄鸭
  • 告别rm -f:在Windows命令行里优雅地‘强制删除’被锁文件(ForceDelete CLI教程)
  • 从Unity Shader Graph到原生GLSL:写给美术和TA的着色器迁移指南
  • 如何轻松实现百度网盘高速下载:3步实战指南
  • 避坑指南:Unity LineRenderer做动态闪电效果,性能优化和常见Bug修复实录
  • ROS2 Navigation2 实战:手把手教你用TurtleBot3在Gazebo里完成自主导航(设置初始位置与目标点)