這裡還有個注意細節,就是通過POST請求發送的數據需要對一些符號做URL Encode,這個我在上一篇文章《從零實現一個http服務器》也詳細做了介紹,還不清楚的可以參見上一篇文章。所以對於向圖片驗證碼坐標信息中含有的逗號信息就要進行URL編碼,從
- answer=114,54,44,46&login_site=E&rand=sjrand
複製代碼
變成
- answer=114%2C54%2C44%2C46&login_site=E&rand=sjrand
複製代碼
所以,在http包頭中指定的Content-Length字段的值應該是編碼後的字符串長度,而不是原始的長度,這個地方特別容易出錯。
如果驗證成功後,接下來就是查票和購票了。這裡就不一一介紹了,所有的原理都是一樣的,作者可以自行探索。當然,我已經將所有的接口都探索完了,並實現了,我這裡貼一下吧:
- /**
- *@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; // "14262319781108815X"
- string passenger_type; // "1"
- string passenger_flag; // "0"
- string passenger_type_name; // "成人"
- string mobile_no; // "13917043320"
- string phone_no;
- string email; // "[email protected]"
- 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,14262319781108815X,1_
- @param passengerTicketStr passengerTicketStr組成的格式:seatType,0,票類型(成人票填1),乘客名,passenger_id_type_code,passenger_id_no,mobile_no,』N』
- 示例: O,0,1,范蠡,1,14262319781108815X,13917043320,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,14262319781108815X,1_ 73
- passengerTicketStr O,0,1,范蠡,1,14262319781108815X,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__
複製代碼
具體的實現代碼就不在文章中貼出來了,您可以下載我的代碼。下載地址在微信公眾號『easyserverdev』中回復『12306源碼』即可得到下載地址,當然,由於12306的接口經常發生改變,當你拿到代碼時,可能12306服務器的接口已經稍微發生了改變,您可以按上面介紹的原理做響應的修改。
最後當您實現了基本的登錄和購票功能後,你就可以不斷模擬某些請求去進行刷票了。 |