當(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)化還是比較簡單的


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)站檢測,沒加這句的話,賬號(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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容