1. 程式人生 > >解析爬蟲12306購票系統流程

解析爬蟲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就是我們在視窗取票時使用的取票碼

到此為止把,下面的付款功能略微複雜,研究的不是特別深入在這裡就不貼出來了。搶票都成功了,付款有半個小時呢,足夠付款成功了。

購票程式基本已經結束。不難看出查詢餘票和登入相對簡單,到後面提交訂單之前各種校驗,各種引數,都要弄清楚,這些東西我也是經過了摸索之後才完成的,但是把整個流程走下來之後,心裡想想流程也就這樣。

經過幾天的努力取得了一點小小的收穫,寫了本篇部落格分享給大家,希望你們在寫購票爬蟲的時候給你們一定的幫助。

如果有哪些地方寫的不好,或者不連貫,歡迎各位少俠的留言。