12306購票之自動化提交初體驗(轉貼)

10年想本身建個網站練練手,因而上萬網申請域名,爲了找個稍微心儀的域名是傷透了腦 筋。當時寫了個很簡單的自動提交表單的查詢,是用webbrowser作的,分析表單數據累了個半死,倒也作出來個簡單能用的,遞歸一直查詢 (a,b...z,az,ab...az...)單線程,而且萬網有限制,查詢間隔太快會被屏蔽,掃了好久也沒掃到多少數據,而後就不了了之。 javascript

12年南下深圳,在園子裏看到各類對12306的思考及吐槽,打算作個簡單的12306買票的小程序,也作過一些嘗試,但因爲本身太菜,遇到各類問題後停了下來。一晃晃過了世界末日,2013來了,買票的問題推到了眼前,硬着頭皮開始編碼。 php

先來看看下面這個對http請求的封裝方法,做者小坦克,我這裏拿來主義了。 html

代碼

須要fiddler或者相似的工具來分析http請求,簡單介紹fiddler,如圖: java

選擇左邊URL後,選擇右邊上下都爲Raw的標籤窗口看到的就是這張圖了,右上角窗口爲http請求(Request),右下角爲http相應(Response)。 程序員

繼續看上圖,你在登陸頁面中點擊登陸實際是發送上圖的第三條請求,該請求爲post,它須要form數據,格式爲Request區域最後一行數據:  web

 

一:登陸 面試

url(Post): https://dynamic.12306.cn/otsweb/loginAction.do?method=login // 登陸請求 data: loginRand=隨機數&loginUser.user_name=用戶名&nameErrorFocus=&user.password=密碼&passwordErrorFocus=&randCode=驗證碼&randErrorFocus=
url(Get):https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=sjrand // 驗證碼
url(Post):https://dynamic.12306.cn/otsweb/loginAction.do?method=loginAysnSuggest // 隨機數 {"loginRand":"494","randError":"Y"} // 返回值

到這裏,登陸就完成了,貌似很簡單啊! 數據庫

 

二:查詢 apache

複製代碼
url(Get): https://dynamic.12306.cn/otsweb/order/querySingleAction.do?method=queryLeftTicket&orderRequest.train_date=2013-01-27&orderRequest.from_station_telecode=GZQ&orderRequest.to_station_telecode=WHN&orderRequest.train_no=&trainPassType=QB&trainClass=QB%23D%23Z%23T%23K%23QT%23&includeStudent=00&seatTypeAndNum=&orderRequest.start_time_str=00%3A00--24%3A00  url解碼: https://dynamic.12306.cn/otsweb/order/querySingleAction.do?method=queryLeftTicket&orderRequest.train_date=2013-01-27&orderRequest.from_station_telecode=GZQ&orderRequest.to_station_telecode=WHN&orderRequest.train_no=&trainPassType=QB&trainClass=QB#D#Z#T#K#QT#&includeStudent=00&seatTypeAndNum=&orderRequest.start_time_str=00:00--24:00 orderRequest.train_date:日期 orderRequest.from_station_telecode/orderRequest.to_station_telecode: 車站代碼(url(Get): https://dynamic.12306.cn/otsweb/js/common/station_name.js)  orderRequest.train_no:車次 trainPassType/trainClass: 車次類型 includeStudent: 學生票標識 seatTypeAndNum:貌似有牛人得出這裏跟下鋪有關係?對我來講未知 orderRequest.start_time_str:起止時間
複製代碼

能夠在登陸狀態下直接請求,比在查詢頁面快而且沒有限制,返回的json(去掉html標籤)爲: json

0,T264,廣州12:19,蘭州16:37,28:18,--,--,--,--,--,無,無,--,無,有,--,預訂 \n1,K226,廣州20:36,蘭州07:12,34:36,--,--,--,--,--,無,無,--,1,有,--,預訂 \n2,T38,廣州23:53,蘭州06:28,30:35,--,--,--,--,--,無,無,--,無,有,--,預訂

依次分別爲:

