上一篇文章講完了12306網站模擬登錄的部分,看這裏 12306改版以後簡單搶票軟件的實現html
如今把後面的步驟所有分析一下。正則表達式
登陸完成要選擇買票人的信息,那麼怎麼得到帳戶中經常使用聯繫人的信息呢?訪問這個地址:https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOsjson
訪問這個頁面須要兩個參數(@code_flyer提醒這裏可能不須要參數,暫時沒驗證),
cookie
說一下第二個參數,REPEAT_SUBMIT_TOKEN是從提交訂單後選擇乘客信息的頁面中得到的,這個頁面在後面還會用到,先給出地址:https://kyfw.12306.cn/otn/confirmPassenger/initDc,頁面頭部有一段js,找到globalRepeatSubmitToken對應的值就是須要的參數dom
得到用戶信息後能夠在界面上展現出來,供用戶選擇,或者直接提供用戶輸入信息的地方,只須要姓名、身份證號、手機號就能夠了,這裏要求輸入的買票人信息是帳號中已經認證過的人,12306作了實名認證了。網站
準備步驟都完成了,下面應該就是查詢餘票了,查票的地址和參數以下:https://kyfw.12306.cn/otn/leftTicket/queryT?leftTicketDTO.train_date=2015-02-16&leftTicketDTO.from_station=SZH&leftTicketDTO.to_station=XCH&purpose_codes=ADULT編碼
參數分別表明出發日期,出發站,到達站,車票類型,複雜一點的就是出發站、到達站的代號問題了,這也難不倒咱們勤勞的猿類,查詢頁面 https://kyfw.12306.cn/otn/leftTicket/init 加載的時候引入了一個站點名稱拼音和代號對應的js文件,spa
訪問這個地址:https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8241,密密麻麻的站點和代號啊,看得我眼花繚亂的,趕忙找到你在的城市吧!我先拿前三個數據來講明一下數據的格式,插件
1 @bjb|北京北|VAP|beijingbei|bjb|0@bjd|北京東|BOP|beijingdong|bjd|1@bji|北京|BJP|beijing|bj|2
每條記錄以"@"開頭,剩下的用"|"來分隔,全大寫的代號就是咱們在查詢餘票時用到的出發站、到達站編號,一開始就把這些信息保存起來,查詢時直接使用是否是能夠快那麼一點點呢?code
差一點忘記了,查詢以前須要先設置cookie值,記錄查詢信息,須要設置的cookie字段以下圖所示:
其中JSESSIONID和BIGipServerotn在上篇文章登陸時已經得到了,剩下的幾個字段就是你查詢車票用到的信息,_jc_save_fromStationh和_jc_save_toStation將要查詢站點的中文名和編號按照特定方式編碼組成,中文名轉爲Unicode值並轉爲全大寫,而後將"\"替換爲"%",後面拼接站點對應編號便可。
下面來看查詢餘票返回的結果,數據爲json格式,爲了方便展現,特地找了一個只有一輛列車的區間:昆明--徐州,其餘區間返回的數據格式也是相似的。
{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":[{"queryLeftNewDTO":{"train_no":"800000K4920F","station_train_code":"K492","start_station_telecode":"KMM","start_station_name":"昆明","end_station_telecode":"JNK","end_station_name":"濟南","from_station_telecode":"KMM","from_station_name":"昆明","to_station_telecode":"XCH","to_station_name":"徐州","start_time":"12:40","arrive_time":"02:59","day_difference":"2","train_class_name":"","lishi":"38:19","canWebBuy":"IS_TIME_NOT_BUY","lishiValue":"2299","yp_info":"1027853214407805000910278503273048850106","control_train_day":"20201231","start_train_date":"20150202","seat_feature":"W3431333","yp_ex":"10401030","train_seat_feature":"3","seat_types":"1413","location_code":"M1","from_station_no":"01","to_station_no":"27","control_day":59,"sale_time":"1430","is_support_card":"0","gg_num":"--","gr_num":"--","qt_num":"--","rw_num":"9","rz_num":"--","tz_num":"--","wz_num":"有","yb_num":"--","yw_num":"有","yz_num":"有","ze_num":"--","zy_num":"--","swz_num":"--"},"secretStr":"MjAxNS0wMi0wMiMwMCNHNzI5OCMwMjozMyMyMDoyMSM1NTAwMEc3Mjk4MDAjT0hII1VVSCMyMjo1NCPoi4%2Flt57ljJcj5b6Q5bee5LicIzAyIzA4I08wMjQ5MDAwNTlNMDQxOTAwMDUwTzAyNDkwMzAwMCNIMSMxNDE4OTk2MjQ0NjgxIzA5MEI3MkQ5NEFDQTgyQzI3MjM2MTc5RTE5ODA3RDQ2NkUxRDAxNjY2MDM1MzVFQzE0QTZFMUQ4","buttonTextInfo":"預訂"}],"messages":[],"validateMessages":{}}
慢慢解析這個數據,咱們可以看到車次 station_train_code:K492,座位信息:"gg_num":"--","gr_num":"--","qt_num":"--","rw_num":"9","rz_num":"--","tz_num":"--","wz_num":"有","yb_num":"--","yw_num":"有","yz_num":"有","ze_num":"--","zy_num":"--","swz_num":"--",這些信息包括了座位類型和剩餘數量,遇到「有」或者數字的座位表明咱們能夠預訂的,其餘的應該是沒有這種類型的座位或無票。篩選車票的邏輯能夠根據本身的須要進行定製了,能夠預先定義好須要哪一個車次什麼類型的座位,發現能預約的車票趕忙下手,到下一步去提交訂單吧,若是沒有合適的車票你能夠在查詢篩選一下,這就在不停的刷票了。這裏還有一個secretStr是比較重要的,待會提交訂單的時候要用到,因此先把這個值保存起來。
下面的這段內容是在12306改版以後新添加進去的,原理和登陸時同樣,增長了動態的參數名和值來判斷在提交訂單時是否是搶票插件的請求,仍是看餘票查詢頁面https://kyfw.12306.cn/otn/leftTicket/init,新加載腳本的地方在這
一樣訪問裏面的內容,得到key的值並計算value值,計算方法參考上一票文章,12306改版以後簡單搶票軟件的實現。後面提交訂單的時候能用到這個key跟value值。
篩選完能預訂的列車,12306網站還作了一個檢驗https://kyfw.12306.cn/otn/login/checkUser,驗證你有沒有登陸的,這個時候你要是沒有登陸那就悲劇了,想一想先你刷了半天的票,忽然發現一張能購買的了,這時候告訴你「去登錄」,跟這張票說byebye吧。前面咱們是先登陸而後查詢餘票的,因此檢查是否登陸這一步是能夠跳過去的,直接進入下一步,提交訂單。
提交訂單的地址:https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest,這裏的參數用到了前面保存的車票secretStr和計算的key、value值,參數見下圖:
這裏須要根據返回的結果判斷是否成功,成功以後12306網站會跳轉到選擇乘客、輸入驗證碼的頁面https://kyfw.12306.cn/otn/confirmPassenger/initDc,這個頁面在前面得到經常使用聯繫人的地方用到過。在這裏咱們從頁面代碼中從新獲取一下globalRepeatSubmitToken的值,方法在文中開頭處那一段,這個值在檢查確認訂單的時候用到,一樣在這個頁面得到key_check_isChange的值,用正則來匹配,對應的正則表達式爲 'key_check_isChange':'(.*?)',本身驗證一下,保存這個值後面使用。在程序中咱們須要將前面準備好的用戶信息模擬提交一下,並查看返回的結果是否成功。具體來分析一下,首先要處理的仍是新增長的驗證,同登陸步驟和上面的提交訂單步驟,要先得到key和value值,這裏就不在贅述。而後請求一個新的驗證碼,和登錄以前的驗證碼地址是相同的,可是參數有所區別,
,注意這裏的參數,將驗證碼ocr自動識別或手動輸入以後最好先提交給12306後臺驗證一下,這一步能夠跳過去直接到下一步檢查確認訂單,可是若是驗證碼錯誤的話也不能矇混過關,檢查訂單時仍是會校驗的。
下面這一步是檢查確認訂單,請求地址是https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo,傳入的參數比較多,下圖所示
分開來說這幾個參數,cancel_flag,bed_level_order_num,tour_flag,_json_att 用圖中固定值便可,randCode是上一步請求的驗證碼,NTkzODQy這一對值是驗證是不是搶票助手用的key,value值,前面已得到,REPEAT_SUBMIT_TOKEN的值也在頁面https://kyfw.12306.cn/otn/confirmPassenger/initDc中從新得到了,就剩下passengerTicketStr和oldPassengerStr這是根據乘客的用戶信息拼接出來的,參考一段別人的代碼(hncdyj)
1 public static String getOldPassengerStr(List<UserInfo> userInfo) { 2 String oldStrs = ""; 3 for (int i = 0; i < userInfo.size(); i++) { 4 String oldStr = userInfo.get(i).getName() + "," + userInfo.get(i).getCardType() + "," + userInfo.get(i).getCardID() + "," + userInfo.get(i).getType(); 5 oldStrs += oldStr + "_"; 6 } 7 return oldStrs; 8 } 9 public static String getPassengerTicketStr(List<UserInfo> userInfo) { 10 String oldStrs = ""; 11 for (int i = 0; i < userInfo.size(); i++) { 12 String oldStr = ""; 13 if ("WZ" == userInfo.get(i).getSeatType()) { 14 } else { 15 oldStr = userInfo.get(i).getSeatType(); 16 } 17 String bR = oldStr + ",0," + userInfo.get(i).getTickType() + "," + userInfo.get(i).getName() + "," + userInfo.get(i).getCardType() + "," + userInfo.get(i).getCardID() + ","
18 + (userInfo.get(i).getPhone() == null ? "" : userInfo.get(i).getPhone()) + ",N"; 19 oldStrs += bR + "_"; 20 } 21 return oldStrs.substring(0, oldStrs.length() - 1); 22 }
結果大概是這個樣子的
passengerTicketStr-->1(座位類型),0,1(車票類型),張三(乘客姓名),1(證件類型),320xxxxxx(身份證號),151xxxx(手機號),N
oldPassengerStr-->張三(乘客姓名),1(證件類型),320xxxxxx(身份證號),1_
能夠本身買票時抓包驗證,這裏給出座位類型和代號的對應關係:商務座(9),特等座(P),一等座(M),二等座(O),高級軟臥(6),軟臥(4),硬臥(3),軟座(2),硬座(1),無座(1)(修正爲1,感謝@code_flyer的提醒),能夠根據本身的須要預先設置要買的座位類型,根據查詢結果判斷對應的類型是否有票。
這一步提交成功根據返回的信息判斷確認定可是否經過,常見的錯有「非法請求」,「輸入的驗證碼錯誤」等。
經過以後離成功就很近啦,來看一下排隊人數的狀況吧,地址:https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount,參數
要查詢的車次和leftTicket信息在查詢餘票時獲得的信息裏面有,不超過排隊人數的狀況下就進行下面一步吧
確認單筆訂單 https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue
這些參數都比較簡單了,前面都用到過了或者已經保存下來準備這裏使用的,train_location在查詢餘票返回的結果裏。
後面還能夠查詢出票等待時間,https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime?random=1419117908672&tourFlag=dc&_json_att=&REPEAT_SUBMIT_TOKEN=xxxxxxxxxxxxxx,參數請作相應替換,random對應的應該是當前時間。
最後還須要補充一點知識,12306返回的信息有些是gzip壓縮格式的,能夠根據返回信息頭中的Content-Encoding來判斷,讀取這些返回的信息流時須要用到GZIPInputStream,不然獲得的是一堆亂碼。
OK,到這裏整個流程走完了,應該能買到你心儀的火車票了,也算是了卻了一樁心事。
買票的所有流程簡單介紹了一下,可能不夠清晰,可是有心人根據這些分析應該能作出一個簡單的搶票軟件了,若是你用本身作的搶票軟件買到了票,請到這裏來分享一下。在作的過程當中有什麼疑問也歡迎到這裏來提問,我會盡可能及時幫助解答,不必定全部的問題都能解決。
空餘的時間還作了一個搶小米的軟件,如今搶火車票的文章告一段落了,閒下來的時候把搶小米的過程也分析一下。
感謝你看到了這裏!歡迎留言討論,也但願你幫我點一下「推薦」