从零实现一款12306抢票软件(三)

本文接上一篇《从零实现一款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』。如果有任何技术或者职业方面的问题需要我提供帮助,可通过这个公众号与我取得联系,此公众号不仅分享高性能服务器开发经验和故事,同时也免费为广大技术朋友提供技术答疑和职业解惑,您有任何问题都可以在微信公众号直接留言,我会尽快回复您。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值