商務座,特等座,一等座,二等座,高級軟臥,軟臥,硬臥,軟座,硬座,無座,其餘。 --:沒有該席別;*:未到開始時間;有:有而且數量充足;數字:有但數量有限:無:已售完

查詢也是這樣簡單,其實這裏還返回來很重要的信息,這裏咱們賣個關子,繼續:

下一步幹什麼呢?預約按鈕,這一步比較麻煩,Post提交的信息比較多,很繁瑣,須要細心的去反覆調試

三:預約

複製代碼
url(Post): https://dynamic.12306.cn/otsweb/order/querySingleAction.do?method=submutOrderRequest // 預約  data: station_train_code=T38&train_date=2013-01-27&seattype_num=&from_station_telecode=GZQ&to_station_telecode=LZJ&include_student=00&from_station_telecode_name=%E5%B9%BF%E5%B7%9E&to_station_telecode_name=%E5%85%B0%E5%B7%9E&round_train_date=2013-01-27&round_start_time_str=00%3A00--24%3A00&single_round_type=1&train_pass_type=QB&train_class_arr=QB%23D%23Z%23T%23K%23QT%23&start_time_str=00%3A00--24%3A00&lishi=30%3A35&train_start_time=23%3A53&trainno4=6300000T3803&arrive_time=06%3A28&from_station_name=%E5%B9%BF%E5%B7%9E&to_station_name=%E5%85%B0%E5%B7%9E&from_station_no=01&to_station_no=22&ypInfoDetail=1*****30884*****00001*****00003*****0000&mmStr=3C8A201EB0DAF5F17803BF07AAFC2016A2D44E0C4302D3469551C86A&locationCode=Q6 url解碼: station_train_code=T38&train_date=2013-01-27&seattype_num=&from_station_telecode=GZQ&to_station_telecode=LZJ&include_student=00&from_station_telecode_name=廣州&to_station_telecode_name=蘭州&round_train_date=2013-01-27&round_start_time_str=00:00--24:00&single_round_type=1&train_pass_type=QB&train_class_arr=QB#D#Z#T#K#QT#&start_time_str=00:00--24:00&lishi=30:35&train_start_time=23:53&trainno4=6300000T3803&arrive_time=06:28&from_station_name=廣州&to_station_name=蘭州&from_station_no=01&to_station_no=22&ypInfoDetail=1*****30884*****00001*****00003*****0000&mmStr=3C8A201EB0DAF5F17803BF07AAFC2016A2D44E0C4302D3469551C86A&locationCode=Q6
複製代碼

前面的參數再也不贅述(有疑問可回頭看看查詢的參數及說明),看看這段:

&ypInfoDetail=1*****30884*****00001*****00003*****0000&mmStr=3C8A201EB0DAF5F17803BF07AAFC2016A2D44E0C4302D3469551C86A&locationCode=Q6

坦率的講,我也不知道它是幹嗎的,我只知道它是從哪裏來的,這裏就是上文賣的關子,其實在點擊預約時附加了該信息(查詢時得到)

onclick=javascript:getSelected('T38#26:47#23:53#6300000T3803#GZQ#TSJ#02:40#廣州#天水#01#20#1*****30884*****00001*****00003*****0000#3C8A201EB0DAF5F17803BF07AAFC2016A2D44E0C4302D3469551C86A#Q6')

預約這裏痛苦了好久,這裏多說幾句,如上圖,該請求爲post類型請求,返回302,即重定向,來看302以後的請求

url(Get): https://dynamic.12306.cn/otsweb/order/confirmPassengerAction.do?method=init // 申請令牌 // 返回值 <input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="2508bfa47ec2b4d909fb30190cabf71a">
<input type="hidden" name="leftTicketStr" id="left_ticket" value="1000003166400000000010000000023000000000" />

就是說302到上面URL以後 上面請求會返回一個TOKEN(令牌,防止重複提交),這兩個值在後續提交訂單和確認購票時會用到。可是重定向以後的請求咱們是拿不到的,咱們能夠再向它請求一次令牌(302的令牌拿不到,咱們再主動找它要一個令牌),記錄便可。

