python+selenium实现
import datetime
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
import time
options = Options()
options.add_argument("--headless") # 无界面运行
options.add_argument("--disable-gpu") # 禁用GPU加速
# 初始化浏览器
wd = webdriver.Chrome()
wd.minimize_window() # 启动后立即最小化窗口
wait = WebDriverWait(wd, 20)
actions = ActionChains(wd)
def input_with_enter(element, text):
"""带回车确认的输入方法"""
element.clear()
time.sleep(0.3)
for char in text:
element.send_keys(char)
time.sleep(0.1)
element.send_keys(Keys.ENTER) # 关键回车确认
time.sleep(0.5)
def set_station_auto(field_id, prompt):
"""自动化设置车站输入(带自动回车确认)"""
while True:
try:
# 获取输入框
input_field = wait.until(EC.element_to_be_clickable((By.ID, field_id)))
# 清除原有内容
input_field.click()
wd.execute_script(f"document.getElementById('{field_id}').value='';")
time.sleep(0.5)
# 获取用户输入
city_name = input(prompt)
if not city_name:
continue
# 输入并回车确认
input_with_enter(input_field, city_name)
# 检查是否成功选择
if input_field.get_attribute("value"):
print(f"✅ 已设置: {input_field.get_attribute('value')}")
return True
except Exception as e:
print(f"❌ 设置失败: {str(e)}")
wd.save_screenshot(f"error_{field_id}.png")
def switch_to_new_window():
"""切换到新打开的窗口"""
original_window = wd.current_window_handle
# 等待新窗口出现
WebDriverWait(wd, 10).until(lambda d: len(d.window_handles) > 1)
# 切换到新窗口
for window_handle in wd.window_handles:
if window_handle != original_window:
wd.switch_to.window(window_handle)
break
print(f"已切换到新窗口,当前URL: {wd.current_url}")
return True
def print_ticket_info():
"""打印车票信息"""
print("\n=== 可购车次信息 ===")
try:
# 等待结果表格加载
wait.until(EC.presence_of_element_located((By.ID, "t-list")))
# 获取所有车次行
train_list = wait.until(EC.presence_of_all_elements_located(
(By.CSS_SELECTOR, "#queryLeftTable tr[id^='ticket_']")))
trains = []
for i, train in enumerate(train_list, 1):
try:
number = train.find_element(By.CLASS_NAME, "number").text
start_time = train.find_element(By.CLASS_NAME, "start-t").text
arrive_time = train.find_element(By.CLASS_NAME, "color999").text
duration = train.find_element(By.CLASS_NAME, "ls").text
seat_types = train.find_elements(By.CSS_SELECTOR, ".seat[class^='seat_']")
seat_info = []
for seat in seat_types:
seat_name = seat.get_attribute("textContent").strip()
seat_count = seat.find_element(By.CLASS_NAME, "seat_surplus").text
seat_info.append(f"{seat_name}:{seat_count}")
train_info = {
"index": i,
"number": number,
"departure": start_time,
"arrival": arrive_time,
"duration": duration,
"seats": " | ".join(seat_info)
}
trains.append(train_info)
print(
f"{i}. 车次: {number} | 出发: {start_time} | 到达: {arrive_time} | 历时: {duration} | 余票: {' | '.join(seat_info)}")
except Exception as e:
print(f"解析车次信息时出错: {str(e)}")
continue
return trains
except Exception as e:
print(f"获取车票信息失败: {str(e)}")
wd.save_screenshot("ticket_info_error.png")
return None
def select_and_book(train_list):
"""选择车次并购票"""
while True:
choice = input("\n请输入要购买的车次编号(1-{}), 或输入q退出: ".format(len(train_list)))
if choice.lower() == 'q':
return False
try:
index = int(choice) - 1
if 0 <= index < len(train_list):
selected_train = train_list[index]
print(f"\n您选择了: {selected_train['number']}")
# 获取当前窗口句柄
original_window = wd.current_window_handle
# 点击预订按钮
book_btn = WebDriverWait(wd, 10).until(
EC.element_to_be_clickable((By.XPATH, "//*[@id='ticket_5500000G1401_01_05']/td[13]/a")))
book_btn.click()
print("正在跳转到预订页面...")
# 处理可能的新窗口/新页面
try:
# 尝试切换到新窗口
switch_to_new_window()
except:
# 如果没新窗口,则等待当前页面刷新
wait.until(EC.url_changes(wd.current_url))
print(f"页面已跳转,当前URL: {wd.current_url}")
return True
else:
print("请输入有效的编号!")
except ValueError:
print("请输入数字!")
def handle_passenger_selection():
"""处理乘客选择"""
try:
# 等待乘客选择页面加载
wait.until(EC.presence_of_element_located((By.ID, "normal_passenger_id")))
# 选择第一个乘客
first_passenger = wait.until(EC.element_to_be_clickable(
(By.CSS_SELECTOR, "#normal_passenger_id input[name^='normalPassenger']")))
first_passenger.click()
print("✅ 已选择乘客")
# 点击提交订单
submit_btn = wait.until(EC.element_to_be_clickable((By.ID, "submitOrder_id")))
submit_btn.click()
print("✅ 已提交订单")
# 确认订单
confirm_btn = wait.until(EC.element_to_be_clickable((By.ID, "qr_submit_id")))
confirm_btn.click()
print("🎉 购票成功!请尽快支付!")
return True
except Exception as e:
print(f"购票过程中出错: {str(e)}")
wd.save_screenshot("book_error.png")
return False
try:
# 访问首页
wd.get("https://www.12306.cn/index/index.html")
print("✅ 已打开12306首页")
# 设置出发地
set_station_auto("fromStationText", "请输入出发地(如'北京')然后按回车: ")
# 设置目的地
set_station_auto("toStationText", "请输入目的地(如'上海')然后按回车: ")
# 设置日期
date_input = wait.until(EC.element_to_be_clickable((By.ID, "train_date")))
travel_date = input("请输入日期(YYYY-MM-DD)然后按回车: ")
input_with_enter(date_input, travel_date)
print(f"📅 已设置日期: {date_input.get_attribute('value')}")
# 点击查询
search_btn = wait.until(EC.element_to_be_clickable((By.ID, "search_one")))
search_btn.click()
print("🔍 正在查询车票...")
# 处理可能的新页面/新窗口
try:
switch_to_new_window()
except:
print("没有新窗口,继续在当前窗口操作")
# 等待结果并打印车票信息
train_list = print_ticket_info()
# 选择车次并购票
if train_list and select_and_book(train_list):
# 处理乘客选择和订单提交
handle_passenger_selection()
except Exception as e:
print(f"❗ 发生错误: {str(e)}")
wd.save_screenshot("main_error.png")
finally:
input("按回车键退出...")
wd.quit()