時隔一年多,終於朋友的忽悠下吧搶票Demo的最后一步完善了,與2014年1月9日成功生成車票。
Demo僅經過自己測試,並未在高峰期進行測試,代碼質量很差,因為趕工,套用去年模板並未使用設計模式。
代碼存在如下BUG:
1)代碼使用 。net 4.5的事件,如果使用4.0或以下的同學,請根據錯誤提示,更改事件即可。已上傳兩個版(.net4.0 .net4.5)本。
2)添加、刷新常用聯系人功能缺失,按鈕已屏蔽。請在官網添加后,重新登錄軟件即可刷新。
3)驗證碼為手動輸入,不支持自動識別。未做原因如下:1.本人太懶了,2.驗證碼經常變化,防止哪天變成公式計算,中文識別等特殊情況。
時間倉促,代碼整體結構是去年的,編寫的代碼很垃圾,未使用任何設計模式,僅從目的出發,並未考慮任何效率、兼容性、安全性、可維護性等問題。高手請繞道,勿噴,謝謝。
如過有任何問題,可以在評論中一起探討。如對代碼有問題,可以一起討論。
本文最后放出的Demo僅供學習,請勿用於搶票操作。
什么都不說,先上圖:
軟件工作流程:
1)拉取登陸驗證碼
2)登陸,獲取Cookie
3)拉取常用聯系人
4)搜索車次前拉取城市地址,供給2221個城市
5)按時間獲取車次信息,並供給用戶選擇
6)用戶選擇指定車次,指定日期獲取車次及車票信息
7)根據用戶選擇座位號,比多剛拉取的車票信息。如果沒有車票則間隔6秒時間后重新刷票(int m = 6 * 100;)。
8)如果有票,判斷用戶是否有勾選常用聯系人,如果未勾選則不進行搶票
9)搶票第一步:請求https://kyfw.12306.cn/otn/confirmPassenger/autoSubmitOrderReques頁面獲取Token
提交車次信息:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add("secretStr", train.Id);
dic.Add("train_date", date);
dic.Add("tour_flag", "dc");
dic.Add("purpose_codes", "ADULT");
dic.Add("query_from_station_name", From.Name);
dic.Add("query_to_station_name", To.Name);
dic.Add("", "");
dic.Add("cancel_flag", "2");
dic.Add("bed_level_order_num", "000000000000000000000000000000");
dic.Add("passengerTicketStr", passengerTicketStr.ToString().TrimEnd('_'));
dic.Add("oldPassengerStr", oldPassengerStr.ToString());
Referer: "https://kyfw.12306.cn/otn/leftTicket/init"
headers: new Dictionary<string, string>() { { "Origin", "https://kyfw.12306.cn" },{"X-Requested-With", "XMLHttpRequest"} }
PostData、Cookie、Referer、Headers這幾個是重點,必須要有,否則失效。
返回Json:data.data.result中保存Token,如:
Q6#BA6C4F23E49E84F96A07B8ECA37A9FF350DAD2E2F484AD96F61C2046#O007450669M0099501499019950025#1
為統一名稱,規定data.data.result使用#進行切割后命名:Q#長Token#短Token#數字
10)搶票第二步:請求頁面https://kyfw.12306.cn/otn/confirmPassenger/getQueueCountAsync獲取車票數量
提交車次數據:
dic.Clear();
dic.Add("train_date",
(Convert.ToDateTime(date).ToString("ddd MMM dd yyy ", DateTimeFormatInfo.InvariantInfo) +
DateTime.Now.ToString("HH:mm:ss").Replace(":", "%3A") + " GMT%2B0800 (China Standard Time)").Replace(' ', '+'));
dic.Add("train_no", train.TrainNo);
dic.Add("stationTrainCode", train.StationTrainCode);
dic.Add("seatType", seatType);
dic.Add("fromStationTelecode", train.from_station_telecode);
dic.Add("toStationTelecode", train.end_station_telecode);
dic.Add("leftTicket", token.ShortToken);
dic.Add("purpose_codes", "ADULT");
dic.Add("_json_att", "");
//注:train_date可以使用URl編碼即可,轉碼前內容:Fri Oct 10 2014 09:59:42 GMT+0800 (China Standard Time)
Referer: "https://kyfw.12306.cn/otn/leftTicket/init"
headers:
new Dictionary<string, string>()
{
{"Origin", "https://kyfw.12306.cn"},
{"X-Requested-With", "XMLHttpRequest"}
});
返回值:data.data.ticket與短Token一致
11)搶票第三步:https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew.do?module=login&rand=sjrand&拉取搶票驗證碼:
Get頁面,仍然要帶入Cookies、Referer,但無需帶入headers
12)搶票第四步:請求https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueueAsys,生成車票
請求數據:
dic.Clear();
dic.Add("passengerTicketStr", System.Web.HttpUtility.UrlEncode((passengerTicketStr.ToString().TrimEnd('_'))).ToUpper());
dic.Add("oldPassengerStr", System.Web.HttpUtility.UrlEncode(oldPassengerStr.ToString()).ToUpper());
dic.Add("randCode", Code);
dic.Add("purpose_codes", "ADULT");
dic.Add("key_check_isChange", token.LongToken);
dic.Add("leftTicketStr", token.ShortToken);
dic.Add("train_location", token.Q);
dic.Add("_json_att", "");
Referer: "https://kyfw.12306.cn/otn/leftTicket/init"
headers:
new Dictionary<string, string>()
{
{"Origin", "https://kyfw.12306.cn"},
{"X-Requested-With", "XMLHttpRequest"}
});
注:passengerTicketStr、oldPassengerStr均要UrlEncode
當data.data.submitStatus返回True時,恭喜你,已經搶票成功了,等待出票。
總結思路:
搶票與官方提供的頁面自動提交搶票一次,但由於省去驗證提交的驗證碼是否正確環節,固加快搶票速度。
擴展思路:
可以使用將搶票端分離,部署至多台計算機上,並且開啟多線程。
驗證碼統一傳輸至服務器端。
再有驗證碼客戶端去服務端拉取驗證碼后,由人工輸入結果並返回。
可以加快搶票速度。
Demo .Net4.5 下載
Demo .New4.0 下載
Demo僅供學習,請勿用於搶票操作。
很遺憾,由於部分原因,停止提供Demo的下載,十分抱歉。