解析爬蟲12306購票系統流程
做爬蟲也將近有一年的時間,本人不是什麼名牌大學畢業,但是對計算機的熱愛無人能擋。大學學了Java語言,大四來到帝都實習找了一份Java偏資料的實習工作,工作的過程中第一次接觸到了爬蟲的工作,並且感覺爬蟲挺有意思。從知道爬蟲到某些網購平臺,新聞網頁等文字資料的抓取,再到登入系統做查詢實時資料,再到解析驗證碼,以至於前兩天完成12306購票系統的爬蟲。完成購票系統爬蟲之後自我感覺有點膨脹,但是這點小小的膨脹被我強大的內心所束縛,以至於低調的寫了這篇部落格,哈哈……。
這個部落格寫的內容是針對於當前12306版本寫的,只要整理完全現在立馬就能買票。2017.5.25日
在做爬蟲的一年的時間裡做過很多系統的註冊、登入、查詢等。以至於在做購票爬蟲的時候不是特別困難。
事先宣告,這次研究爬蟲購票,純屬學習,絕對不會用於商業,如果有志同道合的盆友童鞋們歡迎一起研究哈。
據新聞說,2017春節購票時歷史以來最為艱難的一年,票都去哪了?據媒體報道商業黃牛使用假身份證證件10分鐘鈔殺1000多張票的新聞,原來如此,越來越多的黃牛加入其中,讓外出打工的人們,在外的遊子們感覺到買票回家難,最後不得不拿高價買黃牛手中的票回家。其實我想說使用技術的眼光去看待問題,也是可以達到四兩撥千斤的效果。
再次宣告,本篇部落格,純屬學習,絕對不會用於商業,如果有志同道合的盆友童鞋們歡迎一起研究。
下面開始分享一下我是以怎樣一個思路去做的,首先使用抓包工具先抓下購票過程的每一個連結,通過對每一步請求每一個連結都認真仔細分析後,查詢餘票需要實時監控請問量比較大最好別登入賬號,減少不必要的麻煩,一旦發現有餘票,程式呼叫登入程式,登入成功在呼叫購票程式。所以我的部落格決定按照查詢,登入,訂票的順序來寫。
- 1.查詢餘票
返回結果
{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"messages":[],"validateMessages":{}}
"hBu8r%2BRJBF8wdG5sTYoxcJrR7lRev0Kb5SQzQ9mw8PFJK%2BdTid5mcXTFbrMCgHamBIw%2BxUlWJlQ8%0AWwe6sp0W8299q%2BHS1pWT8CI5My77rJp5AYbDbBdYAwCaWDKH9zbPfXZSXFOYE3lxqbVej2SYKNKT%0And7e93T4NKjGU86uKPP %2B1e0kXZfah5N%2FbBxBSOHYbTOWAj%2FN7Z6nt3WxzAc%2FDHOxWOUPIjCEXagd%0AlBZwonkBTG0y4ELRMg%3D%3D|預訂|330000K5980T|K599|BTC|GZQ|BXP|ZZF|05:14|14:04|08:50|Y|Pm%2Bh1NV7CkWcA%2FWq%2BY69ecqgnHzi70S6ccm9xPpyQvEZ7igwpAyvCcmVrcM%3D|20170619|3|C1|09|22|0|0||||無|||1||11|無||||10401030|1413",
hBu8r%2BRJBF8wdG5sTYoxcJrR7lRev0Kb5SQzQ9mw8PFJK%2BdTid5mcXTFbrMCgHamBIw%2BxUlWJlQ8%0AWwe6sp0W8299q%2BHS1pWT8CI5My77rJp5AYbDbBdYAwCaWDKH9zbPfXZSXFOYE3lxqbVej2SYKNKT%0And7e93T4NKjGU86uKPP%2B1e0kXZfah5N%2FbBxBSOHYbTOWAj%2FN7Z6nt3WxzAc%2FDHOxWOUPIjCEXagd%0AlBZwonkBTG0y4ELRMg%3D%3D
這串字元是火車的資訊,在後面提交訂單的時候就是提交這段字元。這段字元是隨機生成的,過幾秒就回失效。
解釋一下:
330000K5980T 是列車編號
K599是車次
BTC是始發站包頭
GZQ是終點站廣州
BXP是北京
ZZF是鄭州
05:14是發車時間
14:04到達時間
08:50是路程時間
Y是當前車次是否可購票Y是可以N是不可以
20170619 火車始發站時間
3是硬臥(1是硬座,2是軟座,3是硬臥,4是軟臥,O是高鐵二等座,M是高鐵一等座)
後面都是各種座位的票數資訊
- 2.登入
請求登入頁面:https://kyfw.12306.cn/otn/login/init
請求成功後會返回cookie:
Set-Cookie: BIGipServerotn=804258314.50210.0000;JSESSIONID=7F0000012E6A716B2D5261F9C944BCA36BED947150;route=6f50b51faa11b987e576cdb301e545c4
接下來的請求要帶上這個cookie
下面要請求一個頁面
https://kyfw.12306.cn/otn/dynamicJs/lsbmmlg
這個連結的/otn/dynamicJs/lsbmmlg部分是從上面請求到返回資訊中拿到的,最後拼接成url進行請求。
<script src="/otn/dynamicJs/lsbmmlg" type="text/javascript" xml:space="preserve"></script>
{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"result":"1","msg":"TRUE"},"messages":[],"validateMessages":{}}
結果為"data":{"result":"1","msg":"TRUE"}表示正確
接下來請求登入https://kyfw.12306.cn/otn/login/loginAysnSuggest
請求引數:
request.requestbody=loginUserDTO.user_name=xxxxxxx
&userDTO.password=xxxxxxxx
&randCode=193%2C118%2C179%2C48
其中loginUserDTO.user_name=使用者名稱
userDTO.password=密碼
randCode=193%2C118%2C179%2C48登入驗證碼(同於上一個請求的randCode引數)
返回資訊:
{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"otherMsg":"","loginCheck":"Y"},"messages":[],"validateMessages":{}}
結果為"data":{"otherMsg":"","loginCheck":"Y"}表示登入成功。
登入成功之後會返回一個新的cookie
Set-Cookie: BIGipServerotn=250610186.24610.0000;JSESSIONID=7F000001921CE0E5DBE445F459464C88E7014568A2;route=9036359bb8a8a461c164a04f8f50b252
下面登入之後的每一個訪問都要帶上這個cookie去訪問,否則提示未登入。
那麼到這裡登入已經完成……
- 3.購票
購票前已經登入12306了,要請求購票的url,則必須要帶上登入成功的時候返回回來的cookie,方可購票否則會提示未登入。
首先請求js校驗頁面https://kyfw.12306.cn/otn/leftTicket/log?leftTicketDTO.train_date=2017-06-09&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=ZKN&purpose_codes=ADULT
請求之前必須要帶上登入成功後返回的cookie。
這是一個get請求,
leftTicketDTO.train_date = 2017-06-09 //乘車日期
leftTicketDTO.from_station = BJP //出發站
leftTicketDTO.to_station = ZKN //目的站
purpose_codes = ADULT//成人票
返回資訊
{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"messages":[],"validateMessages":{}}
{
"validateMessagesShowId": "_validatorMessage",
"status": true,
"httpstatus": 200,
"data": {
"result": [
"DS4TNFZcmTcOMsShMIkX%2BmO%2FeUp%2FZB0AQmlkGulWYh8YiB3gNQ%2FVjE64vBIZbLEs%2FK9iRwFP6GRC%0A%2BDpD%2BmSV2VTyP%2FmnfBREPfgykoUKw0Kjdumgk2lHAF%2Bei%2Bv2EJJ2uSjRGofvH35ybyDZNNKIF%2BQV%0Aut2%2FBMQaivD%2F1gi8AjanDz37eKyoZJ4RkfbIHanUrIe57KPywaVolpJJuY2Oc5fHeNTJWzXZPJ3I%0ANwRAaqTfWaETt7GTHQ%3D%3D|預訂|240000K4010O|K401|BXP|ZKN|BXP|ZKN|19:36|07:49|12:13|Y|u%2FgQ2DUlF6GUOnbbjwAb%2F%2Fo8eDAI9td%2B%2B2eVZ8ls6HiRH60t3pj9fn2vJMc%3D|20170609|3|PB|01|10|0|0||||6|||有||有|有||||10401030|1413"
],
"flag": "1",
"map": {
"BXP": "北京西",
"ZKN": "周口"
}
},
"messages": [],
"validateMessages": {}
}
接下來請求https://kyfw.12306.cn/otn/login/checkUser連結來檢測是否已經登入成功,請求的時候request.requestbody = _json_att=;請求時帶上登入成功的cookie,即可!
返回結果為下面這段程式碼即為正確。
{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"flag":true},"messages":[],"validateMessages":{}}
secretStr=wvV3vqns6Cjss%2FAt4Nwk0enBSDwxzzHjLBIjQcmj4M%2FRrH1jYWTEN%2B790BtrFRYNnGynsY09mj%2Fu%0AFVt6hG5SmzwBIQ1Tp04MS8fDykPFhIeGQno00ShmvTdmMUalboFvKKbeow5atFi7IrDkWUHEHUKt%0ARRBJu8b0H3l8BQicXLWP7ucSStn0fs7BcvdO5khgQDaoPNcV8G7ZjP4pHShbqe8NVao%2BG%2BOw%2B9oc%0Aofg3MVljtOPBrzYH6w%3D%3D&train_date=2017-06-09&back_train_date=2017-05-25&tour_flag=dc&purpose_codes=ADULT&query_from_station_name=北京&query_to_station_name=周口&undefined
secretStr //是前面提到的一串關於列車資訊的字元
train_date //發車時間
back_train_date //購票時間
tour_flag //購票型別 dc為單程
purpose_codes //乘客型別
query_from_station_name //出發站
query_to_station_name //目的站
undefined
請求返回正確結果
{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":"N","messages":[],"validateMessages":{}}
請求提交購票人資訊和選擇購票坐票或者臥鋪型別頁面
https://kyfw.12306.cn/otn/confirmPassenger/initDc
request.requestbody = _json_att=;
其中變數globalRepeatSubmitToken,key_check_isChange,leftTicket,tourFlag等資訊要從返回資訊中獲取。
var globalRepeatSubmitToken = 'e1839d98b0ce77e0530a8b3dfa8d88e5';
'key_check_isChange':'D498961827DE21575074FFA713A8877C47ED76B176BCFE01FB3FBB01',
'leftTicketStr':'zjfo3ei6G7xunpcnPhELKQhguaV5nR5lPBa4fJv960jY5Z%2B2ogKTZbw3y4k%3D',
'tour_flag':'dc',
cancel_flag=2&bed_level_order_num=000000000000000000000000000000&passengerTicketStr=(購票人資訊)&oldPassengerStr=(購票人資訊)&tour_flag=dc&randCode=&_json_att=&REPEAT_SUBMIT_TOKEN=e1839d98b0ce77e0530a8b3dfa8d88e5
cancel_flag=2//固定引數
bed_level_order_num = 000000000000000000000000000000 //固定
passengerTicketStr = //購票人資訊
tour_flag = dc //上面從ticketInfoForPassengerForm中獲取的引數
randCode //這裡的這個引數為空
_json_att
REPEAT_SUBMIT_TOKEN //從上面ticketInfoForPassengerForm中獲取的引數
請求成功返回
{"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":{}}
介紹request.requestbody引數
train_date = Fri Jun 09 2017 00:00:00 GMT+0800 (中國標準時間)
train_no = 240000K4010O //火車編號
stationTrainCode = K401 //火車列號
seatType = 3 //座位型別 1是硬座,2是軟座,3是硬臥,4是軟臥,O是高鐵二等座,M是高鐵一等座,
fromStationTelecode = BXP //出發站
toStationTelecode = ZKN //目的站
leftTicket //從上面ticketInfoForPassengerForm中獲取的引數
purpose_codes = 00
train_location = PB
_json_att
REPEAT_SUBMIT_TOKEN//從上面ticketInfoForPassengerForm中獲取的引數
返回結果
{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"count":"0","ticket":"222","op_2":"false","countT":"0","op_1":"false"},"messages":[],"validateMessages":{}}
結果顯示硬臥還有"ticket":"222"。
passengerTicketStr=ticketStr&oldPassengerStr=oldPassengerSt&randCode=&purpose_codes=00&key_check_isChange=D498961827DE21575074FFA713A8877C47ED76B176BCFE01FB3FBB01&leftTicketStr=zjfo3ei6G7xunpcnPhELKQhguaV5nR5lPBa4fJv960jY5Z%252B2ogKTZbw3y4k%253D&train_location=PB&choose_seats=&seatDetailType=000&roomType=00&dwAll=N&_json_att=&REPEAT_SUBMIT_TOKEN=e1839d98b0ce77e0530a8b3dfa8d88e5
request.requestbody中的欄位都有
key_check_isChange
leftTicketStr
train_location = PB
choose_seats
seatDetailType = 000
roomType = 00
dwAll = N
_json_att
REPEAT_SUBMIT_TOKEN = e1839d98b0ce77e0530a8b3dfa8d88e5
返回結果
{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"submitStatus":true},"messages":[],"validateMessages":{}}
返回"data":{"submitStatus":true}說明請求成功,出票成功,如果為其他就是扣票失敗
{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"queryOrderWaitTimeStatus":true,"count":0,"waitTime":-1,"requestId":6273433722868856171,"waitCount":0,"tourFlag":"dc","orderId":"EC89816430"},"messages":[],"validateMessages":{}}
EC89816430就是我們在視窗取票時使用的取票碼
到此為止把,下面的付款功能略微複雜,研究的不是特別深入在這裡就不貼出來了。搶票都成功了,付款有半個小時呢,足夠付款成功了。
購票程式基本已經結束。不難看出查詢餘票和登入相對簡單,到後面提交訂單之前各種校驗,各種引數,都要弄清楚,這些東西我也是經過了摸索之後才完成的,但是把整個流程走下來之後,心裡想想流程也就這樣。
經過幾天的努力取得了一點小小的收穫,寫了本篇部落格分享給大家,希望你們在寫購票爬蟲的時候給你們一定的幫助。
如果有哪些地方寫的不好,或者不連貫,歡迎各位少俠的留言。