Python与Selenium结合实现铁路12306自动化购票详解

一、背景与技术选型

12306 作为全球最大的实时票务系统,日均处理数千万次请求,其反爬机制也在不断升级。有这还怕平时回家买不到票?本文将介绍如何通过 Python 和 Selenium 实现自动化购票,重点突破以下技术难点:

  • 反检测配置(隐藏自动化特征)
  • 动态元素处理(显式等待与动态定位)
  • 多场景交互(登录、查询、预订)
  • 二、核心代码解析

    1. 反检测配置(关键反爬策略)

    options = Options()
    # 移除自动化标识
    options.add_experimental_option('excludeSwitches', ['enable-automation'])
    options.add_argument('--disable-blink-features=AutomationsControlled')
    # 关闭证书验证
    options.add_argument('ignore-certificate-errors')
    driver = webdriver.Chrome(options=options)
  • 技术原理:通过修改浏览器启动参数,移除navigator.webdriver属性等自动化特征,降低被 12306 识别的概率。
  • 2. 登录模块(用户输入与二次验证)

    def Longin():
        driver.get('https://kyfw.12306.cn/otn/resources/login.html')
        user = input("请输入用户名: ")
        pwd = getpass.getpass("请输入密码: ")  # 密码输入隐藏
        # 定位并输入用户名/密码
        nameuser = driver.find_element(By.XPATH, '//div/input[@id="J-userName"]')
        password = driver.find_element(By.XPATH, '//div/input[@id="J-password"]')
        nameuser.send_keys(user)
        password.send_keys(pwd)
        driver.find_element(By.XPATH, '//*[@id="J-login"]').click()
        # 二次验证(身份证后四位+短信验证码)
        code_id = input("请输入身份证后四位:")
        driver.find_element(By.XPATH, '//*[@id="verification_code"]').click()
        code = input("请输入验证码:")
        driver.find_element(By.XPATH, '//*[@id="code"]').send_keys(code)
        # 检查登录结果
        try:
            error_element = driver.find_element(By.XPATH, '//*[@id="message"]/p')
            if error_element.text == "用户名或密码错误":
                print("登录失败:请检查信息")
            else:
                print("登录成功")
                driver.find_element(By.XPATH, '//*[@id="link_for_ticket"]').click()
                Inquirer()
        except Exception as e:
            print(f"未找到错误提示:{e}")
  • 安全设计:使用getpass模块隐藏密码输入,避免敏感信息泄露。
  • 验证流程:支持身份证后四位 + 短信验证码的二次验证,符合 12306 最新登录逻辑。
  • 运行结果展示
  • 请输入用户名: 13800138000
    请输入密码: ········  # 输入时密码不可见
    请输入身份证后四位:1234
    验证码正在发送中,请耐心等待
    请输入验证码:56789
    登录成功

  • 3. 查询模块(日期处理与车次提取)

    def Inquirer():
        chufadi = input("请输入出发地:")
        mudidi = input("请输入目的地:")
        # 日期处理(默认当天,支持格式验证)
        current_date = datetime.now().strftime("%Y-%m-%d")
        user_input = input("输入出发日期(格式: YYYY-MM-DD,留空默认今日):")
        departure_date = current_date if not user_input else datetime.strptime(user_input, "%Y-%m-%d").strftime("%Y-%m-%d")
        # 更新日期输入框
        if departure_date != current_date:
            date_input = driver.find_element(By.XPATH, '//*[@id="train_date"]')
            date_input.clear()
            date_input.send_keys(departure_date)
        # 点击查询
        driver.find_element(By.XPATH, '//*[@id="query_ticket"]').click()
        # 提取车次信息(存在索引越界风险,需优化)
        list_Train = []
        for trin in range(20):
            try:
                dizhi = driver.find_element(By.XPATH, f'//*[@id="train_num_{trin}"]/div/strong')
                list_Train.append(dizhi.text)
                print(f"车次: {list_Train[trin]} | 出发地: {list_Train[trin+1]} | 到达时间: {list_Train[trin+2]}")
            except:
                break
  • 日期验证:使用datetime.strptime进行格式校验,确保输入合法。
  • 车次提取:通过循环遍历车次列表,动态获取车次信息(需注意索引越界问题)
  • 运行结果
  • 请输入出发地:北京
    请输入目的地:上海
    输入出发日期(格式: YYYY-MM-DD,留空默认今日):2025-05-20
    正在查询2025-05-20的车票,请稍后...
    
                    出发地                   目的地                   到达时间 
            G1    北京南       上海虹桥       08:00
    
                    出发地                   目的地                   到达时间 
            G3    北京南       上海虹桥       09:00
    
                    出发地                   目的地                   到达时间 
            G5    北京南       上海虹桥       10:00

  • 4. 预订模块(时间匹配与订单提交)

    def Get_ticket():
        # 提取所有车次的出发时间
        time_list = [driver.find_element(By.XPATH, f'//*[@id="train_num_{i}"]/div[3]/strong[1]').text 
                     for i in range(int(driver.find_element(By.XPATH, '//*[@id="trainum"]').text))]
        move_time = input("选择出发时间段(1/2/3):")
        wait = WebDriverWait(driver, 10)
        if move_time == '1':
            driver.find_element(By.XPATH, '//*[@id="cc_start_time"]/option[3]').click()
            accurate_time = input("输入目标时间(如12:00):")
            closest = get_closest_time(accurate_time, time_list)
            # 显式等待确保预订按钮可见
            wait.until(EC.visibility_of_element_located((By.XPATH, f'//a[@class="btn72" and contains(text(), "{closest}")]')))
            driver.find_element(By.XPATH, f'//a[@class="btn72" and contains(@onclick, "{closest}")]').click()
            # 提交订单
            driver.find_element(By.XPATH, '//*[@id="normalPassenger_0"]').click()  # 选择乘客
            driver.find_element(By.XPATH, '//*[@id="submitOrder_id"]').click()  # 提交订单
  • 时间匹配:使用get_closest_time函数计算最接近用户指定时间的车次,提升购票精准度。
  • 显式等待:通过WebDriverWait确保预订按钮加载完成,避免元素未找到错误。
  • 运行结果展示
  • 请输入出发时间段,
    1: 06:00--12:00
    2: 12:00--18:00
    3: 18:00--24:001
    输入准确时间,将会自动购买靠近该时间的车票(12:00)09:30
    最接近 09:30 的时间是: 09:00
    所有时间为 ['08:00', '09:00', '10:00']

  • 三、代码优化与最佳实践

    1. 反检测增强(应对 12306 最新策略)

    # 通过CDP命令修改navigator.webdriver属性
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
        "source": """
        Object.defineProperty(navigator, 'webdriver', {
          get: () => undefined
        })
        """
    })
  • 技术原理:直接修改浏览器环境,隐藏navigator.webdriver属性,绕过 12306 的自动化检测。
  • 2. 动态元素处理(避免固定索引)

    # 优化车次信息提取(使用动态定位)
    trains = driver.find_elements(By.XPATH, '//div[contains(@id, "train_num_")]')
    for train in trains:
        try:
            info = [elem.text for elem in train.find_elements(By.XPATH, './div/strong')]
            print(f"车次: {info[0]} | 出发地: {info[1]} | 到达时间: {info[2]}")
        except IndexError:
            continue
  • 优化点:使用动态元素定位,避免固定索引导致的越界错误。
  • 3. 异常处理(提升稳定性)

    # 添加异常捕获与重试机制
    from selenium.common.exceptions import NoSuchElementException
    
    def safe_click(by, value, timeout=10):
        try:
            wait = WebDriverWait(driver, timeout)
            element = wait.until(EC.element_to_be_clickable((by, value)))
            element.click()
        except NoSuchElementException:
            print(f"元素 {value} 未找到")
        except TimeoutException:
            print(f"等待元素 {value} 超时")
  • 作用:封装点击操作,处理元素未找到或超时异常,提高脚本鲁棒性。
  • 四、风险提示与合规建议
    1. 法律风险

    2. 自动化抢票可能违反 12306 用户协议,甚至涉嫌非法经营罪。
    3. 建议仅用于学习用途,避免用于实际购票。
    4. 账号安全

    5. 频繁自动化操作可能导致账号被封禁。
    6. 避免在代码中存储敏感信息,如账号密码。
    7. 技术对抗

    8. 12306 已推出防抢票专利,如 SVG 验证码、行为验证等。
    9. 脚本需持续更新以应对反爬策略变化。
    五、总结与扩展

    本文介绍了基于 Selenium 的 12306 自动化购票实现,涵盖反检测配置、登录验证、车次查询、时间匹配等核心功能。建议结合以下方向进一步优化:

    1. 多线程与分布式:使用多线程提高查询效率,结合代理 IP 避免 IP 封禁。
    2. 图像识别:集成 OCR 技术自动识别验证码,降低人工干预。
    3. 候补购票:模拟 12306 官方候补逻辑,提升购票成功率。

    六、总体代码展示(Al优化后)

    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.chrome.options import Options
    import time
    from datetime import datetime
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    import getpass
    
    # ----------------------
    # 浏览器反检测配置
    # ----------------------
    def create_chrome_options():
        options = Options()
        # 隐藏自动化特征
        options.add_experimental_option('excludeSwitches', ['enable-automation'])
        options.add_experimental_option('useAutomationExtension', False)
        options.add_argument('--disable-blink-features=AutomationsControlled')
        # 伪装用户代理(可替换为真实浏览器UA)
        options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36')
        # 其他实用配置
        options.add_argument('ignore-certificate-errors')
        options.add_argument('--disable-gpu')
        options.add_argument('--no-sandbox')
        options.add_argument('--headless')  #无头模式 注释这句 可以展示出购票 过程
        #不注释,大大缩短,购票时间
        return options
    
    # ----------------------
    # 登录模块
    # ----------------------
    def login(driver):
        try:
            driver.get('https://kyfw.12306.cn/otn/resources/login.html')
            wait = WebDriverWait(driver, 10)
            
            # 输入用户名和密码
            user = input("请输入用户名: ")
            pwd = getpass.getpass("请输入密码: ")
            
            # 定位并输入用户名
            username_input = wait.until(EC.presence_of_element_located((By.XPATH, '//div/input[@id="J-userName"]')))
            username_input.send_keys(user)
            
            # 定位并输入密码
            password_input = wait.until(EC.presence_of_element_located((By.XPATH, '//div/input[@id="J-password"]')))
            password_input.send_keys(pwd)
            
            # 点击登录按钮
            login_button = wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="J-login"]')))
            login_button.click()
            
            # 二次验证:身份证后四位
            code_id = input("请输入身份证后四位: ")
            id_input = wait.until(EC.presence_of_element_located((By.XPATH, '/html/body/div[1]/div[4]/div[2]/div[1]/div/div[1]/input')))
            id_input.send_keys(code_id)
            
            # 点击获取短信验证码
            get_code_button = wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="verification_code"]')))
            get_code_button.click()
            time.sleep(2)
            
            # 输入短信验证码
            code = input("请输入短信验证码: ")
            code_input = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="code"]')))
            code_input.send_keys(code)
            
            # 检查登录结果
            try:
                error_msg = wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="message"]/p'))).text
                if "用户名或密码错误" in error_msg:
                    print("登录失败:用户名、密码或验证码错误")
                    return False
            except:
                print("登录成功!")
                # 跳转到车票查询页面
                ticket_link = wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="link_for_ticket"]')))
                ticket_link.click()
                return True
        except Exception as e:
            print(f"登录过程中出现错误:{e}")
            return False
    
    # ----------------------
    # 车次查询模块
    # ----------------------
    def search_tickets(driver):
        try:
            wait = WebDriverWait(driver, 10)
            # 输入出发地和目的地
            from_station = input("请输入出发地: ")
            to_station = input("请输入目的地: ")
            
            # 定位出发地输入框
            from_input = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="fromStationText"]')))
            from_input.clear()
            from_input.send_keys(from_station)
            
            # 定位目的地输入框
            to_input = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="toStationText"]')))
            to_input.clear()
            to_input.send_keys(to_station)
            
            # 处理出发日期
            current_date = datetime.now().strftime("%Y-%m-%d")
            user_date = input("输入出发日期(格式: YYYY-MM-DD,留空默认今日): ")
            departure_date = current_date if not user_date else user_date
            
            # 更新日期输入框
            if departure_date != current_date:
                date_input = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="train_date"]')))
                date_input.clear()
                date_input.send_keys(departure_date)
            
            # 点击查询按钮
            search_button = wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="query_ticket"]')))
            search_button.click()
            print(f"正在查询{departure_date}从{from_station}到{to_station}的车票...")
            time.sleep(3)  # 等待查询结果加载
            return departure_date
        except Exception as e:
            print(f"查询车次时出现错误:{e}")
            return None
    
    # ----------------------
    # 车票预订模块
    # ----------------------
    def book_ticket(driver, departure_date):
        try:
            wait = WebDriverWait(driver, 10)
            # 提取所有车次时间
            time_list = []
            train_elements = wait.until(EC.presence_of_all_elements_located((By.XPATH, '//div[contains(@id, "train_num_")]')))
            for i, train in enumerate(train_elements):
                try:
                    time_text = train.find_element(By.XPATH, './div[3]/strong[1]').text
                    time_list.append(time_text)
                except:
                    continue
            
            if not time_list:
                print("未找到可用车次!")
                return
            
            # 用户选择时间段
            move_time = input("请选择出发时间段(1:06:00-12:00,2:12:00-18:00,3:18:00-24:00): ")
            time_ranges = {
                '1': ('06:00--12:00', 3),
                '2': ('12:00--18:00', 4),
                '3': ('18:00--24:00', 5)
            }
            if move_time not in time_ranges:
                print("无效选择!")
                return
            
            # 选择时间段
            range_name, option_index = time_ranges[move_time]
            time_option = wait.until(EC.element_to_be_clickable((By.XPATH, f'//*[@id="cc_start_time"]/option[{option_index}]')))
            time_option.click()
            print(f"已筛选{range_name}的车次")
            
            # 输入目标时间并匹配最接近车次
            target_time = input("请输入目标时间(格式: HH:MM): ")
            closest_time = get_closest_time(target_time, time_list)
            print(f"最接近的车次时间:{closest_time}")
            
            # 等待预订按钮加载并点击
            book_button = wait.until(EC.element_to_be_clickable((By.XPATH, f'//a[@class="btn72" and contains(text(), "{closest_time}")]')))
            book_button.click()
            time.sleep(2)
            
            # 选择乘客(默认第一个乘客,需根据实际情况调整)
            passenger = wait.until(EC.element_to_be_clickable((By.XPATH, '//ul[@id="normal_passenger_id"]/li[1]/input')))
            passenger.click()
            
            # 提交订单(需手动处理可能的验证码或确认弹窗)
            submit_button = wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="submitOrder_id"]')))
            submit_button.click()
            print("已提交订单,请尽快完成支付!")
        except Exception as e:
            print(f"预订车票时出现错误:{e}")
    
    # ----------------------
    # 时间匹配函数
    # ----------------------
    def get_closest_time(user_time, time_list):
        user_min = int(user_time[:2]) * 60 + int(user_time[3:])
        time_diffs = [(abs(int(t[:2])*60 + int(t[3:]) - user_min), t) for t in time_list]
        return min(time_diffs, key=lambda x: x[0])[1]
    
    # ----------------------
    # 主程序入口
    # ----------------------
    if __name__ == "__main__":
        options = create_chrome_options()
        driver = webdriver.Chrome(options=options)
        
        if login(driver):
            departure_date = search_tickets(driver)
            if departure_date:
                book_ticket(driver, departure_date)
        
        input("按回车键退出程序...")
        driver.quit()

    作者原创

    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.chrome.options import Options  # 导入Options操作类
    import time
    from datetime import datetime
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    import getpass
    
    # 获取当前日期(格式:YYYY-MM-DD)
    current_date = datetime.now().strftime("%Y-%m-%d")
    
    options = Options()
    options.add_argument('--headless')  #无头模式
    # 设置自动化特性扩展的关闭,防止被服务器检测到是由selenium驱动的
    options.add_experimental_option('excludeSwitches', ['enable-automation'])
    
    # 设置关闭自动化特性,防止被服务器检测到是由selenium驱动的
    options.add_argument('--disable-blink-features=AutomationsControlled')
    
    # 关闭证书报错
    options.add_argument('ignore-certificate-errors')
    # 启动 WebDriver(假设你使用的是 Chrome)  如果这里不进行传参数 就是有界面模式
    driver = webdriver.Chrome(options = options)
    # driver = webdriver.Chrome()
    
    
    time.sleep(1)
    
    
    def Longin():
        try:
            # 打开目标网页
            driver.get('https://kyfw.12306.cn/otn/resources/login.html')
    
            # 获取用户输入的账号和密码
            user = input("请输入用户名: ")
            # pwd = input("请输入密码: ")
            pwd = getpass.getpass("请输入密码: ")  # 输入时密码不会显示在屏幕上
    
            # 使用 XPath 定位用户名输入框并输入
            nameuser = driver.find_element(By.XPATH, '//div/input[@id="J-userName"]')
            time.sleep(1)
            nameuser.send_keys(user)
    
            # 定位密码输入框并输入
            password = driver.find_element(By.XPATH, '//div/input[@id="J-password"]')
            time.sleep(1)
            password.send_keys(pwd)
    
            # 登录按钮点击
            driver.find_element(By.XPATH, '//*[@id="J-login"]').click()
    
            # 后续代码保持不变
            code_id = input("请输入身份证后四位:")
            id_cord = driver.find_element(By.XPATH, '/html/body/div[1]/div[4]/div[2]/div[1]/div/div[1]/input')
            id_cord.send_keys(code_id)
            print("验证码正在发送中,请耐心等待")
            time.sleep(1)
            driver.find_element(By.XPATH, '//*[@id="verification_code"]').click()
            time.sleep(1)
            id_code = driver.find_element(By.XPATH, '//*[@id="code"]')
            code = input("请输入验证码:")
            id_code.send_keys(f"{code}")
    
            try:
                error_element = driver.find_element(By.XPATH, '//*[@id="message"]/p')
                if error_element.text == "用户名或密码错误":
                    print("密码,用户名,验证码错误,请检查")
                else:
                    print("登录成功")
                    time.sleep(2)
                    driver.find_element(By.XPATH, '//*[@id="link_for_ticket"]').click()
                    time.sleep(2)
                    driver.find_element(By.XPATH, '//*[@id="sureClick"]')
                    Inquirer()
            except Exception as e:
                print(f"未找到错误提示元素: {e}")
    
        except  Exception as e:
            print("程序运行发生错误", e)
    
    def Inquirer():
        try:
            chufadi = input("请输入出发地:")
            # 获取出发地输入框
            from_station_input = driver.find_element(By.XPATH, '//*[@id="fromStationText"]')
            # 清空输入框
            from_station_input.clear()
            # 输入出发地
            from_station_input.send_keys(chufadi)
    
            mudidi = input("请输入目的地:")
            end_station_input = driver.find_element(By.XPATH, '//*[@id="toStationText"]')
            end_station_input.clear()
            end_station_input.send_keys(mudidi)
            from datetime import datetime, timedelta
    
            try:
                # 获取当前日期
                current_date = datetime.now().strftime("%Y-%m-%d")
    
                # 获取用户输入
                user_input = input("不输入默认获取当日,回车跳过输入,输入出发时间格式(2025-5-20):")
    
                # 处理用户输入
                if not user_input.strip():  # 输入为空
                    departure_date = current_date
                    print(f"未输入日期,将查询今日({departure_date})车票")
                else:
                    try:
                        # 验证并格式化日期
                        parsed_date = datetime.strptime(user_input, "%Y-%m-%d")
                        departure_date = parsed_date.strftime("%Y-%m-%d")
                    except ValueError:
                        print("警告:输入日期格式不正确,将使用默认日期")
                        departure_date = current_date
    
                # 如果不是查询当日,需要更新日期输入框
                if departure_date != current_date:
                    date_input = driver.find_element(By.XPATH, '//*[@id="train_date"]')
                    date_input.clear()
                    date_input.send_keys(departure_date)
    
                # 点击查询按钮(合并重复逻辑)
                query_button = driver.find_element(By.XPATH, '//*[@id="query_ticket"]')
                query_button.click()
                print(f"正在查询{departure_date}的车票,请稍后...")
    
                list_Train = []
                for trin in range(0, 20):
                    dizhi = driver.find_element(By.XPATH, f'//*[@id="train_num_{trin}"]/div/strong')
                    time.sleep(1)
                    list_Train.append(dizhi.text)
                    print(f"""
                    出发地                   目的地                   到达时间 
            {list_Train[trin]}    {list_Train[trin+1]}       {list_Train[trin+2]}
                    """)
    
            except Exception as e:
                print(f"程序运行发生错误,在输入日期 {e}")
    
        except Exception as e:
            print("程序运行发生错误,在输入地点", e)
    
    #下面这个会确定元素加载完才会进行下一步
    
    def Get_ticket():
        time_list = []
        chechi_number = driver.find_element(By.XPATH, '//*[@id="trainum"]')
        chechi_number = int(chechi_number.text)
        for i in range(chechi_number):
            time_list.append(driver.find_element(By.XPATH, f'//*[@id="train_num_{i}"]/div[3]/strong[1]').text)
    
        move_time = input("请输入出发时间段,\n1: 06:00--12:00\n2: 12:00--18:00\n3: 18:00--24:00")
    
        # 创建显式等待对象
        wait = WebDriverWait(driver, 10)
    
        if move_time == '1':
            """
            //*[@id="cc_start_time"]/option[*]  选着时间段 
            #这个是通过固定时间选定这个预订标签  onclick 这个下面的时间 选着这个标签 
            //a[@class="btn72" and contains(@onclick, '21:02')]
            """
            driver.find_element(By.XPATH, '//*[@id="cc_start_time"]/option[3]').click()
            accurate_time = input("输入准确时间,将会自动购买靠近该时间的车票(12:00)")
            closest = get_closest_time(accurate_time, time_list)
            print(f"最接近 {accurate_time} 的时间是: {closest}")
            print('所有时间为', str(time_list))
    
            # 添加显式等待
            wait.until(
                EC.visibility_of_element_located((By.XPATH, f'//a[@class="btn72" and contains(text(), "{closest}")]')))
    
            # 点击预定按钮
            driver.find_element(By.XPATH, f'//a[@class="btn72" and contains(@onclick, "{closest}")]').click()
            time.sleep(1)
            driver.find_element(By.XPATH, '//*[@id="normalPassenger_0"]').click()  # 点击坐车人按钮
            driver.find_element(By.XPATH, '//*[@id="dialog_xsertcj_cancel"]').click()  # 点击取消按钮
            driver.find_element(By.XPATH, '//*[@id="submitOrder_id"]').click()  # 点击提交订单按钮
    
        if move_time == '2':
            driver.find_element(By.XPATH, '//*[@id="cc_start_time"]/option[4]').click()
            accurate_time = input("输入准确时间,将会自动购买靠近该时间的车票(12:00)")
            closest = get_closest_time(accurate_time, time_list)
            print(f"最接近 {accurate_time} 的时间是: {closest}")
            print('所有时间为', str(time_list))
    
            # 添加显式等待
            wait.until(
                EC.visibility_of_element_located((By.XPATH, f'//a[@class="btn72" and contains(text(), "{closest}")]')))
    
            # 点击预定按钮
            driver.find_element(By.XPATH, f'//a[@class="btn72" and contains(@onclick, "{closest}")]').click()
            time.sleep(1)
            driver.find_element(By.XPATH, '//*[@id="normalPassenger_0"]').click()  # 点击坐车人按钮
            driver.find_element(By.XPATH, '//*[@id="dialog_xsertcj_cancel"]').click()  # 点击取消按钮
            driver.find_element(By.XPATH, '//*[@id="submitOrder_id"]').click()  # 点击提交订单按钮
    
        if move_time == '3':
            driver.find_element(By.XPATH, '//*[@id="cc_start_time"]/option[5]').click()
            accurate_time = input("输入准确时间,将会自动购买靠近该时间的车票(12:00)")
            closest = get_closest_time(accurate_time, time_list)
            print(f"最接近 {accurate_time} 的时间是: {closest}")
            print('所有时间为', str(time_list))
    
            # 添加显式等待
            wait.until(
                EC.visibility_of_element_located((By.XPATH, f'//a[@class="btn72" and contains(text(), "{closest}")]')))
    
            # 点击预定按钮
            driver.find_element(By.XPATH, f'//a[@class="btn72" and contains(@onclick, "{closest}")]').click()
            time.sleep(1)
            driver.find_element(By.XPATH, '//*[@id="normalPassenger_0"]').click()  # 点击坐车人按钮
            driver.find_element(By.XPATH, '//*[@id="dialog_xsertcj_cancel"]').click()  # 点击取消按钮
            driver.find_element(By.XPATH, '//*[@id="submitOrder_id"]').click()  # 点击提交订单按钮
    
    def get_closest_time(user_time, time_list):
        user_minutes = int(user_time[:2]) * 60 + int(user_time[3:])
        time_diffs = [(abs(int(t[:2]) * 60 + int(t[3:]) - user_minutes), t) for t in time_list]
        return min(time_diffs)[1]
    
    
    if __name__ == '__main__':
        Longin()
    
    # 等待一段时间以观察结果
    # time.sleep()
    input("回车结束")
    # 关闭浏览器
    driver.quit()
    
    
    

    作者:智极Hub

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python与Selenium结合实现铁路12306自动化购票详解

    发表回复