12306自動刷票下單-查票下單


12306自動刷票下單-登錄

前言

上篇寫了12306登錄,隔了快一個月了,才准備動手寫下單篇,真的要非常感謝博客園的 Asimple朋友,如果不是看到你的留言,我幾乎都忘了要寫下篇了,這一點在簡書上就不好,都沒人看/(ㄒoㄒ)/~~,剛開始寫博客,真的需要大家的鼓勵,看的人多了自然有動力寫更多的,所以這一篇要給那些看過我上篇的同學們,尤其是這位Asimple同學,就是為你而寫,沒錯就是這個原因。因為你讓我知道了有人在看,而且用心的在看。發了這么多感慨,其實我不是這么愛感慨的人(✿◡‿◡),直入主題吧。

圖片.png

還得說一下這次我用的是Firefox瀏覽器,在上篇中說了Chrome瀏覽器的一個問題,在抓取的請求過多的情況下,前面的請求就可能看不到請求信息,最后搞得我不得不搞了個虛擬機,裝了xp,用Fiddler去查看請求信息,后來就換了Firefox,最新版的Firefox沒有Firebug可用,但是自帶的開發者工具足夠用了。當然Chrome我也沒卸載,因為Chrome上有個很贊的功能,在元素界面查找元素的時候可以用css和xpath,可不是右鍵copy里面的css和xpath哦

圖片.png

在這個查找框里你就可以寫自己的css和xpath,看實時效果,這個真是太棒了,不用裝額外的插件。Firefox上暫時還沒有發現這個功能,也不知道有沒有類似的插件,如果有人知道的話,麻煩回復一下,先謝過了。鼠標左鍵還壞了,只能把右鍵設置一下暫時用着,一波三折呀!這次真的要進入主題了。

圖片.png

卧了一個槽,忽略我上面說的吧,要寫博客了,有一個請求Firefox竟然不給顯示了,還是虛擬機吧


后記

后記為什么要加載前言后面,而不是在文章最后,我怕你不看。在這次分析中我基本沒有添加什么代碼,因為基本上每一個請求就是定義一個字典、一個url,然后發送請求,獲取數據,然后繼續下一個。另一方面就是我並沒有做代碼優化和整理。我們都應該知道對於沒有反爬措施的網站,基本上看兩個小時的爬蟲教程就能寫的出來,對於有反爬的網站,最難得地方是分析階段,而不是發送請求。最后一點就是做這個也是一時興起,積累一下經驗,最主要是開始寫一寫博客。同時給像我一樣初學爬蟲的朋友一個例子、一個思路。


查票

圖片.png


車票預訂界面的url:https://kyfw.12306.cn/otn/leftTicket/init,選好票以后點擊查詢

圖片.png


多了一下兩個請求,第一個請求我沒用,沒有任何影響,我們就不用去管它,直接看第二個請求
https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-12-25&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=SHH&purpose_codes=ADULT

圖片.png

看一下參數和返回數據,太亂了,稍微仔細看一下,預訂、有、G5,好像還是有些有用信息的,可以按|拆分一下看看

圖片.png

 

圖片.png

看到了車次G101,8、9是開車時間和到達時間,10是歷時,商務座特等座9張余票,32行顯示9,一等二等座都是有票,對應30、31,就不能具體確定了,暫時先不管,至少我們確定了這個請求是查詢出我們需要的車票信息了,那么再看一下請求參數,第一個是時間很容易理解,第四個好像是票的類型,成人票,翻譯一下單詞就知道了,反正每次都一樣,不用管了,中間兩個出發站、目的地,不過這些字母是啥意思,應該是站名對應的編碼,在這個請求之前肯定是有對應關系的,

圖片.png


https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9034
一個js的請求@bjb|北京北|VAP|beijingbei|bjb|0,拆分以后可以看到站名和編號
建議看完所有的分析在看代碼

