歡迎光臨
每天分享高質量文章

再不看就晚了,我用Python搶到了回家的火車票!

不知不覺,一年一度的春運搶票大幕已經拉開,想快速搶到回家的車票嗎?作為程式員,這些技術手段,你一定要知道。


為了讓大家更快捷更便利的搶火車票,各種各樣的搶票軟體應需而生,這類軟體大部分都是付費搶票的機制。


作為程式員,如何用技術手段搶到回家的票?來看看用 Python 寫的搶票指令碼。


手把手教你用 Python 搶票回家過年


環境介紹


windows 8.1
python3.6.1
firefox外掛 geckodriver.exe


操作步驟


引入要的模組

   

     from selenium import webdriver      #控制瀏覽器

        from selenium.webdriver.common.keys import Keys  #用於給元素賦值

        import time   #時間模組

        from selenium.webdriver.support.select import Select  #控制下拉框模組

        from selenium.webdriver.common.by import By   #尋找元素模組

        from selenium.webdriver.support.ui import WebDriverWait  #“顯示等待”模組

        from selenium.webdriver.support import expected_conditions as EC  #等待條件模組


登陸模組


首先需要選擇使用的瀏覽器,此處以 firefox 為例,下載:geckodriver.exe 。


下載地址:

https://github.com/mozilla/geckodriver/releases


提到的 stations.txt 可以直接看這個:


車站資訊:

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


將 geckodriver.exe 放到 python.exe 同級目錄下即可(如果有報錯的情況下,放一個該檔案到與 firefox.exe 同級目錄下,並新增環境變數)

        #可以用input,也可以直接放入到後面的使用者名稱、密碼輸入框中

        #可以利用標準輸入進行批次的操作,此處以個人搶票操作為例

        # username = str(input(‘請輸入你的使用者名稱:’))

        # password = str(input(‘請輸入你的密碼:’))  #這兩行可以暫時忽略

        browser = webdriver.Firefox()      #驅動firefox瀏覽器

        browser.get(“https://kyfw.12306.cn/otn/login/init”)    #啟動瀏覽器後進入該連結下

        browser.find_element_by_id(‘username’).clear()

        browser.find_element_by_id(‘username’).send_keys(‘xxxxx’)    #xxxxx更換為使用者名稱  

        browser.find_element_by_id(‘password’).send_keys(‘xxxxx’)    #xxxxx更換為密碼   

        time.sleep(10)    #此時驗證碼自行點選,該處設定10秒延遲,可以自己設定

        try:

                browser.find_element_by_id(‘loginSub’).click()     #點選登陸操作,該id為登陸按鈕

                #或者 browser.find_element_by_link_text(‘登陸’).click()  #標簽顯示的名稱

        except:

                browser.find_element_by_class_name(‘touclick-bgimg touclick-reload touclick-reload-normal’).click()     #try中驗證碼輸入點錯了會在此處掃清一次

                time.sleep(20)                     #第二次輸入驗證碼前等待20秒,可以自己設定,第一次輸入無誤直接跳過

                browser.find_element_by_id(‘loginSub’).click()    #重新輸入驗證碼後的點選登陸

跳轉模組

#預設跳轉到首頁

        time.sleep(2)    #此處一般無需設定時間等待,除錯程式碼時使用

        clickReserve = browser.find_element_by_link_text(‘車票預訂’).click()  #跳轉到車票預定頁面,該頁面可以查詢票

        time.sleep(2)        #出發地點和到達地點設定

        #此處value值為出發時刻的地點,BJP表示北京,更改value值在頁面上不載入,基本不耗時間,從頁面中也看不到出發地和目的地

        #此處內容以爬取,儲存在stations.txt中,每行表示一個地址,開啟檔案ctrl + F查詢即可

        jsf = ‘var a = document.getElementById(“fromStation”);a.value = “BJP”‘    #此處將BJP更換為你需要的出發地址,value值在以爬取到stations.txt中,自行檢視

        browser.execute_script(jsf)

        jst = ‘var a = document.getElementById(“toStation”);a.value = “LZJ”‘   #終點,同上方法

        browser.execute_script(jst)

        js = “document.getElementById(‘train_date’).removeAttribute(‘readonly’)”    #時間選擇時預設為只讀,透過JS移除只讀屬性

        browser.execute_script(js)    #執行JS陳述句

        browser.find_element_by_id(‘train_date’).clear()    #時間元素中預設有提示字,需要先清空

        browser.find_element_by_id(‘train_date’).send_keys(‘2018-02-01’)   #按照改格式輸入需要查詢的時間

        search = browser.find_element_by_id(‘query_ticket’).click()    #輸入好資訊時點選查詢,該處存在成人票和學生票,預設是成人票,如果購買,對學生票處執行以下陳述句即可:

        #browser.find_element_by_id(‘xxxx’).click()    #對於id還是class或其它自行選擇,[可以檢視此處](http://blog.51cto.com/12376665/2052278)

開始購票


此處,就是點選預定的操作,我在這裡只是舉一個方法例子,也可以透過不斷點選直到成功(這樣可以避免網站倒計時和實際時間的時間差影響,但是不知道 12306 在搶票時對不斷快速訪問有沒有限制)。

  start_time = “Thu Jan 04 08:00:00 2018”    #首先設定需要搶票的時間

        b = time.mktime(time.strptime(start_time,”%a %b %d %H:%M:%S %Y”))        print(time.strftime(“%a %b %d %H:%M:%S %Y”, time.localtime(b)) )  #此處是為了除錯程式碼使用,可忽略,不影響使用

        a = float(b)-time.time()    #利用自己設定的時間減去當前時間的時間戳

        time.sleep(a)    #上一步驟得出的秒數就是需要等待搶票的時間


try:     #此處本來有try中的部分就夠了,WebDriverWait已有相應等待重覆訪問機制,預設為0.5秒試驗一次,except中新增是為了以防萬一

    WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, “ticket_2400000Z550L”)))   #查詢需要預定的車次的id,直到出現,10表示共等待10秒

    ticket = browser.find_element_by_xpath(‘//tr[@id=”ticket_2400000Z550L”]/td[13]/a’).click()    #點選預定按鈕except:

    browser.find_element_by_id(‘query_ticket’).click()

    WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.ID, “ticket_2400000Z550L”)))

    ticket = browser.find_element_by_xpath(‘//tr[@id=”ticket_2400000Z550L”]/td[13]/a’).click()



“””

normalPassenger_8 數字表示該賬號下的第幾位,預設從0開始如果是第一個則為normalPassenger_0

“””WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, “normalPassenger_8”)))

