當(dāng)我準(zhǔn)備出門時(shí)樊展,發(fā)現(xiàn)了......我可以用Python實(shí)現(xiàn)12306自動(dòng)買票

前言

不知道大家有沒有之前碰到這樣的情況,打算去某一個(gè)地方當(dāng)你規(guī)劃好了時(shí)間準(zhǔn)備去買票的時(shí)候宋梧,你想要的那一列往往沒有你想要的票了匣沼,尤其是國慶七天假和春節(jié)半月假,有時(shí)候甚至買不到規(guī)定計(jì)劃時(shí)間內(nèi)的票捂龄,真的是太煩躁了

為此我鉆研了一下释涛,現(xiàn)在科技如此發(fā)達(dá)加叁,想要實(shí)現(xiàn)自動(dòng)化還是比較簡(jiǎn)單的


1.導(dǎo)入需要的模塊

import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

2.初始化WebDriver:

driver = webdriver.Chrome()  # 選擇合適的瀏覽器驅(qū)動(dòng),這里以Chrome為例

3.打開12306網(wǎng)站:

driver.get('https://www.12306.cn')

4.登錄12306賬號(hào):

首先手動(dòng)登錄一次唇撬,然后在瀏覽器中的開發(fā)者工具中找到登錄請(qǐng)求的相關(guān)信息它匕,提取出關(guān)鍵參數(shù)(比如cookies、token等)窖认,可以使用工具庫來自動(dòng)提取這些參數(shù)豫柬。

使用Python的requests庫等發(fā)送該登錄請(qǐng)求,將提取到的關(guān)鍵參數(shù)作為請(qǐng)求的header或body發(fā)送過去扑浸,模擬登錄烧给。

5.進(jìn)入車票查詢頁面:

# 這里可能需要等待一段時(shí)間,直到頁面加載完成
# 可以使用WebDriverWait等待特定的元素加載完成
wait = WebDriverWait(driver, 10)  # 設(shè)置等待時(shí)間為10秒
query_input = wait.until(EC.presence_of_element_located((By.ID, 'query_input')))
query_input.clear()
query_input.send_keys('出發(fā)地點(diǎn)')

上述代碼將輸入出發(fā)地點(diǎn)喝噪,你需要根據(jù)自己的需求修改础嫡。

6.查詢車票:

search_btn = driver.find_element_by_id('search_btn')
search_btn.click()

# 這里可能需要等待一段時(shí)間,直到查詢結(jié)果加載完成
# 同樣可以使用WebDriverWait等待特定的元素加載完成

7.選擇車次和座位:

train_btn = driver.find_element_by_id('train_btn')
train_btn.click()

# 這里可能需要等待一段時(shí)間酝惧,直到車次詳情加載完成

seat_type = driver.find_element_by_id('seat_type')
seat_type.send_keys('座位類型')

buy_btn = driver.find_element_by_id('buy_btn')
buy_btn.click()

上述代碼將選擇指定的座位類型榴鼎,你需要根據(jù)自己的需求修改。

8.填寫乘客信息和提交訂單:

passenger_name = driver.find_element_by_id('passenger_name')
passenger_name.send_keys('乘客姓名')

id_number = driver.find_element_by_id('id_number')
id_number.send_keys('乘客身份證號(hào)碼')

# 填寫其他乘客信息晚唇,如果有多個(gè)乘客

submit_btn = driver.find_element_by_id('submit_btn')
submit_btn.click()

上述代碼將填寫乘客的姓名檬贰、身份證號(hào)碼等信息,你需要根據(jù)自己的需求修改缺亮。

9.處理驗(yàn)證碼:

12306網(wǎng)站可能會(huì)出現(xiàn)驗(yàn)證碼翁涤,你需要使用圖像處理庫(如PIL)來處理驗(yàn)證碼圖片并自動(dòng)識(shí)別驗(yàn)證碼。

如果遇到驗(yàn)證碼萌踱,你可以通過人工干預(yù)或使用一些自動(dòng)化技術(shù)(如OCR)處理葵礼,以便自動(dòng)填寫驗(yàn)證碼。

