大家好,我是好学的小师弟,由于没抢到中秋回家的票,所以我写了这个脚本,还有些模块没有完善,后期会补上。写这个主要是用来加强自己对selenium和python的学习理解。该脚本仅供大家学习参考,辅助大家理解selenium知识。

目录

一、铁路12306登录

1.反爬虫

2.滑块验证码问题解决

3.登录模块完整代码

二、出发地,目的地,乘车日期的选择

1.乘车日期、出发地、目的地代码思路和遇到的问题

2.乘车日期、出发地、目的地模块代码

三、购票、抢票模块

1.流程概述

2.目前程序存在的缺点

3.程序难点(对我个人而言) 

4.抢票、购票模块代码

四、选座模块

五、其他

1.查询余票页面,显示页面超时

2.pycharm与界面的切换

六、完整代码


2021.9.24  更新

一、铁路12306登录

首先我们打开12306的页面,中国铁路12306。发现页面是需要二维码登陆的,所以我们要在代码里,先去选择点击账号密码登录。

             

1.反爬虫

这里如果你直接输入账号密码,是不会显示出验证码来的,直接给你报错。输入以下代码即可解决问题

script = 'Object.defineProperty(navigator,"webdriver",{get:()=>undefined,});'
driver.execute_script(script)

在代码里输入账号和密码后,会出现验证码。这里的验证码是滑块验证码,所以我们可以通过selenium的鼠标事件来执行。

from selenium.webdriver import ActionChains  #导入鼠标事件

2.滑块验证码问题解决

思路: 先确定滑块的初始位置元素,选中它,然后模拟鼠标按住不放,向右平移拖动一定的距离,即可完成验证。验证后,释放鼠标,不然后面鼠标就用不了了(一直会处在这个命令执行状态中)。记得perform()执行,这个指令不能忘

代码:             

#滑动验证码
#定位验证码的头位置元素
time.sleep(1)
picture_start=driver.find_element_by_id('nc_1_n1z')
#移动到相应的位置,并左键鼠标按住往右边拖
ActionChains(driver).move_to_element(picture_start).click_and_hold(picture_start).move_by_offset(300,0).release().perform()

效果图:

                     

3.登录模块完整代码

from selenium import webdriver
import time
from selenium.webdriver import ActionChains  #导入鼠标事件
from selenium.webdriver.common.keys import Keys    #键盘功能键封装的一个函数
driver=webdriver.Chrome()  #驱动
url='https://kyfw.12306.cn/otn/resources/login.html'   #铁路12306网址
driver.get(url)
#反爬虫
script = 'Object.defineProperty(navigator,"webdriver",{get:()=>undefined,});'
driver.execute_script(script)
#窗口最小化
driver.minimize_window()
driver.find_element_by_xpath('//*[@id="toolbar_Div"]/div[2]/div[2]/ul/li[2]/a').click()#点击账号密码登录
x=input('---输入你的用户名---\n')
driver.find_element_by_id('J-userName').send_keys(x)
y=input('---输入你的密码---\n')
driver.find_element_by_id('J-password').send_keys(y)
time.sleep(0.5)
driver.find_element_by_id('J-login').click()
#窗口最大化
driver.maximize_window()
#滑动验证码
#定位验证码的头位置元素
time.sleep(1)
picture_start=driver.find_element_by_id('nc_1_n1z')
#移动到相应的位置,并左键鼠标按住往右边拖
ActionChains(driver).move_to_element(picture_start).click_and_hold(picture_start).move_by_offset(300,0).release().perform()
#验证成功后,页面会出现一个窗口 把他叉掉或者确定掉
time.sleep(3)
try:
    driver.find_element_by_class_name("modal-close").click()
    time.sleep(1)
except:
    print('---程序正在运行---')
driver.find_element_by_xpath('//*[@id="J-index"]/a').click()
#最小化窗口
driver.minimize_window()
time.sleep(1)

二、出发地,目的地,乘车日期的选择

1.乘车日期、出发地、目的地代码思路和遇到的问题