browser.find_element_by_id(‘normalPassenger_8’).click()   #id中的8表示賬號下第九位s = Select(browser.find_element_by_id(‘seatType_1’))

s.select_by_value(‘6’)    #此處value值看下方各個種類,6表示高階軟臥browser.find_element_by_id(‘submitOrder_id’).click()

WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, “qr_submit_id”)))

browser.find_element_by_link_text(‘提交訂單’)

browser.find_element_by_id(‘qr_submit_id’).click()#————————————————-結束#硬座 1#硬臥 3#軟臥 4#高階軟臥 6#二等座 O(大寫字母)#一等座 M#商務座 9

總結


需要替換的地方:

  • 使用者名稱,密碼。

  • 起始地點和目的地的 value 值,查 stations.txt 修改即可。

  • 出發時間。

  • 自己選擇車次的 xpath 路徑,路徑不用變,變對應 id 即可。

  • 勾選使用者的位置(如果只要一個使用者,預設用:normalPassenger_0)。

  • 所選座位類別,預設為有票的類別裡最便宜的種類。


其餘的在測試中都相同,沒有發現有變化,在使用前,可以測試一下程式碼,測試是註意註釋掉提交訂單的程式碼(下單有取消限制,每天好像只能取消三次),測試時網速正常。


有人說用瀏覽器執行速度會慢,確實對於可以直接識別驗證碼的指令碼而言,沒有介面的會更快一些,但是實際上所用時間為預定開始到結束,相同網路下,程式碼執行時間是要快於人工操作的,


另外,時間可以研究一下,之前研究過某寶的時間,秒殺時間是要比北京時間提前一點幾秒的,感覺全國各地有微小時間差的。


完整指令碼示例


#python3.6.1#data:2018-01-03#author:LGC247CG”””

說明:

1.該指令碼主要是提供一個實現思路,實現方法有很多,可以最佳化的地方也有很多,觸發機制也可以自己設定,程式碼以壓縮到最短,只是為了讓大家都可以看明白