10.確認(rèn)訂單和支付:

confirm_btn = driver.find_element_by_id('confirm_btn')
confirm_btn.click()

# 這里可能需要等待一段時(shí)間并鸵,直到支付頁面加載完成

# 在該頁面處理支付鸳粉,根據(jù)你使用的支付方式進(jìn)行自動(dòng)化支付

以上只是一個(gè)基本的框架,實(shí)際實(shí)現(xiàn)可能需要根據(jù)12306網(wǎng)站的更新和變化進(jìn)行相應(yīng)的調(diào)整

那么根據(jù)具體時(shí)間2023年8月10號(hào)為例子园担,我們來具體操作一下届谈,以下是具體源碼:

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from config import Config
from selenium.webdriver.common.keys import Keys
import time
import select
 
# 用拋出異常來判斷一個(gè)元素存不存在太慢了,需要等5秒鐘
# def isElementExist(ele):
#     flag = True
#     result = EC.presence_of_element_located((By.XPATH, '//tbody[@id="queryLeftTable"]/tr[1]/td[13]/a'))
#     try:
#         # ele.find_element(by=By.CLASS_NAME, value='btn72')
#         result(ele)
#         return flag
#     except:
#         flag = False
#         return flag
 
 
def isElementExist(driver):
    flag=True
    ele = driver.find_elements(by=By.CLASS_NAME, value='btn72')
    if len(ele) == 0:
        flag = False
        return flag
    if len(ele) == 1:
        return flag
    else:
        flag = False
        return flag
 
 
