<think>我们正在使用Selenium实现12306抢票自动化。根据引用内容,我们已经掌握了Selenium的基本操作,包括处理验证码、跳转网页、处理内嵌框架等。现在,我们将按照步骤来实现这个功能。主要步骤包括:1.准备工作:安装必要的包和浏览器驱动。2.访问12306网站,登录账户。3.处理登录过程中的验证码(12306的验证码比较复杂,可能需要人工干预或使用第三方验证码识别服务)。4.填写出发地、目的地、日期等信息并查询车票。5.选择车次和座位类型,添加乘客。6.提交订单,确认并支付(支付环节通常需要人工操作,因为涉及银行交互)。由于12306网站结构可能会变化,以下代码可能需要调整。###代码实现步骤####1.准备工作安装必要的包:```bashpipinstallseleniuminterval-ihttps://pypi.tuna.tsinghua.edu.cn/simple```下载对应浏览器的驱动(如Chrome的chromedriver),并确保驱动所在的目录在系统PATH中,或者在代码中指定驱动路径。####2.初始化WebDriver```pythonfromseleniumimportwebdriverfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.supportimportexpected_conditionsasECimporttime#指定chromedriver路径,如果已经加入环境变量则不需要driver=webdriver.Chrome(executable_path='chromedriver路径')driver.maximize_window()#最大化窗口```####3.访问12306登录页面```pythondriver.get("https://kyfw.12306.cn/otn/login/init")```####4.登录由于12306登录需要验证码,这里我们可能需要人工输入验证码,或者尝试使用第三方验证码识别服务(但12306验证码识别难度较大,通常需要人工)。等待用户输入用户名、密码和验证码,然后点击登录按钮。```python#等待用户输入username=driver.find_element(By.ID,"username")password=driver.find_element(By.ID,"password")#假设用户手动输入,我们等待一段时间time.sleep(20)#等待20秒,让用户输入#点击登录按钮driver.find_element(By.ID,"loginSub").click()```####5.跳转到车票预订页面登录成功后,我们跳转到车票预订页面。```python#等待登录成功,然后点击“车票预订”标签WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.LINK_TEXT,"车票预订"))).click()```####6.填写查询条件在车票预订页面,填写出发地、目的地、日期等信息。```python#出发地from_station=driver.find_element(By.ID,"fromStationText")from_station.click()from_station.send_keys("北京")#示例#选择下拉框中的第一个匹配项(实际中需要更精确的选择)WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.XPATH,"//ul[@id='ul_list1']/li[1]"))).click()#目的地to_station=driver.find_element(By.ID,"toStationText")to_station.click()to_station.send_keys("上海")WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.XPATH,"//ul[@id='ul_list1']/li[1]"))).click()#日期选择,这里选择今天后的第二天(示例)#注意:12306日期输入框是只读的,需要通过js来赋值date_input=driver.find_element(By.ID,"train_date")driver.execute_script("arguments[0].removeAttribute('readonly')",date_input)date_input.clear()date_input.send_keys("2023-12-31")#替换为需要的日期#点击查询按钮driver.find_element(By.ID,"query_ticket").click()```####7.选择车次和座位查询后,在车次列表中选择需要的车次和座位类型。```python#等待查询结果出现WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH,"//tbody[@id='queryLeftTable']/tr")))#选择第一个车次的二等座(根据实际情况调整)#注意:车次列表中的每一行可能有多个车次信息,需要根据具体结构定位#这里假设每一行是一个车次,二等座对应的按钮是第8个(索引从0开始,实际需要查看网页结构)#注意:有些车次可能没有二等座,所以需要判断tr_list=driver.find_elements(By.XPATH,"//tbody[@id='queryLeftTable']/tr[not(@datatran)]")fortrintr_list:#获取车次号,假设在td[0]的位置train_no=tr.find_element(By.XPATH,".//td[1]/div/div").textiftrain_no=='G123':#假设我们要选择G123次#二等座按钮在td[8]的位置,class为ticket-seat,且为可点击的a标签seat_btn=tr.find_element(By.XPATH,".//td[8]/a")ifseat_btn.text=='有'orseat_btn.text.isdigit():#有票seat_btn.click()break```####8.选择乘客并提交在接下来的页面中选择乘客,并提交订单。```python#等待乘客选择页面加载WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.ID,"normalPassenger_0"))).click()#选择第一个乘客#提交订单driver.find_element(By.ID,"submitOrder_id").click()#确认订单WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.ID,"qr_submit_id"))).click()```####9.支付支付环节通常需要人工操作,因为需要输入银行支付密码等。###注意事项1.12306网站经常更新,元素ID或结构可能会变化,需要及时调整。2.验证码处理是一个难点,上述代码中采用人工输入的方式,如果需要自动识别,可能需要接入打码平台(但存在风险)。3.在抢票高峰期,车票可能很快售罄,所以需要不断查询(刷新)直到有票。4.需要处理各种异常情况,如网络延迟、元素未加载等。###完整代码示例请注意,以下代码仅为示例,实际运行时需要根据当前网页结构调整。```pythonfromseleniumimportwebdriverfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.supportimportexpected_conditionsasECimporttime#初始化driver=webdriver.Chrome(executable_path='你的chromedriver路径')driver.maximize_window()try:#登录driver.get("https://kyfw.12306.cn/otn/login/init")time.sleep(20)#等待用户输入用户名、密码和验证码#跳转到车票预订WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.LINK_TEXT,"车票预订"))).click()#填写查询条件#出发地from_station=driver.find_element(By.ID,"fromStationText")from_station.click()from_station.send_keys("北京")WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.XPATH,"//ul[@id='ul_list1']/li[1]"))).click()#目的地to_station=driver.find_element(By.ID,"toStationText")to_station.click()to_station.send_keys("上海")WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.XPATH,"//ul[@id='ul_list1']/li[1]"))).click()#日期date_input=driver.find_element(By.ID,"train_date")driver.execute_script("arguments[0].removeAttribute('readonly')",date_input)date_input.clear()date_input.send_keys("2023-12-31")#查询driver.find_element(By.ID,"query_ticket").click()#选择车次WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH,"//tbody[@id='queryLeftTable']/tr")))tr_list=driver.find_elements(By.XPATH,"//tbody[@id='queryLeftTable']/tr[not(@datatran)]")fortrintr_list:train_no=tr.find_element(By.XPATH,".//td[1]/div/div").textiftrain_no=='G123':seat_btn=tr.find_element(By.XPATH,".//td[8]/a")ifseat_btn.textin['有','无']orseat_btn.text.isdigit():ifseat_btn.text!='无'andnotseat_btn.text.isdigit()orint(seat_btn.text)>0:seat_btn.click()break#选择乘客WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.ID,"normalPassenger_0"))).click()driver.find_element(By.ID,"submitOrder_id").click()#确认WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.ID,"qr_submit_id"))).click()#等待支付print("请手动完成支付")time.sleep(60)finally:driver.quit()```###相关问题1.如何处理12306的验证码?2.如何提高Selenium自动化脚本的稳定性?3.12306抢票过程中有哪些常见的异常,如何处理?4.如何实现自动监控余票并自动刷新查询?</think>### 使用Selenium实现12306抢票自动化的完整指南
#### 一、准备工作
1. **安装依赖包**:
```bash
pip install selenium interval requests pillow -i https://pypi.tuna.tsinghua.edu.cn/simple
```
2. **浏览器驱动配置**:
- 下载与浏览器版本匹配的[chromedriver](https://chromedriver.chromium.org/)
- 将驱动放入系统PATH或指定路径
#### 二、核心实现步骤
```python
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
import time
# 初始化浏览器
driver = webdriver.Chrome(executable_path='chromedriver.exe')
driver.get("https://kyfw.12306.cn/otn/login/init")
# 1. 登录处理
def login(username, password):
driver.find_element(By.ID, "username").send_keys(username)
driver.find_element(By.ID, "password").send_keys(password)
# 验证码处理(需人工识别或接入打码平台)
time.sleep(15) # 预留手动操作时间
driver.find_element(By.ID, "loginSub").click()
WebDriverWait(driver, 10).until(EC.title_contains("我的12306"))
# 2. 车票查询
def search_ticket(from_station, to_station, date):
driver.find_element(By.LINK_TEXT, "车票预订").click()
# 输入查询条件
driver.find_element(By.ID, "fromStationText").click()
driver.find_element(By.ID, "fromStationText").send_keys(from_station)
driver.find_element(By.ID, "toStationText").send_keys(to_station)
# 日期处理(移除只读属性)
date_elem = driver.find_element(By.ID, "train_date")
driver.execute_script("arguments[0].removeAttribute('readonly')", date_elem)
date_elem.clear()
date_elem.send_keys(date)
driver.find_element(By.ID, "query_ticket").click()
# 3. 车次选择与下单
def select_train(train_no, passenger_name):
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, f"//a[contains(text(),'{train_no}')]"))
)
# 选择二等座(根据实际车次调整)
driver.find_element(By.XPATH, f"//tr[@datatran='{train_no}']/td[last()-2]/a").click()
# 选择乘客
WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, f"//label[contains(text(),'{passenger_name}')]"))
).click()
# 提交订单
driver.find_element(By.ID, "submitOrder_id").click()
WebDriverWait(driver, 5).until(
EC.element_to_be_clickable((By.ID, "qr_submit_id"))
).click()
# 主流程
try:
login("您的账号", "您的密码")
search_ticket("北京", "上海", "2023-12-31")
select_train("G123", "张三")
print("抢票成功!请及时支付")
except Exception as e:
print(f"执行失败: {str(e)}")
finally:
driver.quit()
```
#### 三、关键问题解决方案
1. **验证码处理**:
- 方案1:预留15秒手动操作时间(如上代码)
- 方案2:接入打码平台(如超级鹰)自动识别
```python
# 示例代码片段
from chaojiying import Chaojiying
captcha_elem = driver.find_element(By.ID, "loginCode")
captcha_elem.screenshot("captcha.png")
# 使用打码平台识别
cjy = Chaojiying('用户名', '密码', '软件ID')
result = cjy.post_pic(open('captcha.png','rb').read(), 6113)
driver.find_element(By.ID, "randCode").send_keys(result['pic_str'])
```
2. **余票监控与重试**:
```python
import interval
from interval import every
@every(5.seconds) # 每5秒刷新一次
def monitor_ticket():
driver.refresh()
if "有" in driver.find_element(By.XPATH, "//a[@id='ticket_123456']").text:
select_train("G123", "张三")
```
3. **支付处理**:
- 跳转支付页面后需人工完成支付
- 可扩展对接支付接口(需谨慎处理安全验证)
#### 四、优化建议
1. **稳定性提升**:
- 使用`WebDriverWait`显式等待代替`sleep`
- 添加异常重试机制(如`retrying`库)
```python
from retrying import retry
@retry(stop_max_attempt_number=3, wait_fixed=2000)
def safe_click(element):
element.click()
```
2. **多线程抢票**:
```python
import threading
def worker(station):
# 每个线程处理不同车次
threading.Thread(target=worker, args=("G123",)).start()
```
3. **日志记录**:
```python
import logging
logging.basicConfig(filename='ticket.log', level=INFO)
```
#### 五、注意事项
1. 遵守12306使用条款,避免频繁请求(建议间隔≥5秒)
2. 网站结构变更时需及时调整元素定位方式
3. 重要操作前检查登录状态:
```python
if "我的12306" not in driver.title:
login()
```
> **提示**:完整项目参考[python+selenium实现12306抢票](https://github.com/testerSunshine/12306)[^2]