登陆成功后,我们就来到了出发地,目的地和乘车日期的选择界面。这里我们用到了selenium的封装keys函数,keys函数可以用来调用键盘上的一些功能键,比如control,enter,backspace等。通过keys函数我们就可以做到把输入文本框里的内容,全选+删除,然后输入我们想要的出发日期,目的地,出发地。

 注意:这里的出发日期是含有下拉框的,你输入你想要的日期之后,下拉框还是不会消失,下拉框不消失,他就会覆盖到你的查询按钮,从而导致你click不到查询这个元素,无法完成接下来的操作,程序会报错。这个时候你点击空白处,下拉框才会消失,但是selenium中我没定位到空白区域的元素(或者就是压根没有?怀疑中),所以我找了另一个方法,点击静态图片,因为他们没有超链接,也就相当于空白区域,从而达到使得下拉框消失的目的,让程序可以接着运行。

2.乘车日期、出发地、目的地模块代码

here_place=input('---你的出发地---\n')
driver.find_element_by_xpath('//*[@id="fromStationText"]').clear()
driver.find_element_by_xpath('//*[@id="fromStationText"]').send_keys(here_place)
time.sleep(0.5)
driver.find_element_by_xpath('//*[@id="fromStationText"]').send_keys(Keys.ENTER)
there_place=input('---你的目的地---\n')
driver.find_element_by_xpath('//*[@id="toStationText"]').clear()
driver.find_element_by_xpath('//*[@id="toStationText"]').send_keys(there_place)
driver.find_element_by_xpath('//*[@id="toStationText"]').send_keys(Keys.ENTER)
#买哪天的票,出发日期填写
travel_date=input('由于疫情目前12306只支持提前15天买票\n出发日期格式:20xx-05-07\n')
#页面最大化
#driver.maximize_window()
time.sleep(1)
driver.find_element_by_xpath('//*[@id="train_date"]').send_keys(Keys.CONTROL,'a')    #control+a全选
time.sleep(1)
driver.find_element_by_xpath('//*[@id="train_date"]').send_keys(Keys.BACKSPACE)
time.sleep(1)
driver.find_element_by_xpath('//*[@id="train_date"]').send_keys(travel_date)
driver.find_element_by_xpath('//*[@id="train_date"]').click()
time.sleep(1)
print('等待中')
time.sleep(0.5)

try:
    #不能一次性点到链接,那就中间再点两次无用的静态文字
    driver.find_element_by_xpath('//*[@id="toolbar_Div"]/div[4]/div[3]/div[1]/h3').click()
    time.sleep(2)
    driver.find_element_by_xpath('//*[@id="index_ads"]').click()
    time.sleep(2)
    # 点击页面上的静态图片(没有内含超链接),为了把上面车票的日期下拉框给去掉
    driver.find_element_by_xpath('//*[@id="toolbar_Div"]/div[5]/div[1]/div/h2').click()
except:
    driver.find_element_by_xpath('//*[@id="index_ads"]').click()
    time.sleep(2)
    #点击页面上的静态图片(没有内含超链接),为了把上面车票的日期下拉框给去掉
    driver.find_element_by_xpath('//*[@id="toolbar_Div"]/div[5]/div[1]/div/h2').click()
time.sleep(2)
driver.find_element_by_xpath('//*[@id="search_one"]').click()
#叉掉疫情提示
time.sleep(2)

三、购票、抢票模块

1.流程概述

12306每次都是在固定时间出售票的,一般是早上8点或者下午5点(时间有可能有变化,看情况)。所以,我们只需要在程序中加一个定时模块就OK了,提前3分钟执行程序,就完成了抢票功能。

来说说购票

思路:点击查询按钮,先询问购票人是否需要购买学生票,然后输入你想要乘坐的车次,这里我预选的是二等座,如果你想要乘坐硬座,列表[]里写入15,我记得好象是,你可以把返回的字符串列表,打印出来,然后一个个数,看硬座所对应的位置是列表[]多少,然后改一下就行了。

查询余票会出现两种情况:

1.如果有余票,直接帮你预定,程序跳到下一步选座模块。

2.没有余票了或者票还没发售,程序开始不停的点击查询按钮,一旦发售或者有票,立刻帮你预定上(这里我好像只设置了运行30秒,我觉得30秒没抢到票,基本也没戏了。。。>-<)抢到票,就跳到选座模块;抢不到,浏览器直接退出。

2.目前程序存在的缺点

如果12306的页面搜索出现两行同名称的列车车次,并且你想乘坐这辆车,他只会选中第一辆车


目前只支持购买一个人的票,并且是乘车人列表出现的第一个人

3.程序难点(对我个人而言) 

1.xpath的元素定位

/  绝对定位  

//相对定位

.  当前节点

.//a[@id='苏州']   当前节点的相对位置下 a标签中的属性为id的,且id的值为苏州的xpath路径

