selenium实现12306抢票脚本

qiangpiao.py

python 复制代码
import time

from selenium import webdriver
from selenium.common import NoSuchElementException, ElementNotInteractableException
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from station_name import get_station
from selenium.webdriver.support.ui import Select

class QiangPiao:
    #登录网址
    login_url = 'https://kyfw.12306.cn/otn/resources/login.html'
    station_info = {}
    #初始化driver
    def __init__(self, driver,from_station,to_station, date_station,trains,passenger_name_lst):
        self.driver = driver
        self.from_station = from_station
        self.to_station = to_station
        self.date_station = date_station
        self.trains = trains
        self.passenger_name_lst = passenger_name_lst
        self.seat_type = None
        for station_name, station_code in get_station():
            self.station_info[station_name] = station_code

    #打开网址
    def open(self):
        self.driver.get(self.login_url)
        self.driver.maximize_window()
    #扫描登录
    def login(self):
        WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="toolbar_Div"]/div[3]/div[2]/ul/li[2]/a'))).click()
        WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.ID, 'gerenzhongxin')))
    #查询车票
    def search(self):
        self.driver.find_element(By.ID, 'link_for_ticket').click()
        from_station_code = self.station_info[self.from_station]
        to_station_code = self.station_info[self.to_station]

        from_station_element = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.ID, 'fromStation')))
        to_station_element = self.driver.find_element(By.ID, 'toStation')
        train_date = self.driver.find_element(By.ID, 'train_date')
        self.driver.execute_script('arguments[0].value = "%s"' % from_station_code, from_station_element)
        self.driver.execute_script('arguments[0].value = "%s"' % to_station_code, to_station_element)
        train_date.clear()
        train_date.send_keys(self.date_station)
        is_flag = False #标记是否有余票
        is_select_fram_and_to = False #标记是否已勾选出发地和目的地复选框
        while True:
            self.driver.find_element(By.ID, 'query_ticket').click()
            # 勾选出发和目的地
            if not is_select_fram_and_to:
                WebDriverWait(self.driver,10).until(EC.element_to_be_clickable((By.ID, f"cc_from_station_{self.from_station}"))).click()
                WebDriverWait(self.driver,10).until(EC.element_to_be_clickable((By.ID, f"cc_to_station_{self.to_station}"))).click()
                is_select_fram_and_to = True
            #获取车次
            WebDriverWait(self.driver, 1000).until(
                EC.presence_of_element_located((By.XPATH, '//tbody[@id="queryLeftTable"]/tr[not(@datatran)]')))
            train_infos = self.driver.find_elements(By.XPATH, '//tbody[@id="queryLeftTable"]/tr[not(@datatran)]')
            #分别遍历每个车次
            for train_info in train_infos:
                infos = train_info.text.replace('\n', ' ').split(' ')
                train_no = infos[0]
                if train_no in self.trains:
                    #根据key获取值 席别是一个列表
                    seat_types = self.trains[train_no]
                    for seat_type in seat_types: #遍历席位的列表
                        if seat_type == 'O': #说明是二等座
                            count = infos[11] #索引为11的是二等座
                            if count.isdigit() or count == '有':#有票就可以执行单击预定
                                is_flag = True
                                self.seat_type = seat_type
                                break
                        elif seat_type == 'M': #说明是一等座
                            count = infos[10] #索引为10的是一等座
                            if count.isdigit() or count == '有':#有票就可以执行单击预定
                                is_flag = True
                                self.seat_type = seat_type
                                break
                    if is_flag:
                        order_btn = train_info.find_element(By.XPATH, './/a[@class="btn72"]')
                        order_btn.click()
                        return

    def confirm(self):
        submit_btn = WebDriverWait(self.driver, 1000).until(EC.element_to_be_clickable((By.ID, 'submitOrder_id')))
        passengers = self.driver.find_elements(By.XPATH, '//ul[@id="normal_passenger_id"]/li/label')
        for passenger in passengers: #分别遍历每一位乘车人
            name = passenger.text #获取乘车人姓名
            if name in self.passenger_name_lst:
                passenger.click()
        #确认席位
        seat_select = Select(self.driver.find_element(By.ID, 'seatType_1'))
        try:
            seat_select.select_by_value(self.seat_type)
        except NoSuchElementException:
            pass
        #提交订单
        submit_btn.click()

        ok_btn = WebDriverWait(self.driver, 1000).until(EC.element_to_be_clickable((By.ID, 'qr_submit_id')))
        while True:
            try:
                ok_btn.click()
                ok_btn = self.driver.find_element(By.ID, 'qr_submit_id')
            except NoSuchElementException:
                print("aaa")
                break
            except ElementNotInteractableException:
                print("bbb")
                # 如果仍不可交互,等一小会儿再试
                break
        print(self.seat_type)
        if self.seat_type == 'O':
            print('恭喜抢到车票了,坐席为二等坐!!!')
        elif self.seat_type == 'M':
            print('恭喜抢到车票了,坐席为一等坐!!!')
    def start(self):
        self.open()
        self.login()
        self.search()
        self.confirm()

station_name.py

python 复制代码
import requests
import re
import openpyxl
def get_station():
    url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9369'
    #伪装成浏览器,为了成功得到数据
    headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36'}
    resp = request = requests.get(url, headers=headers)
    resp.encoding = 'utf-8'
    stations = re.findall(r'([\u4e00-\u9fa5]+)\|([A-Z]+)', resp.text)
    # print(stations)
    return stations
def save(lst):
    wb = openpyxl.Workbook()
    ws = wb.active
    for item in lst:
        ws.append(item)
    wb.save('station_name.xlsx')
if __name__ == '__main__':
    lst = get_station()
    save(lst)

main.py

python 复制代码
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from qiangpiao import QiangPiao

driver = webdriver.Chrome(service=Service('chromedriver.exe'))
qp = QiangPiao(driver,'上虞南','杭州东', '2026-07-16',{'G7588': ['O','M']}, ['胡渠洋']) #O是二等座,M是一等座
qp.start()

input('按任意键继续')