這裏,預約的模擬就完成了,接下來提交訂單。

 

四:提交訂單

url(Get): https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=randp  // 提交訂單驗證碼 注意該部分參數與登陸不一樣 
複製代碼
url(Post): https://dynamic.12306.cn/otsweb/order/confirmPassengerAction.do?method=checkOrderInfo&rand=bdte // 提交訂單請求  data: // 該部分數據因爲涉及身份信息,見下文解碼信息  url解碼: org.apache.struts.taglib.html.TOKEN=ad45f047d7c4222a11c437ebd1f977f7&leftTicketStr=1026353107408145000010263500003046250000&textfield=中文或拼音首字母&checkbox1=1&orderRequest.train_date=2013-01-28&orderRequest.train_no=630000K22609&orderRequest.station_train_code=K226&orderRequest.from_station_telecode=GZQ&orderRequest.to_station_telecode=TSJ&orderRequest.seat_type_code=&orderRequest.ticket_type_order_num=&orderRequest.bed_level_order_num=000000000000000000000000000000&orderRequest.start_time=20:36&orderRequest.end_time=02:22&orderRequest.from_station_name=廣州&orderRequest.to_station_name=天水&orderRequest.cancel_flag=1&orderRequest.id_mode=Y&passengerTickets=1,0,1,姓名,1,身份證號碼,電話號碼,Y&oldPassengers=姓名,1,身份證號碼&passenger_1_seat=1&passenger_1_ticket=1&passenger_1_name=姓名&passenger_1_cardtype=1&passenger_1_cardno=身份證號碼&passenger_1_mobileno=電話號碼&checkbox9=Y&oldPassengers=&checkbox9=Y&oldPassengers=&checkbox9=Y&oldPassengers=&checkbox9=Y&randCode=h94b&orderRequest.reserve_flag=A&tFlag=dc
複製代碼
url(Get): https://dynamic.12306.cn/otsweb/order/confirmPassengerAction.do?method=getQueueCount&train_date=2013-01-27&train_no=630000K22609&station=K226&seat=1&from=GZQ&to=LZJ&ticket=1029053183409105000010290500553050750000  // 查詢餘票 {"countT":0,"count":229,"ticket":"1*****31644*****00001*****00013*****0000","op_1":true,"op_2":false} // 返回值

提交訂單的請求完成。

咱們回來來看 1*****31644*****00001*****00013*****0000 這 段,從查詢請求開始,反覆出現該部分,經過在提交訂單環節餘票信息分析,該數據就是返回的餘票信息,即餘票信息在第一次查詢時就已經返回,但在第一次查詢 和提交訂單後的查詢的數字稍微有所出入,估計爲查詢時得到數據的緩存時間有關係,固然,提交訂單後查詢得到的應該更爲接近數據庫,具體數據以下:

1*****31644*****00001*****00013*****0000 // 無座:164 軟臥:0 硬座:1 硬臥:0 1*****3無座4*****0軟臥1*****0硬座3*****0硬臥

上面不部分爲較爲普通車型返回的餘票數據,什麼是普通車型:K,T,Z系列(不包括高鐵,普通慢車,臨客),而且該車型包含軟臥,硬臥,硬座,無座四中票種。也可能出現臥鋪車(Z系列),或者無臥鋪車因此返回的數據應該是1*****31644*****00001醬紫的,高鐵未測試,道理亦然。

 

不管在最開始的查詢,仍是提交訂單後查詢,都是操做緩存,因此在提交訂單後查詢數據爲0時,也能夠無視餘票直接強行確認訂單,有機會定到票哦。沒有通過大量測試,一般會返回當前排隊人數大於與票數或者餘票不足(這裏須要取捨的,推薦仍是查詢餘票>0時提交訂單)。

工做基本完成了,臨門一腳。

 

五:確認訂單