通过这种纯手写的xpath路径方法,可以更快的获得我们想要的元素

如果我们f12,点击元素,右键copy中选择xpath,往往得到的是含有通配符的//*[@id='苏州'],这种方法,往往只能获取到一个元素,但我们想要爬的是一个列表,所以这种方法在这个时候,就不是那么合适。

2.字符串如何变成列表?

我们爬下来的所乘日期当天的班次是字符串,所以我们需要把字符串变为列表。不然的话,不好定位,不好写输出哪个值

两种方法

第一种  分-总形式

自己定义一个空列表,然后for in 遍历;空列表进行追加,把车次,二等座,硬座,余票等等这些全部挨个建列表,然后再通过一个for in遍历找到你所想要的那个车次,看循环第几遍找到,然后列表输出那个数字-1,这样子就找到了,我们想要的信息了

第二种方法  总—split函数—>字符串列表

直接把当天的班次,全部打印出来,找共同点,通过split函数对他们进行切片,分割。split返回的即是字符串列表。这个更方便一点

3.逻辑思考

这里就是if else   try except  这些逻辑上的编写, 还有break的循环跳出 ,还有就是自定义函数的编写调用。这点就是要考虑的场景太多了,耗时间和逻辑太多。。。所以我直接把场景设置死了,但是还是会有很多可能性。。。

4.抢票、购票模块代码


#看乘车人是否为学生,是否想购买学生票
def student():
    stu=input('是否购买学生票,请输入是或否\n')
    if stu=='否':
        driver.find_element_by_xpath('//*[@id="query_ticket"]').click()
        accept_stu='已选择成人票'
    else:
        stu_element=driver.find_element_by_xpath('//*[@id="sf2"]')
        stu_element.click()
        time.sleep(1)
        driver.find_element_by_xpath('//*[@id="query_ticket"]').click()
        accept_stu='已勾选购买学生票'
    return accept_stu

student()
time.sleep(1)


#还没开始售票,在不断点击预定按钮

def refresh_search_ticket(train_message,train_number):
    for times in range(0,31):
        success_message=''
        if times<30:
            driver.find_element_by_xpath('//*[@id="query_ticket"]').click()
            time.sleep(1)
            if'有'==train_message[10]or'有'==train_message[9]:
                print('---您选的车次二等座有票了,正在为您预定---')
                time.sleep(0.5)
                button = train_number.find_element_by_xpath('.//a[@class="btn72"]')  # 已经锁定车次,对当前车次进行车票预定
                time.sleep(0.5)
                button.click()
                # .//a[@class="btn72"]意思就是取a标签的属性为class的值为btn72的元素xpath
                time.sleep(1)
                success_message='---预定成功,现在正在选择乘车人---'
                print('---预定成功,现在正在选择乘车人---')
                break
            else:
                print('继续点击查询按钮,刷新余票',end='\r')

        else:
            time.sleep(1)
            success_message='---查询时间已到!抱歉,您所选的车次,目前没有二等座的票了,您可去12306官网,继续查询---'
            time.sleep(0.5)
            print('---查询时间已到!抱歉,您所选的车次,目前没有二等座的票了,您可去12306官网,继续查询---')
            break
    return success_message


def order_ticket():
    train_num=input('---输入你想乘坐的火车车次---\n')
    train_numbers = driver.find_elements_by_xpath('//tbody[@id="queryLeftTable"]/tr[not(@datatran)]')
    #记得在元素element后面加s,因为有好多个元素,这里定位的是不含datatran的所有元素
    for train_number in train_numbers:
        order_ticket_message=''
        train_messages=train_number.text.replace('\n',' ')#把换行符替换成空格
        train_message=train_messages.split(' ')
        '''
                split():拆分字符串。通过指定分隔符对字符串进行切片,并返回分割后的字符串列表(list)
        '''
        if train_num==train_message[0]:
            print('---找到你想要的车次了---')
            if'有'==train_message[10]or'有'==train_message[9]:#这里的列表数值,各个车可能有出入,到时候大家打印下整个车次列表,然后再改下数字就行了
                print('---该车次现在有二等座---')
                time.sleep(1)
                button=train_number.find_element_by_xpath('.//a[@class="btn72"]')#已经锁定车次,对当前车次进行车票预定
                button.click()
                #.//a[@class="btn72"]意思就是取a标签的属性为class的值为btn72的元素xpath
                time.sleep(1)
                order_ticket_message='预定成功,现在正在选择乘车人'
                print('预定成功,现在正在选择乘车人')
                break
            else:
                print('---抱歉,您所选的车次车票,目前没票了,存在有候补票这种情况,正在为你刷新页面,实时更新车票情况---')
                
                result='---查询时间已到!抱歉,您所选的车次,目前没有二等座的票了,您可去12306官网,继续查询---'
                time.sleep(1)
                success_messages=refresh_search_ticket(train_message,train_number)
               
                if result==success_messages:
                    time.sleep(1)
                    order_ticket_message='---本次抢票已结束,祝你好运---'
                    print('---本次抢票已结束,祝你好运---')
                    break
                else:
                    order_ticket_message='查到票了,预定成功,开始选乘坐人'
                    print('查到票了,预定成功,开始选乘坐人')
                    break
        else:
            print('---正在查询你所需要的车次---',end='\r')  #覆盖前面一句---正在查询你所需要的车次---,不然每循环一次,就会打印一次这句话

    return order_ticket_message

finally_message=order_ticket()


if finally_message=='---本次抢票已结束,祝你好运---':
    print('---即将关闭浏览器打开的所有窗口---')
    driver.quit()
    time.sleep(1)
    print('---浏览器已关闭---')
else:
    
    time.sleep(2)
    # 点击乘车人信息,默认第一个为本人
    driver.find_element_by_xpath('//*[@id="normalPassenger_0"]').click()
    print('---乘车人选择成功,默认选择12306上面乘车人列表的第一位---')
    time.sleep(0.5)
    点击提交订单
    driver.find_element_by_xpath('//*[@id="submitOrder_id"]').click()

四、选座模块

因为12306每天只有3次机会退票,好象是。所以我没法多测试这个模块到底行不行。。。。

#选座,报错预警>_<  。。。可能会涉及到网络超时问题
print('---窗户 A B C 过道 D F窗户---')
time.sleep(1)
seat=input('---输入你想要的位置,如果没有该位置,随机为你选择一个位置---\n---输入格式:place_X---\n---X为A B C D F---\n')
time.sleep(1)
seat_A='//*[@id="1A"]'  #座位
seat_B='//*[@id="1B"]'
seat_C='//*[@id="1C"]'
seat_D='//*[@id="1D"]'
seat_F='//*[@id="1F"]'
decision_true='//*[@id="qr_submit_id"]'  #确定按钮
pay_money='---没有自己想要选择的位置了,随机!---'



try:
    seat=='place_A'
    driver.find_element_by_xpath(seat_A).click()
    time.sleep(1)
    driver.find_element_by_xpath(decision_true).click()
    pay_money = '---选座成功!宝贝,快来付钱---'
    print('---选座成功!宝贝,快来付钱---')
    time.sleep(1)
    seat = 'place_A'
except:
    driver.find_element_by_xpath(seat_A).click()
    time.sleep(1)
    print('你选的不是位置A/没有位置A了')
    time.sleep(1)

try:
    seat == 'place_B'
    driver.find_element_by_xpath(seat_B).click()
    time.sleep(1)
    driver.find_element_by_xpath(decision_true).click()
    pay_money = '---选座成功!宝贝,快来付钱---'
    print('---选座成功!宝贝,快来付钱---')
    time.sleep(1)
    seat = 'place_B'
except:

    time.sleep(1)
    print('你选的不是位置B/没有位置B了')
    time.sleep(1)

try:
    seat=='place_C'
    driver.find_element_by_xpath(seat_C).click()
    time.sleep(1)
    driver.find_element_by_xpath(decision_true).click()
    pay_money = '---选座成功!宝贝,快来付钱---'
    print('---选座成功!宝贝,快来付钱---')
    time.sleep(1)
    seat = 'place_C'
except:
    time.sleep(1)
    print('你选的不是位置C/没有位置C了')
    time.sleep(1)

try:
    seat=='place_D'
    driver.find_element_by_xpath(seat_D).click()
    time.sleep(1)
    driver.find_element_by_xpath(decision_true).click()
    pay_money = '---选座成功!宝贝,快来付钱---'
    print('---选座成功!宝贝,快来付钱---')
    time.sleep(1)
    seat = 'place_D'
except:
    time.sleep(1)
    print('你选的不是位置D/没有位置D了')
    time.sleep(1)

try:
    seat=='place_F'
    driver.find_element_by_xpath(seat_F).click()
    time.sleep(1)
    driver.find_element_by_xpath(decision_true).click()
    pay_money = '---选座成功!宝贝,快来付钱---'
    print('---选座成功!宝贝,快来付钱---')
    time.sleep(1)
    seat = 'place_F'
except:
    time.sleep(1)
    print('你选的不是位置F/没有位置F了')
    time.sleep(1)


if pay_money !='---选座成功!宝贝,快来付钱---':
    print(pay_money)
    driver.find_element_by_xpath(decision_true).click()
    time.sleep(1)
    print('15分钟内,快去扫码付钱!')
else:
    print('15分钟内,快去扫码付钱!')

pay_result=input('---付款是否成功,选择是或否---')
if pay_result=='是':
    driver.quit()
else:
    print('---selenium不稳定---')

五、其他

1.查询余票页面,显示页面超时

晚上10点后,12306pc网页就不太行了,一直显示超时,所以我们就尽量不要在这个时候去登陆,我也写了一个刷新函数,有效果,虽然解决了这个页面超时问题,但是选座的时候,还会出现新的网络超时问题。。。。

2.pycharm与界面的切换

在pycharm下方出现 '等待中' 以后的那个查询余票界面,要切出来到pycharm中继续写你的需求,这个切换,最好用tab+alt。


六、完整代码

from selenium import webdriver
import time
from selenium.webdriver import ActionChains  #导入鼠标事件
from selenium.webdriver.common.keys import Keys    #键盘功能键封装的一个函数

'''
by csdn 好学的小师弟
博客:https://blog.csdn.net/weixin_43784564
新人小白,欢迎大家一起学习,交流,互相进步

注:本文目前还差一个定时执行任务脚本,且目前只支持选择含有二等座选项的车次,购买的也是二等座。
   如果12306的页面搜索出现两行同名称的列车车次,这个程序就会报错
   目前只支持购买一个人的票
   目前只测出这些bug,还有好多种乘车情况没考虑,因为逻辑太多了,写不来了,白天要打工  >_<
   
如果想要自动执行,就把input写死,然后加个定时执行脚本就行了
注意:12306网页晚上10点左右好像就不灵光了,打开有困难,一直显示页面超时
     页面和pycharm之间切换,用tab+alt
     如果报错,要么就是网络问题、又要你重新登陆了,多试几次
     要么就是selenium不稳定。
     要么就是我写的程序有问题,逻辑出错。可惜我没时间去测试改了
     (主要是选座的那一块,我发博客之前零食写的,没时间了,tfs一堆任务要去忙)
     白天打工太累了。。。。。
     如果这个脚本对大家学习selenium和python有帮助的话,大家可以写一个完善版
     如果程序一直报错,那就把选座模块注释掉,再运行;
     我昨晚试了下没有选座模块的程序,是能正常运行的。
     选座模块是我今天中午吃饭的时候写的,还没来得及测试。


本脚本,仅用来让大家互相交流学习,大家一起共同进步!

'''
driver=webdriver.Chrome()  #驱动
url='https://kyfw.12306.cn/otn/resources/login.html'   #铁路12306网址
driver.get(url)
#反爬虫
script = 'Object.defineProperty(navigator,"webdriver",{get:()=>undefined,});'
driver.execute_script(script)
#窗口最小化
driver.minimize_window()
driver.find_element_by_xpath('//*[@id="toolbar_Div"]/div[2]/div[2]/ul/li[2]/a').click()#点击账号密码登录
x=input('---输入你的用户名---\n')
driver.find_element_by_id('J-userName').send_keys(x)
y=input('---输入你的密码---\n')
driver.find_element_by_id('J-password').send_keys(y)
time.sleep(0.5)
driver.find_element_by_id('J-login').click()
#窗口最大化
driver.maximize_window()
#滑动验证码
#定位验证码的头位置元素
time.sleep(1)
picture_start=driver.find_element_by_id('nc_1_n1z')
#移动到相应的位置,并左键鼠标按住往右边拖
ActionChains(driver).move_to_element(picture_start).click_and_hold(picture_start).move_by_offset(300,0).release().perform()
#有页面有个窗口 把他叉掉或者确定掉
time.sleep(3)
try:
    driver.find_element_by_class_name("modal-close").click()
    time.sleep(1)
except:
    print('---程序正在运行---')
driver.find_element_by_xpath('//*[@id="J-index"]/a').click()
#最小化窗口
driver.minimize_window()
time.sleep(1)
here_place=input('---你的出发地---\n')
driver.find_element_by_xpath('//*[@id="fromStationText"]').clear() #清除对话框原内容,这里用的是clear函数
driver.find_element_by_xpath('//*[@id="fromStationText"]').send_keys(here_place)#输入你的出发地
time.sleep(0.5)
#因为12306输入出发地存在一个下拉框问题,所以这里我们使用keys这个封装好的函数,来调用键盘上的回车键,这样就避免了鼠标点击空白处 这个操作
driver.find_element_by_xpath('//*[@id="fromStationText"]').send_keys(Keys.ENTER)
there_place=input('---你的目的地---\n')
driver.find_element_by_xpath('//*[@id="toStationText"]').clear()
driver.find_element_by_xpath('//*[@id="toStationText"]').send_keys(there_place)
driver.find_element_by_xpath('//*[@id="toStationText"]').send_keys(Keys.ENTER)
#买哪天的票,出发日期填写
travel_date=input('由于疫情目前12306只支持提前15天买票\n出发日期格式:20xx-05-07\n')
time.sleep(1)
#这里用的是keys函数,调用ctrl+a全选,然后按删除键,删除原对话框内容
driver.find_element_by_xpath('//*[@id="train_date"]').send_keys(Keys.CONTROL,'a')    #control+a全选
time.sleep(1)
driver.find_element_by_xpath('//*[@id="train_date"]').send_keys(Keys.BACKSPACE)
time.sleep(1)
driver.find_element_by_xpath('//*[@id="train_date"]').send_keys(travel_date)
driver.find_element_by_xpath('//*[@id="train_date"]').click()
time.sleep(1)
print('等待中')
time.sleep(0.5)

'''
这里注意,就算你输入了订票日期,下拉框也依旧存在,这里我们使用以下方法,解决这个问题,详细解释
https://blog.csdn.net/weixin_43784564/article/details/120352680
'''

try:
    #不能一次性到链接,那就中间再点两次无用的静态文字
    driver.find_element_by_xpath('//*[@id="toolbar_Div"]/div[4]/div[3]/div[1]/h3').click()
    time.sleep(2)
    driver.find_element_by_xpath('//*[@id="index_ads"]').click()
    time.sleep(2)
    # 点击页面上的静态图片(没有内含超链接),为了把上面车票的日期下拉框给去掉
    driver.find_element_by_xpath('//*[@id="toolbar_Div"]/div[5]/div[1]/div/h2').click()
except:
    driver.find_element_by_xpath('//*[@id="index_ads"]').click()
    time.sleep(2)
    #点击页面上的静态图片(没有内含超链接),为了把上面车票的日期下拉框给去掉
    driver.find_element_by_xpath('//*[@id="toolbar_Div"]/div[5]/div[1]/div/h2').click()
time.sleep(2)
driver.find_element_by_xpath('//*[@id="search_one"]').click()
#叉掉疫情提示
time.sleep(2)

'''
为什么要获得窗口句柄,详细解释详见我上一条csdn博客https://blog.csdn.net/weixin_43784564/article/details/120368372
'''
# 获取打开的多个窗口句柄
windows = driver.window_handles
# 切换到当前最新打开的窗口
driver.switch_to.window(windows[-1])
time.sleep(2)

#窗口最大化
driver.maximize_window()
time.sleep(2)




#这里打开页面会有一个温馨提示,把他叉掉
try:
    driver.find_element_by_xpath('//*[@id="gb_closeDefaultWarningWindowDialog_id"]').click()
except:
    print('---这个温馨提示我没叉掉/不存在温馨提示---')


#现在来到了购票页面,点击查询按钮
#这里出现一个问题,会出现页面查询超时,所以我们应该搞个功能,即点击按钮,直到出现车次情况!!!用到自己写的一个函数

driver.find_element_by_xpath('//*[@id="query_ticket"]').click()
time.sleep(2)
driver.minimize_window()
time.sleep(2)
driver.find_element_by_xpath('//*[@id="query_ticket"]').click()
time.sleep(2)

#如果页面查询超时,就用下面这个函数
def refresh_yemian():
    successful_search = ''
    try:
        time.sleep(1)
        driver.find_element_by_xpath('//*[@id="float"]/th[1]').click()
        time.sleep(1)
        successful_search='---查询页面正常,可正常查询车次---'
        time.sleep(1)
    except:
        print('---正在点击查询按钮---',end='\r')
        time.sleep(1)
        driver.find_element_by_xpath('//*[@id="query_ticket"]').click()
        time.sleep(1)
        refresh_yemian()
    return successful_search

refresh_yemian()
time.sleep(1)

#看乘车人是否为学生,是否想购买学生票
def student():
    stu=input('是否购买学生票,请输入是或否\n')
    if stu=='否':
        driver.find_element_by_xpath('//*[@id="query_ticket"]').click()
        accept_stu='已选择成人票'
    else:
        stu_element=driver.find_element_by_xpath('//*[@id="sf2"]')
        stu_element.click()
        time.sleep(1)
        driver.find_element_by_xpath('//*[@id="query_ticket"]').click()
        accept_stu='已勾选购买学生票'
    return accept_stu

student()
time.sleep(1)

#注意,本抢票脚本只适用于开始售票的那几分钟有用,毕竟抢票就抢那几分钟
#注意,本抢票脚本还差一个定时执行任务脚本。需要你定个时

#定义一个函数,如果发现目前页面没票了,就不断刷新页面,给你查询是否有票现在.
def refresh_search_ticket(train_message,train_number):
    for times in range(0,31):
        success_message=''
        if times<30:
            driver.find_element_by_xpath('//*[@id="query_ticket"]').click()
            time.sleep(1)
            if'有'==train_message[10]or'有'==train_message[9]:
                print('---您选的车次二等座有票了,正在为您预定---')
                time.sleep(0.5)
                button = train_number.find_element_by_xpath('.//a[@class="btn72"]')  # 已经锁定车次,对当前车次进行车票预定
                time.sleep(0.5)
                button.click()
                # .//a[@class="btn72"]意思就是取a标签的属性为class的值为btn72的元素xpath
                time.sleep(1)
                success_message='---预定成功,现在正在选择乘车人---'
                print('---预定成功,现在正在选择乘车人---')
                break
            else:
                print('继续点击查询按钮,刷新余票',end='\r')

        else:
            time.sleep(1)
            success_message='---查询时间已到!抱歉,您所选的车次,目前没有二等座的票了,您可去12306官网,继续查询---'
            time.sleep(0.5)
            print('---查询时间已到!抱歉,您所选的车次,目前没有二等座的票了,您可去12306官网,继续查询---')
            break
    return success_message


#输入你想乘坐的车次,订票
def order_ticket():
    train_num=input('---输入你想乘坐的火车车次---\n')
    train_numbers = driver.find_elements_by_xpath('//tbody[@id="queryLeftTable"]/tr[not(@datatran)]')
    #记得在元素element后面加s,因为有好多个元素,这里定位的是不含datatran的所有元素
    for train_number in train_numbers:
        order_ticket_message=''
        train_messages=train_number.text.replace('\n',' ')#把换行符替换成空格
        train_message=train_messages.split(' ') #split切割函数,用' '来切片,并返回字符串列表

        if train_num==train_message[0]:
            print('---找到你想要的车次了---')
            if'有'==train_message[10]or'有'==train_message[9]:#这里的列表数值,各个车可能有出入,到时候大家打印下整个车次列表,然后再改下数字就行了,一般来说是不用改的
                print('---该车次现在有二等座---')
                time.sleep(1)
                button=train_number.find_element_by_xpath('.//a[@class="btn72"]')#已经锁定车次,对当前车次进行车票预定
                button.click()
                #.//a[@class="btn72"]意思就是取当前节点,相对路径下的a标签的属性为class的值为btn72的元素xpath
                time.sleep(1)
                order_ticket_message='预定成功,现在正在选择乘车人'
                print('预定成功,现在正在选择乘车人')
                break
            else:
                print('---抱歉,您所选的车次车票,目前没票了,存在有候补票这种情况,正在为你刷新页面,实时更新车票情况---')
                result='---查询时间已到!抱歉,您所选的车次,目前没有二等座的票了,您可去12306官网,继续查询---'
                time.sleep(1)
                #没票了,现在调用不断刷新页面的函数,来给你查询余票,并返回值
                success_messages=refresh_search_ticket(train_message,train_number)

                if result==success_messages:
                    time.sleep(1)
                    order_ticket_message='---本次抢票已结束,祝你好运---'
                    print('---本次抢票已结束,祝你好运---')
                    break
                else:
                    order_ticket_message='查到票了,预定成功,开始选乘坐人'
                    print('查到票了,预定成功,开始选乘坐人')
                    break
        else:
            print('---正在查询你所需要的车次---',end='\r')  #覆盖前面一句---正在查询你所需要的车次---,不然每循环一次,就会打印一次这句话

    return order_ticket_message

finally_message=order_ticket()
if finally_message=='---本次抢票已结束,祝你好运---':
    print('---即将关闭浏览器打开的所有窗口---')
    driver.quit()
    time.sleep(1)
    print('---浏览器已关闭---')
else:
    time.sleep(2)
    # 点击乘车人信息,默认第一个为本人
    driver.find_element_by_xpath('//*[@id="normalPassenger_0"]').click()
    time.sleep(1)
    print('---乘车人选择成功,默认选择12306上面乘车人列表的第一位---')
    time.sleep(1)
    #点击提交订单
    driver.find_element_by_xpath('//*[@id="submitOrder_id"]').click()
    time.sleep(1)
    print('---已提交订单---')
    time.sleep(1)
    
    
    #下面可能存在逻辑错误
#选座
print('---窗户 A B C 过道 D F窗户---')
time.sleep(1)
seat=input('---输入你想要的位置,如果没有该位置,随机为你选择一个位置---\n---输入格式:place_X---\n---X为A B C D F---\n')
time.sleep(1)
seat_A='//*[@id="1A"]'  #座位
seat_B='//*[@id="1B"]'
seat_C='//*[@id="1C"]'
seat_D='//*[@id="1D"]'
seat_F='//*[@id="1F"]'
decision_true='//*[@id="qr_submit_id"]'  #确定
pay_money='---没有自己想要选择的位置了,随机!---'



try:
    seat=='place_A'
    driver.find_element_by_xpath(seat_A).click()
    time.sleep(1)
    driver.find_element_by_xpath(decision_true).click()
    pay_money = '---选座成功!宝贝,快来付钱---'
    print('---选座成功!宝贝,快来付钱---')
    time.sleep(1)
    seat = 'place_A'
except:
    driver.find_element_by_xpath(seat_A).click()
    time.sleep(1)
    print('你选的不是位置A/没有位置A了')
    time.sleep(1)

try:
    seat == 'place_B'
    driver.find_element_by_xpath(seat_B).click()
    time.sleep(1)
    driver.find_element_by_xpath(decision_true).click()
    pay_money = '---选座成功!宝贝,快来付钱---'
    print('---选座成功!宝贝,快来付钱---')
    time.sleep(1)
    seat = 'place_B'
except:

    time.sleep(1)
    print('你选的不是位置B/没有位置B了')
    time.sleep(1)

try:
    seat=='place_C'
    driver.find_element_by_xpath(seat_C).click()
    time.sleep(1)
    driver.find_element_by_xpath(decision_true).click()
    pay_money = '---选座成功!宝贝,快来付钱---'
    print('---选座成功!宝贝,快来付钱---')
    time.sleep(1)
    seat = 'place_C'
except:
    time.sleep(1)
    print('你选的不是位置C/没有位置C了')
    time.sleep(1)

try:
    seat=='place_D'
    driver.find_element_by_xpath(seat_D).click()
    time.sleep(1)
    driver.find_element_by_xpath(decision_true).click()
    pay_money = '---选座成功!宝贝,快来付钱---'
    print('---选座成功!宝贝,快来付钱---')
    time.sleep(1)
    seat = 'place_D'
except:
    time.sleep(1)
    print('你选的不是位置D/没有位置D了')
    time.sleep(1)

try:
    seat=='place_F'
    driver.find_element_by_xpath(seat_F).click()
    time.sleep(1)
    driver.find_element_by_xpath(decision_true).click()
    pay_money = '---选座成功!宝贝,快来付钱---'
    print('---选座成功!宝贝,快来付钱---')
    time.sleep(1)
    seat = 'place_F'
except:
    time.sleep(1)
    print('你选的不是位置F/没有位置F了')
    time.sleep(1)


if pay_money !='---选座成功!宝贝,快来付钱---':
    print(pay_money)
    driver.find_element_by_xpath(decision_true).click()
    time.sleep(1)
    print('15分钟内,快去扫码付钱!')
else:
    print('15分钟内,快去扫码付钱!')

pay_result=input('---付款是否成功,选择是或否---')
if pay_result=='是':
    driver.quit()
else:
    print('---selenium不稳定---')

新人创作不易,觉得不错的看官,点个赞吧,么么哒!!!

该篇仅用来学习,禁止转载!

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