1. 程式人生 > >又到了買票的季節,學完Python這招你還會為車票而感到苦惱嗎?

又到了買票的季節,學完Python這招你還會為車票而感到苦惱嗎?

0?wx_fmt=gif&wxfrom=5&wx_lazy=1

一年一度的春運過段時間就到到來,今天小編給大家帶來如何使用Python攻克12306最後一道防線,實現自動搶票!

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1

最近有好幾個同學問我12306自動搶票能否實現,在這之前我分享過直播教學,不知道有多少人看見,今天文字實操,帶大家攻克12306最後一道防線,實現自動搶票。

我們要做12306搶票而官方又沒有提供相應的介面(也不可能提供),那麼我們就只能通過自己尋找12306的資料包和買票流程來模擬瀏覽器行為實現自動化操作了,說直白一點就是爬蟲,接下來進入正題,前方高能,請繫好安全帶~~

0?wx_fmt=jpeg

首先在買票前我們需要先確認是否有票,那麼進行正常的查票,開啟12306查票網站https://kyfw.12306.cn/otn/leftTicket/init 輸入出發地和目的地進行搜尋。

0?wx_fmt=jpeg

那麼一般在看到這個頁面的時候我們能想到的獲取車次及相關資訊的方式是什麼呢?對於零基礎的同學而言第一時間就會想到在原始碼裡面找,但這裡事實上原始碼裡面根本沒有相關內容,因為該請求是採用的js中ajax非同步請求的方式動態載入的,並不包含在原始碼裡面,所以我們只能夠通過抓包的方式來檢視瀏覽器與伺服器的資料互動情況,我用的是谷歌瀏覽器所以開啟開發者工具的快捷鍵是F12。

0?wx_fmt=jpeg

注意選中紅線框出來的那一個選項,此時只要是瀏覽器和伺服器發生資料互動都會在下面列表框顯示出來,我們再次點選查詢按鈕。

0?wx_fmt=jpeg

結果發現列表當中有了兩個請求,也就是說我們點選查詢按鈕以後瀏覽器向伺服器發起了兩次請求,那麼我們來通過返回值分析下那個請求才是真正獲取到車次相關資料的請求,以便我們用Python來模擬瀏覽器操作。

第一次請求:

0?wx_fmt=jpeg

很明顯第一次請求返回的值沒有我們需要的車次資訊。

第二次請求:

0?wx_fmt=jpeg

第二次請求裡面看到了很多資料,雖然我們暫時還沒看到車次資訊,但是我們發現它有個特性,就是有個列表的值裡面有6個元素,而剛好我們搜尋出來的從長沙到成都的車輛也是6條資料,所以這兩者肯定有一定關係,那麼我們先用Python來獲取到這些資料再進行下一步分析:

# -*- coding: utf-8 -*-importurllib2 importsslssl._create_default_https_context = ssl._create_unverified_context defgetList():req = urllib2.Request('https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-07-10&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=CSQ&purpose_codes=ADULT') req.add_header( 'User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36') html = urllib2.urlopen(req).read() returnhtml printgetList()

首先定義一個函式來獲取車次列表資訊:

從抓包資料中獲取到該請求的url:https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-07-10&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=CSQ&purpose_codes=ADULT

為了防止被12306檢測到遮蔽我們的請求那麼我們可以簡單的增加個頭資訊來模擬瀏覽器的請求。

req. add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36')

其中的:

ssl. _create _default_https _context = ssl. _create _unverified _context

zhengshu5.com
dajinnylee.cn
xc.xyseo.net
xyseo.net/xuancai/

是因為12306採用的是https協議,而ssl證書是它自己做的並沒有得到瀏覽器的認可,所以Python預設是不會請求不受信任的證書的網站的,我們可以通過這行程式碼來關閉掉證書的驗證

那麼我們先來看看能不能正常獲取到我們想要的資訊 :

0?wx_fmt=jpeg

事實證明我們的操作沒有問題,接下來先拿到包含有6條資料的這個列表再說。

返回的資料是json格式,但是Python標準資料型別中沒有json這個型別,所以對於Python而言它就是個字串,如果要非常方便的操作這個json我們就可以藉助Python中的json這個包來把json這個字串變成dict型別,然後通過dict的鍵值對操作方法把列表取出來並進行返回。

