本文接上一篇《从零实现一款12306抢票软件(二)》。
其他接口就不一一介绍了,所有的原理都是一样的,作者可以自行探索。当然,我已经将所有的接口都探索完了,并实现了,我这里贴一下吧:
/**
*@desc: 封装获取验证码、校验验证码、登录等12306各个请求的类,Client12306.h文件
*@author: zhangyl
*@date: 2017.01.17
*/
#ifndef __CLIENT_12306_H__
#define __CLIENT_12306_H__
#include <vector>
#include <string>
using namespace std;
//车次类型
#define TRAIN_GC 0x00000001
#define TRAIN_D (0x00000001 << 1)
#define TRAIN_Z (0x00000001 << 2)
#define TRAIN_T (0x00000001 << 3)
#define TRAIN_K (0x00000001 << 4)
#define TRAIN_OTHER (0x00000001 << 5)
#define TRAIN_ALL (TRAIN_GC | TRAIN_D | TRAIN_Z | TRAIN_T | TRAIN_K | TRAIN_OTHER)
//票信息
struct queryLeftNewDTO
{
string train_no;
string station_train_code;
string start_station_telecode; //始发站
string start_station_name;
string end_station_telecode; //终点站
string end_station_name;
string from_station_telecode; //出发站
string from_station_name; //到达站
string to_station_telecode;
string to_station_name;
string start_time;
string arrive_time;
string day_difference;
string train_class_name;
string lishi;
string canWebBuy;
string lishiValue;
string yp_info;
string control_train_day;
string start_train_date;
string seat_feature;
string yp_ex;
string train_seat_feature;
string seat_types;
string location_code;
string from_station_no;
string to_station_no;
string control_day;
string sale_time;
string is_support_card;
string controlled_train_flag;
string controlled_train_message;
string train_type_code;
string start_province_code;
string start_city_code;
string end_province_code;
string end_city_code;
string swz_num; //商务座
string rz_num; //软座
string yz_num; //硬座
string gr_num; //高级软卧
string rw_num; //软卧
string yw_num; //硬卧
string tz_num; //特等座
string zy_num; //一等座
string ze_num; //二等座
string wz_num; //无座
string gg_num;
string yb_num;
string qt_num;
};
struct ticketinfo
{
queryLeftNewDTO DTO;
string secretStr;
string buttonTextInfo;
};
//var station_names = '@bjb|北京北|VAP|beijingbei|bjb|0@bjd|北京东|BOP|beijingdong|bjd|1@bji|北京|BJP|beijing|bj|2
struct stationinfo
{
string code1;
string hanzi;
string code2;
string pingyin;
string simplepingyin;
int no;
};
struct passenager
{
string code; //"8"
string passenger_name; //"范蠡"
string sex_code;// "M"
string sex_name; // "男"
string born_date; //"1989-12-08 00:00:00"
string country_code;// "CN"
string passenger_id_type_code;// "1"
string passenger_id_type_name; // "二代身份证"
string passenger_id_no; // "14292919781105815X"
string passenger_type; // "1"
string passenger_flag; // "0"
string passenger_type_name; // "成人"
string mobile_no; // "13917043320"
string phone_no;
string email; // "balloonxxx@qq.com"
string address; // ""
string postalcode; // ""
string first_letter;// ""
string recordCount;// "13"
string total_times;// "99"
string index_id;// "0"
};
class Client12306
{
public:
static Client12306& GetInstance();
private:
Client12306();
~Client12306();
private:
Client12306(const Client12306&);
Client12306& operator=(const Client12306&);
public:
bool ReloadVCodeImage();
/**
* 游客查票
* https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-05-24&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=SHH&purpose_codes=ADULT
* 应答:{“validateMessagesShowId”:”_validatorMessage”,”status”:true,”httpstatus”:200,”messages”:[],”validateMessages”:{}}
*@param: train_date列车发车日期,格式:2017-01-28
*@param: from_station出发站,格式:SHH 对应上海
*@parma: to_station到站,格式:BJP 对应北京
*@param: purpose_codes 票类型,成人票:ADULT 学生票:0X00
*@param: v 查票结果
*/
bool GuestQueryTicket(const char* train_date, const char* from_station, const char* to_station, const char* purpose_codes, vector<ticketinfo>& v);
/**
* 初始化session,获取JSESSIONID
*/
bool loginInit();
bool DownloadVCodeImage(const char* module = "login");
/**
*@return 0校验成功;1校验失败;2校验出错
*/
int checkRandCodeAnsyn(const char* vcode);
/**
*@return 0校验成功;1校验失败;2校验出错
*/
int loginAysnSuggest(const char* user, const char* pass, const char* vcode);
/**
* 正式登录
*/
bool userLogin();
/**
* 模拟12306跳转
*/
bool initMy12306();
/**
* 拉取乘客买票验证码
*/
//bool GetVCodeImage();
/**
* 拉取乘客买票验证码
*/
/**
* 查询余票第一步
* https://kyfw.12306.cn/otn/leftTicket/log?leftTicketDTO.train_date=2017-02-08&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=NJH&purpose_codes=ADULT
* 应答:{“validateMessagesShowId”:”_validatorMessage”,”status”:true,”httpstatus”:200,”messages”:[],”validateMessages”:{}}
*@param: train_date列车发车日期,格式:2017-01-28
*@param: from_station出发站,格式:SHH 对应上海
*@parma: to_station到站,格式:BJP 对应北京
*@param: purpose_codes 票类型,成人票:ADULT 学生票:0X00
*/
bool QueryTickets1(const char* train_date, const char* from_station, const char* to_station, const char* purpose_codes);
/**
* 查询余票第二步
* 这几种情形都有可能,所以应该都尝试一下
* https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=2017-02-08&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=NJH&purpose_codes=ADULT
* https://kyfw.12306.cn/otn/leftTicket/queryX?leftTicketDTO.train_date=2017-02-08&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=NJH&purpose_codes=ADULT
* https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-02-08&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=NJH&purpose_codes=ADULT
* {"status":false,"c_url":"leftTicket/query","c_name":"CLeftTicketUrl"}
* {"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"messages":["非法请求"],"validateMessages":{}}
* 应答中含有实际余票信息
*@param: train_date列车发车日期,格式:2017-01-28
*@param: from_station出发站,格式:SHH 对应上海
*@parma: to_station到站,格式:BJP 对应北京
*@param: purpose_codes 票类型,成人票:ADULT 学生票:0X00
*/
bool QueryTickets2(const char* train_date, const char* from_station, const char* to_station, const char* purpose_codes, vector<ticketinfo>& v);
/**
* 检测用户是否登录
* https://kyfw.12306.cn/otn/login/checkUser POST _json_att=
* Cookie: JSESSIONID=0A01D967FCD9827FC664E43DEE3C7C6EF950F677C2; __NRF=86A7CBA739653C1CC2C3C3AA7C88A1E3; BIGipServerotn=1742274826.64545.0000; BIGipServerportal=3134456074.17695.0000; current_captcha_type=Z; _jc_save_fromStation=%u4E0A%u6D77%2CSHH; _jc_save_toStation=%u5357%u4EAC%2CNJH; _jc_save_fromDate=2017-01-22; _jc_save_toDate=2017-01-22; _jc_save_wfdc_flag=dc
* {"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"flag":true},"messages":[],"validateMessages":{}}
*/
bool checkUser();
/**
* 预提交订单 POST
* https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest?secretStr=secretStr&train_date=2017-01-21&back_train_date=2016-12-23&tour_flag=dc&purpose_codes=ADULT&query_from_station_name=深圳&query_to_station_name=武汉&undefined=
*/
bool submitOrderRequest(const char* secretStr, const char* train_date, const char* back_train_date, const char* tour_flag, const char* purpose_codes, const char* query_from_station_name, const char* query_to_station_name);
/**
* 模拟跳转页面InitDc,Post
*/
bool initDc();
/**
* 拉取常用联系人 POST
* https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs?_json_att=&REPEAT_SUBMIT_TOKEN=SubmitToken
*/
bool getPassengerDTOs(vector<passenager>& v);
/**
* 购票人确定
* https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo
@param oldPassengerStr oldPassengerStr组成的格式:乘客名,passenger_id_type_code,passenger_id_no,passenger_type,’_’
示例: 范蠡,1,14292919781105815X,1_
@param passengerTicketStr passengerTicketStr组成的格式:seatType,0,票类型(成人票填1),乘客名,passenger_id_type_code,passenger_id_no,mobile_no,’N’
示例: O,0,1,范蠡,1,14292919781105815X,13817056720,N 101
@tour_flag dc表示单程票
应答:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"ifShowPassCode":"N","canChooseBeds":"N","canChooseSeats":"N","choose_Seats":"MOP9","isCanChooseMid":"N","ifShowPassCodeTime":"1","submitStatus":true,"smokeStr":""},"messages":[],"validateMessages":{}}
*/
bool checkOrderInfo(const char* oldPassengerStr, const char* passengerTicketStr, const char* tour_flag, bool& bVerifyVCode);
/**
* 准备进入排队
* https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount
_json_att 10
fromStationTelecode VNP 23
leftTicket enu80ehMzuVJlK2Q43c6kn5%2BzQF41LEI6Nr14JuzThrooN57 63
purpose_codes 00 16
REPEAT_SUBMIT_TOKEN 691c09b5605e46bfb2ec2380ee65de0e 52
seatType O 10
stationTrainCode G5 19
toStationTelecode AOH 21
train_date Fri Feb 10 00:00:00 UTC+0800 2017 50
train_location P2 17
train_no 24000000G502 21
应答:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"count":"4","ticket":"669","op_2":"false","countT":"0","op_1":"true"},"messages":[],"validateMessages":{}}
*/
bool getQueueCount(const char* fromStationTelecode, const char* leftTicket, const char* purpose_codes, const char* seatType, const char* stationTrainCode, const char* toStationTelecode, const char* train_date, const char* train_location, const char* train_no);
/**
* 确认购买
* https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue
_json_att 10
choose_seats 13
dwAll N 7
key_check_isChange 7503FD317E01E290C3D95CAA1D26DD8CFA9470C3643BA9799D3FB753 75
leftTicketStr enu80ehMzuVJlK2Q43c6kn5%2BzQF41LEI6Nr14JuzThrooN57 66
oldPassengerStr 范蠡,1,14292919781105815X,1_ 73
passengerTicketStr O,0,1,范蠡,1,14292919781105815X
,13917043320,N 101
purpose_codes 00 16
randCode 9
REPEAT_SUBMIT_TOKEN 691c09b5605e46bfb2ec2380ee65de0e 52
roomType 00 11
seatDetailType 000 18
train_location P2 17
应答:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"submitStatus":true},"messages":[],"validateMessages":{}}
*/
bool confirmSingleForQueue(const char* leftTicketStr, const char* oldPassengerStr, const char* passengerTicketStr, const char* purpose_codes, const char* train_location);
/**
* 查询订单状态: https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime?random=1486368851278&tourFlag=dc&_json_att=&REPEAT_SUBMIT_TOKEN=691c09b5605e46bfb2ec2380ee65de0e
GET
_json_att
random 1486368851278
REPEAT_SUBMIT_TOKEN 691c09b5605e46bfb2ec2380ee65de0e
tourFlag dc
响应:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"queryOrderWaitTimeStatus":true,"count":0,"waitTime":-1,"requestId":6234282826330508533,"waitCount":0,"tourFlag":"dc","orderId":"E061149209"},"messages":[],"validateMessages":{}}
*/
bool queryOrderWaitTime(const char* tourflag, string& orderId);
/**
* https://kyfw.12306.cn/otn/confirmPassenger/resultOrderForDcQueue POST
_json_att 10
orderSequence_no E061149209 27
REPEAT_SUBMIT_TOKEN 691c09b5605e46bfb2ec2380ee65de0e 52
{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"submitStatus":true},"messages":[],"validateMessages":{}}
*/
//bool resultOrderForDcQueue();
/**
* 未完成的订单页面 https://kyfw.12306.cn/otn/queryOrder/initNoComplete GET
* 获取未完成的订单 https://kyfw.12306.cn/otn/queryOrder/queryMyOrderNoComplete POST _json_att=
*/
/*
{
"validateMessagesShowId": "_validatorMessage",
"status": true,
"httpstatus": 200,
"data": {
"orderDBList": [
{
"sequence_no": "E079331507",
"order_date": "2017-02-09 10:10:55",
"ticket_totalnum": 1,
"ticket_price_all": 55300,
"cancel_flag": "Y",
"resign_flag": "4",
"return_flag": "N",
"print_eticket_flag": "N",
"pay_flag": "Y",
"pay_resign_flag": "N",
"confirm_flag": "N",
"tickets": [
{
"stationTrainDTO": {
"trainDTO": {},
"station_train_code": "G41",
"from_station_telecode": "VNP",
"from_station_name": "北京南",
"start_time": "1970-01-01 09:16:00",
"to_station_telecode": "AOH",
"to_station_name": "上海虹桥",
"arrive_time": "1970-01-01 14:48:00",
"distance": "1318"
},
"passengerDTO": {
"passenger_name": "范蠡",
"passenger_id_type_code": "1",
"passenger_id_type_name": "二代身份证",
"passenger_id_no": "14262319781108815X",
"total_times": "98"
},
"ticket_no": "E079331507110008B",
"sequence_no": "E079331507",
"batch_no": "1",
"train_date": "2017-02-11 00:00:00",
"coach_no": "10",
"coach_name": "10",
"seat_no": "008B",
"seat_name": "08B号",
"seat_flag": "0",
"seat_type_code": "O",
"seat_type_name": "二等座",
"ticket_type_code": "1",
"ticket_type_name": "成人票",
"reserve_time": "2017-02-09 10:10:55",
"limit_time": "2017-02-09 10:10:55",
"lose_time": "2017-02-09 10:40:55",
"pay_limit_time": "2017-02-09 10:40:55",
"ticket_price": 55300,
"print_eticket_flag": "N",
"resign_flag": "4",
"return_flag": "N",
"confirm_flag": "N",
"pay_mode_code": "Y",
"ticket_status_code": "i",
"ticket_status_name": "待支付",
"cancel_flag": "Y",
"amount_char": 0,
"trade_mode": "",
"start_train_date_page": "2017-02-11 09:16",
"str_ticket_price_page": "553.0",
"come_go_traveller_ticket_page": "N",
"return_deliver_flag": "N",
"deliver_fee_char": "",
"is_need_alert_flag": false,
"is_deliver": "N",
"dynamicProp": "",
"fee_char": "",
"insure_query_no": ""
}
],
"reserve_flag_query": "p",
"if_show_resigning_info": "N",
"recordCount": "1",
"isNeedSendMailAndMsg": "N",
"array_passser_name_page": [
"范蠡"
],
"from_station_name_page": [
"北京南"
],
"to_station_name_page": [
"上海虹桥"
],
"start_train_date_page": "2017-02-11 09:16",
"start_time_page": "09:16",
"arrive_time_page": "14:48",
"train_code_page": "G41",
"ticket_total_price_page": "553.0",
"come_go_traveller_order_page": "N",
"canOffLinePay": "N",
"if_deliver": "N",
"insure_query_no": ""
}
],
"to_page": "db"
},
"messages": [],
"validateMessages": {}
}
*/
/**
* 已完成订单(改/退) : https://kyfw.12306.cn/otn/queryOrder/queryMyOrder POST
* queryType 1 按订票日期 2 按乘车日期
* 查询日期queryStartDate=2017-02-09&queryEndDate=2017-02-09
* come_from_flag: my_order 全部 my_resign 可改签 my_cs_resign 可变更到站 my_refund 可退票
* &pageSize=8&pageIndex=0&
* query_where G 未出行订单 H 历史订单
* sequeue_train_name 订单号/车次/乘客姓名
*/
/* 历史订单格式
参见[历史订单.txt]
*/
/**
* 获取全国车站信息
*@param si 返回的车站信息
*@param bForceDownload 强制从网络上下载,即不使用本地副本
*/
bool GetStationInfo(vector<stationinfo>& si, bool bForceDownload = false);
/**
* 获取所有高校信息 https://kyfw.12306.cn/otn/userCommon/schoolNames POST provinceCode=11&_json_att=
*/
/**
* 获取所有城市信息 https://kyfw.12306.cn/otn/userCommon/allCitys POST station_name=&_json_att=
*/
/**
* 查询常用联系人
*/
bool QueryPassengers(int pageindex = 2, int pagesize = 10);
bool GetVCodeFileName(char* pszDst, int nLength);
private:
bool GetCookies(const string& data);
/**
* 发送一个http请求
*@param url 请求的url
*@param strResponse http响应结果
*@param get true为GET,false为POST
*@param headers 附带发送的http头信息
*@param postdata post附带的数据
*@param bReserveHeaders http响应结果是否保留头部信息
*@param timeout http请求超时时间
*/
bool HttpRequest(const char* url, string& strResponse, bool get = true, const char* headers = NULL, const char* postdata = NULL, bool bReserveHeaders = false, int timeout = 10);
private:
char m_szCurrVCodeName[256]; //当前验证码图片的名称
string m_strCookies;
string m_strGlobalRepeatSubmitToken;
string m_strKeyCheckIsChange;
};
#endif //!__CLIENT_12306_H__
具体的实现代码就不在文章中贴出来了,您可以下载我的代码。下载地址在微信公众号『CppGuide』中回复『12306源码』即可得到下载地址,当然,由于12306的接口经常发生改变,当你拿到代码时,可能12306服务器的接口已经稍微发生了改变,您可以按上面介绍的原理做响应的修改。
最后当您实现了基本的登录和购票功能后,你就可以不断模拟某些请求去进行刷票了。
文章版权所有,转载请保留版权申明。
欢迎关注公众号『CppGuide』。如果有任何技术或者职业方面的问题需要我提供帮助,可通过这个公众号与我取得联系,此公众号不仅分享高性能服务器开发经验和故事,同时也免费为广大技术朋友提供技术答疑和职业解惑,您有任何问题都可以在微信公众号直接留言,我会尽快回复您。