複製代碼
url(Post): https://dynamic.12306.cn/otsweb/order/confirmPassengerAction.do?method=confirmSingleForQueue data: // 該部分數據因爲涉及身份信息,見下文解碼信息  url解碼: org.apache.struts.taglib.html.TOKEN=ad45f047d7c4222a11c437ebd1f977f7&leftTicketStr=1026353107408145000010263500003046250000&textfield=中文或拼音首字母&checkbox1=1&orderRequest.train_date=2013-01-28&orderRequest.train_no=630000K22609&orderRequest.station_train_code=K226&orderRequest.from_station_telecode=GZQ&orderRequest.to_station_telecode=TSJ&orderRequest.seat_type_code=&orderRequest.ticket_type_order_num=&orderRequest.bed_level_order_num=000000000000000000000000000000&orderRequest.start_time=20:36&orderRequest.end_time=02:22&orderRequest.from_station_name=廣州&orderRequest.to_station_name=天水&orderRequest.cancel_flag=1&orderRequest.id_mode=Y&passengerTickets=1,0,1,姓名,1,身份證號碼,電話號碼,Y&oldPassengers=姓名,1,身份證號碼&passenger_1_seat=1&passenger_1_ticket=1&passenger_1_name=姓名&passenger_1_cardtype=1&passenger_1_cardno=身份證號碼&passenger_1_mobileno=電話號碼&checkbox9=Y&oldPassengers=&checkbox9=Y&oldPassengers=&checkbox9=Y&oldPassengers=&checkbox9=Y&randCode=h94b&orderRequest.reserve_flag=A

{"errMsg":"Y"} // 返回值
複製代碼

又一大堆參數,但回頭對照提交訂單Data,直接將 &tFlag=dc 截掉便可。

鐺鐺鐺鐺...,多想來段美妙的音樂,回家的路通了,遺憾的是,高興的太早了。

春運(1月26日)以前若是返回Y,那麼直接就表示有票了,但在春運以後,坑爹的排隊又開始了,因此表示只是排上隊了,不表明必定有票,若是在開售的第一個整點,排上隊拿到票的概率很大,越日後拿到飄的概率越小。

若是返回的信息包含:非法的購票請求,意味着某一個請求的data部分參數錯誤。

以上徹底根據小坦克博文(感謝)推動,地址:

http://www.cnblogs.com/TankXiao/archive/2012/02/20/2350421.html

下面的地址分析是在完成後才找到的,遺憾沒有早看到,走了不少彎路:

http://www.cnblogs.com/waninlezu/archive/2012/01/07/tran_ticket.html http://sskaje.me/index.php/2012/01/12306bot/

 源碼:下載  (2013.01.22,持續更新)

 

PS: 該文編輯時通過多個週期,其中參數級數據沒有連續性,以實際Fiddler數據爲準。

若是藉助該文,能幫你買到票,固然最好,若是沒有,試着用本身掌握的知識,能去學習和解決一些實際生活中的問題,何嘗不是更大的收穫。

 

之前工做中有問題也偶爾上園子、msdn找找資料,沒什麼特別大的感觸,感受對.net(確切的說是asp.net)理解也僅僅是拖拖控件,而後數 據綁定完事了。園子裏逛久了,看了大量技術文章及分享,如湯姆大叔,老趙,劉未鵬等大神們的博文,才知道.net的博大精深及本身的淺薄,知道本身的無知 也算知吧(聊表安慰)?。做爲要奔三去了的老菜鳥一枚,慚愧的同時又滿懷但願。也感謝網絡那一端無私分享的你。

 

下面部分爲吐槽和求助:

做爲一名80後、沒學歷(弱弱的問,高中不算學歷吧?)、培訓出身(著名的***鳥,被各類鄙視和吐槽,囧)、菜鳥程序員。看着園子裏各類大神(園子廣泛稱大牛)及90後新人們的2012總結,深感愧疚。

網上投了一籮筐的簡歷,沒有收到一個面試邀請,不知道是簡歷的問題(菜+低調),學歷的問題,仍是人品?有招聘信息的碼農推薦一下。

深圳,.net程序員,3年經驗(asp.net)。

 

要不要簽名,這也是一個問題。
相關文章
相關標籤/搜索