2.正常網路狀況下,不設定指定時間時,從點選確認驗證碼到下單基本上1秒左右,所以速度上還是沒問題的

3.由於同時勾選多人和單人使用所需時間基本相同,希望該方法只用於技術交流,請勿作為黃牛使用

4.在作為技術交流的情況下,如果驗證碼可以實現將可以完全實現自動搶票:

–1>驗證碼有一定規律和數量,可以利用指令碼獲取所有圖片,並加上相應標簽

–2>將頁面的文字和標簽相匹配,再將圖片進行相似度計算,對對應圖片進行點選操作

–3>或是訓練深度學習的圖片識別模型,透過演演算法識別

“””from selenium import webdriverfrom selenium.webdriver.common.keys import Keysimport timefrom selenium.webdriver.support.select import Selectfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as EC

browser = webdriver.Firefox()

browser.get(“https://kyfw.12306.cn/otn/login/init”)

browser.find_element_by_id(‘username’).clear()

browser.find_element_by_id(‘username’).send_keys(‘xxxxxxx’)

browser.find_element_by_id(‘password’).send_keys(‘xxxxxxx’)

time.sleep(10)try:

    browser.find_element_by_id(‘loginSub’).click()except:

    browser.find_element_by_class_name(‘touclick-bgimg touclick-reload touclick-reload-normal’).click()

    time.sleep(15)

    browser.find_element_by_id(‘loginSub’).click()#跳轉到車票預定頁面time.sleep(2)

clickReserve = browser.find_element_by_link_text(‘車票預訂’).click()#出發地點和到達地點設定WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, “fromStation”)))

jsf = ‘var a = document.getElementById(“fromStation”);a.value = “BJP”‘browser.execute_script(jsf)

jst = ‘var a = document.getElementById(“toStation”);a.value = “LZJ”‘browser.execute_script(jst)

js = “document.getElementById(‘train_date’).removeAttribute(‘readonly’)”browser.execute_script(js)

browser.find_element_by_id(‘train_date’).clear()

browser.find_element_by_id(‘train_date’).send_keys(‘2018-02-02’)

search = browser.find_element_by_id(‘query_ticket’).click()#對於時間,我一直覺得網站計算的時間和自己獲取的時間差一秒左右,這個根據不同環境自己測試start_time = “Thu Jan 04 10:00:00 2018″    #首先設定需要搶票的時間b = time.mktime(time.strptime(start_time,”%a %b %d %H:%M:%S %Y”))

print(time.strftime(“%a %b %d %H:%M:%S %Y”, time.localtime(b)) )  #此處是為了除錯程式碼使用,可忽略,不影響使用a = float(b)-time.time()    #利用自己設定的時間減去當前時間的時間戳time.sleep(a)    #上一步驟得出的秒數就是需要等待搶票的時間browser.find_element_by_id(‘query_ticket’).click()    #時間到了先點選查詢掃清一下,以防找不到元素try:

    WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, “ticket_2400000Z550L”)))

    ticket = browser.find_element_by_xpath(‘//tr[@id=”ticket_2400000Z550L”]/td[13]/a’).click()except:

    browser.find_element_by_id(‘query_ticket’).click()

    WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.ID, “ticket_250000K8880L”)))

    ticket = browser.find_element_by_xpath(‘//tr[@id=”ticket_250000K8880L”]/td[13]/a’).click()”””

normalPassenger_8 數字表示該賬號下的第幾位,預設從0開始如果是第一個則為normalPassenger_0

“””WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, “normalPassenger_8”)))

browser.find_element_by_id(‘normalPassenger_8’).click()

s = Select(browser.find_element_by_id(‘seatType_1’))

s.select_by_value(‘6’)

browser.find_element_by_id(‘submitOrder_id’).click()

WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, “qr_submit_id”)))

browser.find_element_by_link_text(‘提交訂單’)#browser.find_element_by_id(‘qr_submit_id’).click()

作者:LGC247CG

簡介:大學時候就已經接觸 Python,並研究 Python 爬蟲,資料分析,文字處理,影象處理等,有五年應用經驗,工作以後主要用在 Linux 系統運維指令碼。
本文轉載自51CTO技術棧,編輯:陶家龍、孫淑娟

推薦閱讀



高可用架構

改變網際網路的構建方式

長按二維碼 關註「高可用架構」公眾號

贊(0)

分享創造快樂