def get_ticket(conf, driver, url):
    # 過網(wǎng)站檢測(cè)弯汰,沒加這句的話艰山,賬號(hào)密碼登錄時(shí)滑動(dòng)驗(yàn)證碼過不了,但二維碼登錄不受影響
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": """Object.defineProperty(navigator, 'webdriver', {
          get: () => undefined})"""})
    driver.maximize_window()
    driver.get(url)
    # 最多等待5秒使頁面加載進(jìn)來咏闪,隱式等待
    driver.implicitly_wait(5)
 
    # 獲取并點(diǎn)擊右上角登錄按鈕
    login = driver.find_element(by=By.ID, value='J-btn-login')
    login.click()
    driver.implicitly_wait(10)
 
    # 賬號(hào)密碼登錄
    username_tag = driver.find_element(by=By.ID, value='J-userName')
    username_tag.send_keys(conf.username)
    password_tag = driver.find_element(by=By.ID, value='J-password')
    password_tag.send_keys(conf.password)
    login_now = driver.find_element(by=By.ID, value='J-login')
    login_now.click()
    time.sleep(20)
 
    # # 過滑動(dòng)驗(yàn)證碼
    # picture_start = driver.find_element(by=By.ID, value='nc_1_n1z')
    # # 移動(dòng)到相應(yīng)的位置曙搬,并左鍵鼠標(biāo)按住往右邊拖
    # ActionChains(driver).move_to_element(picture_start).click_and_hold(picture_start).move_by_offset(300, 0).release().perform()
    #
    #
    # # 掃碼登錄
    # scan_QR = driver.find_element(by=By.XPATH, value='//*[@id="toolbar_Div"]/div[2]/div[2]/ul/li[2]/a')
    # scan_QR.click()
    # driver.implicitly_wait(10)
 
 
    # 點(diǎn)提示框
    # driver.find_element(by=By.XPATH, value='//div[@class="dzp-confirm"]/div[2]/div[3]/a').click()
    # driver.implicitly_wait(5)
 
    # 點(diǎn)擊車票預(yù)訂跳轉(zhuǎn)到預(yù)訂車票頁面
    driver.find_element(by=By.XPATH, value='//*[@id="link_for_ticket"]').click()
    driver.implicitly_wait(10)
 
    # 輸入出發(fā)地和目的地信息
    # 出發(fā)地
    driver.find_element(by=By.XPATH, value='//*[@id="fromStationText"]').click()
    driver.find_element(by=By.XPATH, value='//*[@id="fromStationText"]').clear()
    driver.find_element(by=By.XPATH, value='//*[@id="fromStationText"]').send_keys(conf.fromstation)
    time.sleep(1)
    driver.find_element(by=By.XPATH, value='//*[@id="fromStationText"]').send_keys(Keys.ENTER)
 
    # 目的地
    destination_tag = driver.find_element(by=By.XPATH, value='//*[@id="toStationText"]')
    destination_tag.click()
    destination_tag.clear()
    destination_tag.send_keys(conf.destination)
    time.sleep(1)
    destination_tag.send_keys(Keys.ENTER)
    driver.implicitly_wait(5)
 
    # 出發(fā)日期
    date_tag = driver.find_element(by=By.XPATH, value='//*[@id="train_date"]')
    date_tag.click()
    date_tag.clear()
    date_tag.send_keys(conf.date)
    time.sleep(1)
    query_tag = driver.find_element(by=By.XPATH, value='//*[@id="query_ticket"]')
 
    start = time.time()
 
    while True:
        driver.implicitly_wait(5)
        # 點(diǎn)擊查詢
        driver.execute_script("$(arguments[0]).click()", query_tag)
 
        # 判斷頁面中有沒有“預(yù)訂”按鈕,如果沒有預(yù)訂按鈕就不斷查詢直到車票開售
        if not isElementExist(driver):
            # 車票處于待開售狀態(tài)
            print(f"15點(diǎn)30分起售,現(xiàn)在是{time.strftime('%H:%M:%S', time.localtime())}纵装,還未開始售票")
            # 每隔兩分鐘刷新一次征讲,否則3分鐘內(nèi)無購票操作12306系統(tǒng)會(huì)自動(dòng)登出
            if time.time() - start >= 120:
                driver.refresh()
                start = time.time()
            # 延時(shí)1秒防止過于快速地點(diǎn)擊導(dǎo)致查詢超時(shí),當(dāng)然偶爾還是會(huì)出現(xiàn)超時(shí)現(xiàn)象橡娄,不過超時(shí)也沒關(guān)系诗箍,一般等待6秒之后就會(huì)繼續(xù)自動(dòng)查詢
            time.sleep(1)
            continue
 
        # 獲取所有車票
        tickets = driver.find_elements(by=By.XPATH, value='//*[@id="queryLeftTable"]/tr')
        # 每張車票有兩個(gè)tr,但是第二個(gè)tr沒什么用
        tickets = [tickets[i] for i in range(len(tickets) - 1) if i % 2 == 0]
        #print(tickets)
        for ticket in tickets:
            # 如果車票的車次等于想要的車次并且硬臥的狀態(tài)不是候補(bǔ)則點(diǎn)擊預(yù)訂
            #if ticket.find_element(by=By.CLASS_NAME,value='cdz').text== conf.fromstation:
                #print(ticket.find_element(by=By.CLASS_NAME,value='number').text)
                # value = '//td[8]'表示硬臥挽唉,td[10]表示硬座
            if ticket.find_element(by=By.CLASS_NAME,value='number').text == conf.trainnumber and ticket.find_element(by=By.XPATH, value='//td[8]').text != "候補(bǔ)":
                # 點(diǎn)擊預(yù)訂
                #print(ticket.find_element(by=By.CLASS_NAME,value='cdz').text)
                #time.sleep(1)
                ticket.find_element(by=By.CLASS_NAME, value='btn72').click()
                # 這里之后就不能繼續(xù)使用ticket.find_element()了扳还,因?yàn)轫撁孢M(jìn)行了跳轉(zhuǎn),會(huì)出現(xiàn)stale element reference: element is not attached to the page document的錯(cuò)誤
                # 我們可以使用driver.find_element()
                # 選擇乘車人橱夭,如果是學(xué)生氨距,則需要確認(rèn)購買學(xué)生票
                driver.find_element(by=By.XPATH, value='//*[@id="normalPassenger_0"]').click()
                # 點(diǎn)擊確認(rèn)購買學(xué)生票,如果不是學(xué)生棘劣,把這行注釋了就行
                #driver.find_element(by=By.XPATH, value='//*[@id="dialog_xsertcj_ok"]').click()
                # 第二個(gè)乘車人
                # driver.find_element(by=By.XPATH, value='//*[@id="normalPassenger_1"]').click()
                # 如果第二個(gè)乘車人也是學(xué)生俏让,則需要點(diǎn)擊確認(rèn)第二個(gè)人也購買學(xué)生票
                # driver.find_element(by=By.XPATH, value='//*[@id="dialog_xsertcj_ok"]').click()
                # 提交訂單
                driver.find_element(by=By.XPATH, value='//*[@id="submitOrder_id"]').click()
                # 選座  F座
                #time.sleep(1)
                #move = driver.find_element(By.ID, value='1F')
                #ActionChains(driver).move_to_element(move).perform()
                # time.sleep(1)
                #這里直接使用id和xpath定位不到,所以直接加上他的路徑,可以不用這么長茬暇,但是懶得刪
                driver.find_element(by=By.XPATH, value='//html/body/div[5]/div/div[5]/div[1]/div/div[2]/div[2]/div[3]/div[2]/div[2]/ul[2]/li[2]/a[@id="1F"]').click()
                # 確認(rèn)提交訂單首昔,然后這里和上面是一樣的
                driver.find_element(by=By.XPATH, value='//html/body/div[5]/div/div[5]/div[1]/div/div[2]/div[2]/div[8]/a[2][@id="qr_submit_id"]').click()
                print(f"{conf.trainnumber}次列車搶票成功,請(qǐng)盡快在10分鐘內(nèi)支付糙俗!")
                return
 
 
if __name__ == '__main__':
    # 有關(guān)車票的配置信息保存在該類里
    # 請(qǐng)事先在config.py里填好相關(guān)信息
    conf = Config()
 
    url = 'https://www.12306.cn/index/'
 
    # chromedriver.exe版本為104勒奇,可以根據(jù)自己瀏覽器版本重新下載chromedriver.exe替換
    # chromedriver.exe下載地址:http://chromedriver.storage.googleapis.com/index.html
    # s = Service(r'chromedriver.exe')
    driver = webdriver.Chrome()
    get_ticket(conf, driver, url)
    time.sleep(10)
    driver.quit()

有問題歡迎留言~~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市巧骚,隨后出現(xiàn)的幾起案子赊颠,更是在濱河造成了極大的恐慌,老刑警劉巖劈彪,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竣蹦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡沧奴,警方通過查閱死者的電腦和手機(jī)痘括,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來滔吠,“玉大人纲菌,你說我怎么就攤上這事〈粒” “怎么了翰舌?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長矗愧。 經(jīng)常有香客問我灶芝,道長郑原,這世上最難降的妖魔是什么唉韭? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任夜涕,我火速辦了婚禮,結(jié)果婚禮上属愤,老公的妹妹穿的比我還像新娘女器。我一直安慰自己,他們只是感情好住诸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布驾胆。 她就那樣靜靜地躺著,像睡著了一般贱呐。 火紅的嫁衣襯著肌膚如雪丧诺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天奄薇,我揣著相機(jī)與錄音驳阎,去河邊找鬼。 笑死馁蒂,一個(gè)胖子當(dāng)著我的面吹牛呵晚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沫屡,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼饵隙,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了沮脖?” 一聲冷哼從身側(cè)響起金矛,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎勺届,沒想到半個(gè)月后绷柒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涮因,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年废睦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片养泡。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嗜湃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出澜掩,到底是詐尸還是另有隱情购披,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布肩榕,位于F島的核電站刚陡,受9級(jí)特大地震影響惩妇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜筐乳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一歌殃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蝙云,春花似錦氓皱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至身隐,卻和暖如春廷区,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贾铝。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工隙轻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人忌傻。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓大脉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親水孩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子镰矿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容