火车票抢票程序【selenium】【python】

火车票抢票小程序

    • 提示
    • 环境
    • 功能
    • 项目结构
    • 导包
    • 账号以及车票信息
    • 代码入口
    • 抢票函数
    • 判断`预订`按钮是否存在
    • Github

提示

该项目仅用于selenium学习,不可用于违法行为,否则后果自负

环境

本文测试环境:win10,python3,selenium > 4.0,chrome = 104,pycharm,chromedriver.exe = 104

功能

本项目实现了火车抢票系统的自动登录,可自动过滑动验证码,自动查询车票是否开售,一旦放票可以自动选票并提交订单,可以选学生票,你只需在10分钟内支付即可。需要注意的是该程序要在放票前几分钟运行,要不然票都被抢光了再运行神仙也抢不到

项目结构

火车票抢票程序【selenium】【python】_第1张图片

导包

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

账号以及车票信息

config.py中填入账号以及车票信息

class Config:
    def __init__(self):
        # 账号
        self.username = '#########'
        # 密码  每3天12306就会强制要求修改一次密码才能使用账号密码登录
        self.password = '#########'
        # 出发地 一定要写清楚 比如:北京西站就要写北京西站,不要只写北京
        self.fromstation = '#########'
        # 目的地 一定要写清楚 比如:郑州西站就要写郑州西站,不要只写郑州
        self.destination = '#########'
        # 出发日期,格式一定要是这样:2022-08-17
        self.date = '#########'
        # 车次   例如Z146,G127
        self.trainnumber = '#########'

代码入口

本项目测试时的chrome版本为104,对应的chromedriver.exe版本也为104
可以依据自己电脑上chrome的版本号下载对应版本的chromedriver.exe替换项目中的chromedriver.exe
chromedriver.exe下载地址:http://chromedriver.storage.googleapis.com/index.html

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('./chromedriver.exe')
    driver = webdriver.Chrome(service=s)
    get_ticket(conf, driver, url)
    time.sleep(10)
    driver.quit()

抢票函数

def get_ticket(conf, driver, url):
    # 过12306网站检测,没加这句的话,账号密码登录时滑动验证码过不了,但二维码登录不受影响
    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(2)

过滑动验证码

	# 定位滑块的位置
    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()

查询车票是否开售,若开售则抢票
并且考虑并解决了这种情况下的车票选择问题
两张同车次火车票
如果想选第一张,只需要在config.py中写清楚出发地是“南昌西”,目的地是“郑州”即可
如果想选第二张,只需要在config.py中写清楚出发地是“南昌西”,目的地是“郑州西”即可

查询车票是否可以预订
12306系统只支持支持C、D、G字头的动车组列车选座

    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]
        for ticket in tickets:
            # 车票出发站
            start_station = str(ticket.text).replace("复\n", "").replace("智\n", "").split('\n')[1]
            # 车票到达站
            arrival_station = str(ticket.text).replace("复\n", "").replace("智\n", "").split('\n')[2]
            # 如果车票的车次等于想要的车次并且车票的出发站等于您的出发站,到达站等于您的到达站并且硬卧的状态不是候补则点击预订,这样可使得车票唯一
            # value = '//td[2]'表示商务座特等座,'//td[3]'表示一等座,'//td[4]'表示二等座,'//td[5]'表示高级软卧,'//td[6]'表示软卧 ,'//td[7]'表示动卧,'//td[8]'表示硬卧,'//td[9]'表示软座,td[10]表示硬座
            if ticket.find_element(by=By.CLASS_NAME,value='number').text == conf.trainnumber and start_station == conf.fromstation and arrival_station == conf.destination and ticket.find_element(by=By.XPATH, value='//td[8]').text != "候补":
                # 点击预订
                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()
                # 等待1秒,才能获取座位(最好不要删掉,否则好像选座会出问题)
				# time.sleep(1)
				# 选座  A座:'1A',B座:'1B',C座:'1C',D座:'1D',E座:'1E',F座:'1F'
				# driver.execute_script('document.getElementById("1F").click()')
				# 第二个人选座  A座:'2A',B座:'2B',C座:'2C',D座:'2D',E座:'2E',F座:'2F'
				# driver.execute_script('document.getElementById("1F").click()')
                # 确认提交订单
                driver.find_element(by=By.XPATH, value='//*[@id="qr_submit_id"]').click()
                print(f"{conf.trainnumber}次列车 从{start_station}-->{arrival_station} 抢票成功,请尽快在10分钟内支付!")
                return
		print(f"抱歉,没有找到车次为{conf.trainnumber},出发站为{conf.fromstation},到达站为{conf.destination}的车票!")

要想选座,可以解开下面代码的注释

# 等待1秒,才能获取座位(最好不要删掉,否则好像选座会出问题)
# time.sleep(1)
# 第一个人选座  A座:'1A',B座:'1B',C座:'1C',D座:'1D',E座:'1E',F座:'1F'
# driver.execute_script('document.getElementById("1F").click()')
# 第二个人选座  A座:'2A',B座:'2B',C座:'2C',D座:'2D',E座:'2E',F座:'2F'
# driver.execute_script('document.getElementById("1F").click()')

判断预订按钮是否存在

def isElementExist(driver):
    flag=True
    ele = driver.find_elements(by=By.CLASS_NAME, value='btn72')
    if len(ele) == 0:
        flag = False
        return flag
    else:
        return flag

Github

链接请直接点击跳转,不要复制,因为这不是完整链接(博文里不能出现那5个数字)
https://github.com/MichistaLin/ticket-grabbing

你可能感兴趣的:(python,爬虫,selenium)