When I was about to go out, I found out... I can use Python to realize 12306 automatic ticket purchase

foreword

I don’t know if you have encountered such a situation before. When you plan to go to a certain place, when you plan the time and prepare to buy tickets, the ticket you want is often not available in the column you want, especially during the seven-day National Day holiday. And the half-month holiday of the Spring Festival, sometimes I can't even buy a ticket within the specified planning time, which is really annoying

For this reason, I studied it a bit. Now that the technology is so advanced, it is relatively simple to realize automation.

1. Import the required modules

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. Initialize WebDriver:

driver = webdriver.Chrome()  # 选择合适的浏览器驱动,这里以Chrome为例

3. Open the 12306 website:

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

4. Log in to the 12306 account:

First, log in manually once, then find the relevant information of the login request in the developer tools in the browser, and extract key parameters (such as cookies, token, etc.), and use the tool library to automatically extract these parameters.

Use Python's requests library to send the login request, and send the extracted key parameters as the header or body of the request to simulate login.

5. Enter the ticket query page:

# 这里可能需要等待一段时间,直到页面加载完成
# 可以使用WebDriverWait等待特定的元素加载完成
wait = WebDriverWait(driver, 10)  # 设置等待时间为10秒
query_input = wait.until(EC.presence_of_element_located((By.ID, 'query_input')))
query_input.clear()
query_input.send_keys('出发地点')

The above code will enter the departure location, you need to modify it according to your needs.

6. Query tickets:

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

# 这里可能需要等待一段时间,直到查询结果加载完成
# 同样可以使用WebDriverWait等待特定的元素加载完成

7. Select train number and seat:

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

# 这里可能需要等待一段时间,直到车次详情加载完成

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()

The above code will select the specified seat type, you need to modify it according to your needs.

8. Fill in the passenger information and submit the order:

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('乘客身份证号码')

# 填写其他乘客信息,如果有多个乘客

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

The above code will fill in the passenger’s name, ID number and other information, and you need to modify it according to your needs.

9. Process verification code:

12306 There may be a verification code on the website, you need to use an image processing library (such as PIL) to process the verification code image and automatically identify the verification code.

If you encounter a verification code, you can process it through manual intervention or use some automation technology (such as OCR) to automatically fill in the verification code.

10. Confirm order and payment:

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

# 这里可能需要等待一段时间,直到支付页面加载完成

# 在该页面处理支付,根据你使用的支付方式进行自动化支付

The above is just a basic framework, and the actual implementation may need to be adjusted accordingly according to the updates and changes of the 12306 website

Then, according to the specific time of August 10, 2023 as an example, let's do a specific operation. The following is the specific source code:

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
 
# 用抛出异常来判断一个元素存不存在太慢了,需要等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):
    # 过网站检测,没加这句的话,账号密码登录时滑动验证码过不了,但二维码登录不受影响
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": """Object.defineProperty(navigator, 'webdriver', {
          get: () => undefined})"""})
    driver.maximize_window()
    driver.get(url)
    # 最多等待5秒使页面加载进来,隐式等待
    driver.implicitly_wait(5)
 
    # 获取并点击右上角登录按钮
    login = driver.find_element(by=By.ID, value='J-btn-login')
    login.click()
    driver.implicitly_wait(10)
 
    # 账号密码登录
    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)
 
    # # 过滑动验证码
    # picture_start = driver.find_element(by=By.ID, value='nc_1_n1z')
    # # 移动到相应的位置,并左键鼠标按住往右边拖
    # 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)
 
 
    # 点提示框
    # driver.find_element(by=By.XPATH, value='//div[@class="dzp-confirm"]/div[2]/div[3]/a').click()
    # driver.implicitly_wait(5)
 
    # 点击车票预订跳转到预订车票页面
    driver.find_element(by=By.XPATH, value='//*[@id="link_for_ticket"]').click()
    driver.implicitly_wait(10)
 
    # 输入出发地和目的地信息
    # 出发地
    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)
 
    # 出发日期
    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)
        # 点击查询
        driver.execute_script("$(arguments[0]).click()", query_tag)
 
        # 判断页面中有没有“预订”按钮,如果没有预订按钮就不断查询直到车票开售
        if not isElementExist(driver):
            # 车票处于待开售状态
            print(f"15点30分起售,现在是{time.strftime('%H:%M:%S', time.localtime())},还未开始售票")
            # 每隔两分钟刷新一次,否则3分钟内无购票操作12306系统会自动登出
            if time.time() - start >= 120:
                driver.refresh()
                start = time.time()
            # 延时1秒防止过于快速地点击导致查询超时,当然偶尔还是会出现超时现象,不过超时也没关系,一般等待6秒之后就会继续自动查询
            time.sleep(1)
            continue
 
        # 获取所有车票
        tickets = driver.find_elements(by=By.XPATH, value='//*[@id="queryLeftTable"]/tr')
        # 每张车票有两个tr,但是第二个tr没什么用
        tickets = [tickets[i] for i in range(len(tickets) - 1) if i % 2 == 0]
        #print(tickets)
        for ticket in tickets:
            # 如果车票的车次等于想要的车次并且硬卧的状态不是候补则点击预订
            #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 != "候补":
                # 点击预订
                #print(ticket.find_element(by=By.CLASS_NAME,value='cdz').text)
                #time.sleep(1)
                ticket.find_element(by=By.CLASS_NAME, value='btn72').click()
                # 这里之后就不能继续使用ticket.find_element()了,因为页面进行了跳转,会出现stale element reference: element is not attached to the page document的错误
                # 我们可以使用driver.find_element()
                # 选择乘车人,如果是学生,则需要确认购买学生票
                driver.find_element(by=By.XPATH, value='//*[@id="normalPassenger_0"]').click()
                # 点击确认购买学生票,如果不是学生,把这行注释了就行
                #driver.find_element(by=By.XPATH, value='//*[@id="dialog_xsertcj_ok"]').click()
                # 第二个乘车人
                # driver.find_element(by=By.XPATH, value='//*[@id="normalPassenger_1"]').click()
                # 如果第二个乘车人也是学生,则需要点击确认第二个人也购买学生票
                # 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()
                # 确认提交订单,然后这里和上面是一样的
                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}次列车抢票成功,请尽快在10分钟内支付!")
                return
 
 
if __name__ == '__main__':
    # 有关车票的配置信息保存在该类里
    # 请事先在config.py里填好相关信息
    conf = Config()
 
    url = 'https://www.12306.cn/index/'
 
    # chromedriver.exe版本为104,可以根据自己浏览器版本重新下载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()

If you have any questions, please leave a message~~~~

insert image description here

Guess you like

Origin blog.csdn.net/ch950401/article/details/132217238