携程手机版国内机票数据 携程手机版国际机票数据 携程网页版国内机票数据 携程网页版国际机票数据 去哪儿网手机版机票数据 去哪儿网网页版机票数据 携程手机版机票数据添加代理 去哪儿网网页版机票数据添加代理
概述携程网页版机票查询的网址是https://flights.ctrip.com/ 本篇使用北京到首尔的数据。
请求分析查看network,可以很容易找到请求地址是https://flights.ctrip.com/international/search/api/search/batchSearch 其中,headers里面有两个略微复杂的请求参数,transactionid 和 sign,这两个参数拿到后即可直接post请求。 其实,国内机票和国际机票查询的居然是同一个地址。
transactionid获取这个可以通过网络请求直接获取到,比如北京到首尔的数据,通过https://flights.ctrip.com/international/search/api/flightlist/oneway-bjs-sel?_=1&depdate=2021-12-31&cabin=y_s&adult=1&containstax=1 拼接规则就是oneway-出发地三字码-目的地三字码?_=1&depdata=出发日期&cabin=查询舱位&adult=成人数量&containstax=金额是否含税 请求headers中增加accept: application/json,确保返回的是json格式。
模拟的curl如下
curl 'https://flights.ctrip.com/international/search/api/flightlist//oneway-bjs-sel?_=1&depdate=2021-12-31&cabin=y_s&adult=1&containstax=1' \
-H 'accept: application/json' \
--compressed
返回结果为
{
"status":0,
"msg":"success",
"data":{
"adultCount":1,
"childCount":0,
"infantCount":0,
"flightWay":"S",
"cabin":"Y_S",
"scope":"i",
"extensionAttributes":{
"LoggingSampling":false,
"isFlightIntlNewUser":false
},
"transactionID":"8c12c83315fb42fe8a73b3dd9afab405",
"flightSegments":[
{
"departureCityCode":"BJS",
"arrivalCityCode":"SEL",
"departureCityName":"北京",
"arrivalCityName":"首尔",
"departureDate":"2021-12-31",
"departureCountryId":1,
"departureCountryName":"中国",
"departureCountryCode":"CN",
"departureProvinceId":1,
"departureCityId":1,
"arrivalCountryId":42,
"arrivalCountryName":"韩国",
"arrivalCountryCode":"KR",
"arrivalProvinceId":0,
"arrivalCityId":274,
"departureCityTimeZone":480,
"arrivalCityTimeZone":540,
"timeZone":480
}
],
"directFlight":false,
"extGlobalSwitches":{
"useAllRecommendSwitch":true,
"unfoldPriceListSwitch":true
},
"noRecommend":false
}
}
返回结果的data中有transactionID字段。
sign获取sign是直接本地js进行的加密,没有进行网络请求,最终结果是32位md5加密后小写。加密规则为
sign = md5(transactionID + 起始地大写三字码+ 降落的大写三字码 + 出发时间yyyy-MM-dd)
以北京到首尔为例,如果transactionID是a24b0ed8040540bab8cea5f1c7dced62,那么最终结果就是
sign=md5(a24b0ed8040540bab8cea5f1c7dced62BJSSEL2021-12-31)
=4e4ca18f296fc7742e262f116a742f3c
航班数据
headers中参数搞定后,就可以直接请求https://flights.ctrip.com/international/search/api/search/batchSearch 请求参数为(参数基本上都来自请求transactionid的接口)
{
"flightWayEnum":"OW",
"arrivalProvinceId":0,
"extGlobalSwitches":{
"useAllRecommendSwitch":true,
"unfoldPriceListSwitch":true
},
"arrivalCountryName":"韩国",
"infantCount":0,
"cabin":"Y_S",
"cabinEnum":"Y_S",
"departCountryName":"中国",
"flightSegments":[
{
"departureDate":"2021-12-31",
"arrivalProvinceId":0,
"arrivalCountryName":"韩国",
"arrivalCountryCode":"KR",
"departureCityName":"北京",
"departureCityCode":"BJS",
"departureCountryName":"中国",
"departureCountryCode":"CN",
"arrivalCityName":"首尔",
"arrivalCityCode":"SEL",
"departureCityTimeZone":480,
"arrivalCountryId":42,
"timeZone":480,
"departureCityId":1,
"departureCountryId":1,
"arrivalCityTimeZone":540,
"departureProvinceId":1,
"arrivalCityId":274
}
],
"childCount":0,
"segmentNo":1,
"scope":"i",
"adultCount":1,
"extensionAttributes":{
"LoggingSampling":false,
"isFlightIntlNewUser":false
},
"transactionID":"a24b0ed8040540bab8cea5f1c7dced62",
"directFlight":false,
"departureCityId":1,
"isMultiplePassengerType":0,
"noRecommend":false,
"army":"",
"flightWay":"S",
"arrivalCityId":274,
"departProvinceId":1
}
响应结果见Java代码后方。
Java代码和国内机票的基本一致。
public static void main(String[] args) throws Exception {
String url1 = "https://flights.ctrip.com/international/search/api/flightlist//oneway-bjs-sel?_=1&depdate=2021-12-31&cabin=y_s&adult=1&containstax=1";
String result1 = Jsoup.connect(url1).header("accept", "application/json").ignoreContentType(true).method(Method.POST).execute().body();
System.out.println(result1);
JSONObject obj1 = JSON.parseObject(result1);
JSONObject data = obj1.getJSONObject("data");
JSONObject flightSegment = data.getJSONArray("flightSegments").getJSONObject(0);
JSONObject param = data;
param.put("flightWayEnum", "OW"); //单程
param.put("arrivalProvinceId", flightSegment.getString("arrivalProvinceId"));
param.put("arrivalCountryName", flightSegment.getString("arrivalCountryName"));
param.put("cabinEnum", data.getString("cabin"));
param.put("departCountryName", flightSegment.getString("departCountryName"));
param.put("segmentNo", "1");
param.put("departureCityId", flightSegment.getString("departureCityId"));
param.put("isMultiplePassengerType", "0");
param.put("army", "");
param.put("arrivalCityId", flightSegment.getString("arrivalCityId"));
param.put("departProvinceId", flightSegment.getString("departProvinceId"));
String transactionid = obj1.getJSONObject("data").getString("transactionID");
String sign = TextUtils.MD5(transactionid+"BJSSEL2021-12-31").toLowerCase();
String url2 = "https://flights.ctrip.com/international/search/api/search/batchSearch";
String result2 = Jsoup.connect(url2)
.header("sign", sign)
.header("scope", data.getString("scope"))
.header("content-type", "application/json;charset=UTF-8")
.header("accept", "application/json")
.header("transactionid", transactionid)
.header("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Safari/537.36")
.ignoreContentType(true)
.requestBody(param.toString())
.method(Method.POST).execute().body();
System.out.println(result2);
}
其中TextUtil中仅使用的MD5加密,可以使用