# -*- coding: utf-8 -*-importurllib2 importssl importjsonssl._create_default_https_context = ssl._create_unverified_contextdef getList(): req = urllib2. Request('https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date= 2017- 07-10&leftTicketDTO.from_station= CDW&leftTicketDTO.to_station= CSQ&purpose_codes=ADULT') req.add_header(' User- Agent',' Mozilla/ 5. 0( WindowsNT10. 0; Win64; x64)AppleWebKit/ 537. 36( KHTML, like Gecko) Chrome/ 59. 0. 3071. 115Safari/ 537. 36') html = urllib2.urlopen(req).read() dict = json.loads(html) result= dict['data'][' result'] returnresult

最終返回的是一個list資料,我們先把這個資料for出來再看看每一條資料都有些什麼東西:

foriingetList(): print i

for出來之後我們先來看看第一條資料是什麼樣的:

| 預訂| 76000G131805| G1318| ICW| IZQ| ICW| CWQ| 07:54| 18:54| 11:00| N|UHESFcaIDeX22Z0zWfqttDuZXJFuWPdIa148i6TNk5spIqfp| 20170710| 3| W2| 01| 16| 0|0||||||||||| 無| 無| 無|| O0M090|OM9

其實我們稍微留一下就會發現裡面有包含G1318,07:54,18:54,無這樣的車次資訊的,只不過看起來比較亂,但是他們都有一個特點,每個資料都是由|這個符號分開的,所以我們可以通過用|分割看看能發現什麼呢?

foriingetList(): forn ini. split('|'): print n break

0?wx_fmt=jpeg

可以看到所有的值都打印出來了,我們再在前面加上一個序號就能清楚到看到每個序號所對應的值到底是什麼了,比如有輛火車硬座還剩3張票,軟臥還剩8張票,那我們就檢視哪個序號對應的值是3哪個序號對應的值是8就搞清楚了哪個序號是代表什麼座次或者其他引數了。

c = 0fori ingetList(): forn ini.split( '|'): print'[%s] %s'%(c,n) c += 1c = 0break#索引3=車次#索引8=出發時間#索引9=到達時間

0?wx_fmt=jpeg

到了這裡不知道同學們有沒有發現一個問題,就是我用的這個函式只能夠獲取到從長沙到成都的資料,而別人不一定是買這個方向的火車,所以我們還得搞清楚請求的url當中的出發站和到達站的值是怎麼來的。

https:/ /kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date= 2017- 07-10&leftTicketDTO.from_station= CDW&leftTicketDTO.to_station= CSQ&purpose_codes=ADULT

先找到出發站和到達站的引數分別是:

leftTicketDTO.from_station= CDWleftTicketDTO.to_station=CSQ

然而通過查詢和分析我並沒有發現這兩個引數有規律,那麼也就是說這兩個值是在之前的請求裡面就已經獲取到了的,通過檢查網頁原始碼沒有找到,那麼又只能通過抓包的方式來找。

在抓包過程中找到了一個包的返回值是附帶有各城市的代號的,url如下:

https:/ /kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9018

0?wx_fmt=jpeg

那麼我們把這裡面的城市資料複製出來單獨新建一個cons.py的檔案儲存起來

0?wx_fmt=jpeg

然後我們通過把引數做成通過輸入出發城市和到達城市就可以直接在這個資料裡面匹配到相應的城市代號,程式碼如下:

station = {} fori incons.station_names. split( '@'): ifi: tmp = i. split( '|') station[tmp[ 1]] = tmp[ 2] #print stationtrain_date = raw_input( '請輸入出發時間')from_station = station[raw_input( '請輸入出發城市')]to_station = station[raw_input( '請輸入到達城市')]

到這裡就已經能夠通過輸入時間,城市獲取相應的車次資訊了 。

0?wx_fmt=jpeg

那麼我們再進行一些簡單的判斷,就能實現檢查相應的時間,地點,車次是否有餘票了。

同時再結合登入,購票等流程,通過自動判斷是否有票,如果無票就繼續重新整理,直到有票之後自動登入下單後通過簡訊或者電話等方式全自動聯絡購票人手機就可以了,如下圖:

0?wx_fmt=jpeg

                                       0?wx_fmt=jpeg