def query_ticket_info(date, info): js_info = json.loads(info) if js_info.get("status") != True: print("查詢余票失敗") return result = js_info.get("data").get("result") for i in result: lst = i.split('|') if lst[11] != "Y": #主要是判斷是否開票了,見下圖情況 continue item = { "預定號":lst[0], #預定號 "train_no":lst[2], "車次":lst[3], #車次 "始發站":get_station_name_from_code(lst[4]), #始發站 "終點站":get_station_name_from_code(lst[5]), #終點站 "起始站":get_station_name_from_code(lst[6]), #起始站 "目標站":get_station_name_from_code(lst[7]), #目標站 "出發時間":"{} {}".format(date, lst[8]), #出發時間 "到達時間":get_end_time(date, lst[8], lst[9], lst[10]), #到達時間 "歷時":lst[10], #歷時 "train_location":lst[15], "高級動卧":lst[21], #高級動卧 "軟卧":lst[23], #軟卧 "軟座":lst[24], #軟座 "特等座":lst[25], #特等座 "無座":lst[26], #無座 "硬卧":lst[28], #硬卧 "硬座":lst[29], #硬座 "二等座":lst[30], #二等座 "一等座":lst[31], #一等座 "商務座":lst[32], #商務座 "動卧":lst[33], #動卧 } yield item 
圖片.png

這個函數需要解釋一下,主要是這些站點信息是怎么找到的,說一下思路

  • 當我們打開車票預訂界面的時候,是這樣的,下面是空的
    圖片.png
  • 點擊查詢后
    圖片.png
  • 由此我們知道車票信息是通過js動態添加的(maybe),那么我們就查找動態創建出來的元素,隨便在上面找一個元素特征,比如我找這個
    圖片.png
    當然你可以隨便找一個,前提是不是動態創建的,然后在我們的js和document請求中去查找這個值,最后我找到了https://kyfw.12306.cn/otn/resources/merged/queryLeftTicket_end_UAM_js.js?scriptVersion=1.9053
    圖片.png
    我找這個是干什么呢?我們上面的請求得到了余票信息,是json格式的,那么肯定是通過js把它添加到界面上,我們要知道哪一個信息是添加到硬座上,哪一個是添加到硬卧上,就像我代碼里寫的那樣,把所有的票種都找出來
  • 通過分析js
    圖片.png

    然后把大部分我們需要的參數都對應出來,這樣就看到像我上面寫的函數那樣,取出我們需要的信息

  • 其實有一種更簡單的方式,就是你查詢了余票信息以后,會看到余票幾張幾張,然后去對應的信息參數中去找,比如
    圖片.png

    我們就知道了32對應的是商務座特等,多查詢一些站點就會把所有對應索引都找出來


預訂

點擊預訂后看一下請求,記住我上一遍說過的,一般是看xhr和document請求,
https://kyfw.12306.cn/otn/login/checkUser

圖片.png

看起來很簡單,參數也只有一個_json_att,值為空
重頭戲來了https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest

圖片.png

看請求參數secretStr,其他的請求幾次發現沒啥變化,重點就在這個secretStr上了,太亂了,咦,我上面好像說過這三個字,對他們有關系。怎么去理解呢,這里是發了一個post請求,而這個secretStr是作為參數發送給服務器的,那么它必定是在我們本地產生的,在這個請求之前應該能找到。看一下特征:+wOQuwrBzvR6e...。是不是能發現查票那個請求里返回的數據第一個%2BwOQuwrBzvR6e,相似度很高啊,肯定是進行了編碼或解碼。Fiddler這點很好

圖片.png

點擊查票請求的第一條數據,右鍵->Send to TextWizare...

圖片.png

哇哦,So Beautiful,這下就相等了,是使用了urldecode,這里支持很多種編碼解碼方式,非常方便,真相已經出來了,secretStr是我們上面輸出的第0行字符串的urldecode解碼值,python3中是parse.unquote。
train_date:訂哪一天的票,back_train_date:今天的時間,還有出發站和目的地。

12306自動刷票下單-下單

 


如果你覺得我的文章還可以,可以關注我的微信公眾號:Python爬蟲實戰之路
也可以掃描下面二維碼,添加我的微信號

 
公眾號

 

 
微信號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM