【selenium12306抢票购票脚本】肝了一周,2万3千字超详细代码详解 【建议收藏】 您所在的位置:网站首页 铁路12306买票可靠吗 【selenium12306抢票购票脚本】肝了一周,2万3千字超详细代码详解 【建议收藏】

【selenium12306抢票购票脚本】肝了一周,2万3千字超详细代码详解 【建议收藏】

2024-05-28 05:50| 来源: 网络整理| 查看: 265

        大家好,我是好学的小师弟,由于没抢到中秋回家的票,所以我写了这个脚本,还有些模块没有完善,后期会补上。写这个主要是用来加强自己对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秒没抢到票,基本也没戏了。。。>-字符串列表

直接把当天的班次,全部打印出来,找共同点,通过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_< 。。。可能会涉及到网络超时问题 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


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有