不知不觉,一年一度的春运抢票大幕已经拉开,想快速抢到回家的车票吗?作为程序员,这些技术手段,你一定要知道。

为了让大家更快捷更便利的抢火车票,各种各样的抢票软件应需而生,这类软件大部分都是付费抢票的机制。

作为程序员,如何用技术手段抢到回家的票?来看看用 Python 写的抢票脚本。

手把手教你用 Python 抢票回家过年

环境介绍

windows 8.1
python3.6.1
firefox插件 geckodriver.exe

操作步骤

引入要的模块

from selenium import webdriver      #控制浏览器

from selenium.webdriver.common.keys import Keys  #用于给元素赋值

import time   #时间模块

from selenium.webdriver.support.select import Select  #控制下拉框模块

from selenium.webdriver.common.by import By   #寻找元素模块

from selenium.webdriver.support.ui import WebDriverWait  #“显示等待”模块

from selenium.webdriver.support import expected_conditions as EC  #等待条件模块


登陆模块

首先需要选择使用的浏览器,此处以 firefox 为例,下载:geckodriver.exe 。

下载地址:

https://github.com/mozilla/geckodriver/releases

提到的 stations.txt 可以直接看这个:

车站信息:

https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9042

将 geckodriver.exe 放到 python.exe 同级目录下即可(如果有报错的情况下,放一个该文件到与 firefox.exe 同级目录下,并添加环境变量)

#可以用input,也可以直接放入到后面的用户名、密码输入框中

#可以利用标准输入进行批量的操作,此处以个人抢票操作为例

# username = str(input('请输入你的用户名:'))

# password = str(input('请输入你的密码:'))  #这两行可以暂时忽略

browser = webdriver.Firefox()      #驱动firefox浏览器

browser.get("https://kyfw.12306.cn/otn/login/init")    #启动浏览器后进入该链接下

browser.find_element_by_id('username').clear()

browser.find_element_by_id('username').send_keys(‘xxxxx’)    #xxxxx更换为用户名

browser.find_element_by_id('password').send_keys(‘xxxxx’)    #xxxxx更换为密码

time.sleep(10)    #此时验证码自行点击,该处设置10秒延迟,可以自己设置

try:

browser.find_element_by_id('loginSub').click()     #点击登陆操作,该id为登陆按钮

#或者 browser.find_element_by_link_text('登陆').click()  #标签显示的名称

except:

browser.find_element_by_class_name('touclick-bgimg touclick-reload touclick-reload-normal').click()     #try中验证码输入点错了会在此处刷新一次

time.sleep(20)                     #第二次输入验证码前等待20秒,可以自己设置,第一次输入无误直接跳过

browser.find_element_by_id('loginSub').click()    #重新输入验证码后的点击登陆

跳转模块

#默认跳转到首页

time.sleep(2)    #此处一般无需设置时间等待,调试代码时使用

clickReserve = browser.find_element_by_link_text('车票预订').click()  #跳转到车票预定页面,该页面可以查询票

time.sleep(2)        #出发地点和到达地点设置

#此处value值为出发时刻的地点,BJP表示北京,更改value值在页面上不加载,基本不耗时间,从页面中也看不到出发地和目的地

#此处内容以爬取,保存在stations.txt中,每行表示一个地址,打开文档ctrl + F查找即可

jsf = 'var a = document.getElementById("fromStation");a.value = "BJP"'    #此处将BJP更换为你需要的出发地址,value值在以爬取到stations.txt中,自行查看

browser.execute_script(jsf)

jst = 'var a = document.getElementById("toStation");a.value = "LZJ"'   #终点,同上方法

browser.execute_script(jst)

js = "document.getElementById('train_date').removeAttribute('readonly')"    #时间选择时默认为只读,通过JS移除只读属性

browser.execute_script(js)    #执行JS语句

browser.find_element_by_id('train_date').clear()    #时间元素中默认有提示字,需要先清空

browser.find_element_by_id('train_date').send_keys('2018-02-01')   #按照改格式输入需要查询的时间

search = browser.find_element_by_id('query_ticket').click()    #输入好信息时点击查询,该处存在成人票和学生票,默认是成人票,如果购买,对学生票处执行以下语句即可:

#browser.find_element_by_id('xxxx').click()    #对于id还是class或其它自行选择,[可以查看此处](http://blog.51cto.com/12376665/2052278)

开始购票

此处,就是点击预定的操作,我在这里只是举一个方法例子,也可以通过不断点击直到成功(这样可以避免网站倒计时和实际时间的时间差影响,但是不知道 12306 在抢票时对不断快速访问有没有限制)。

start_time = "Thu Jan 04 08:00:00 2018"    #首先设置需要抢票的时间

b = time.mktime(time.strptime(start_time,"%a %b %d %H:%M:%S %Y"))        print(time.strftime("%a %b %d %H:%M:%S %Y", time.localtime(b)) )  #此处是为了调试代码使用,可忽略,不影响使用

a = float(b)-time.time()    #利用自己设置的时间减去当前时间的时间戳

time.sleep(a)    #上一步骤得出的秒数就是需要等待抢票的时间

try:     #此处本来有try中的部分就够了,WebDriverWait已有相应等待重复访问机制,默认为0.5秒试验一次,except中添加是为了以防万一

WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "ticket_2400000Z550L")))   #查找需要预定的车次的id,直到出现,10表示共等待10秒

ticket = browser.find_element_by_xpath('//tr[@id="ticket_2400000Z550L"]/td[13]/a').click()    #点击预定按钮except:

browser.find_element_by_id('query_ticket').click()

WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.ID, "ticket_2400000Z550L")))

ticket = browser.find_element_by_xpath('//tr[@id="ticket_2400000Z550L"]/td[13]/a').click()

"""

normalPassenger_8 数字表示该账号下的第几位,默认从0开始如果是第一个则为normalPassenger_0

"""WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "normalPassenger_8")))

browser.find_element_by_id('normalPassenger_8').click()   #id中的8表示账号下第九位s = Select(browser.find_element_by_id('seatType_1'))

s.select_by_value('6')    #此处value值看下方各个种类,6表示高级软卧browser.find_element_by_id('submitOrder_id').click()

WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "qr_submit_id")))

browser.find_element_by_link_text('提交订单')

browser.find_element_by_id('qr_submit_id').click()#-------------------------------------------------结束#硬座 1#硬卧 3#软卧 4#高级软卧 6#二等座 O(大写字母)#一等座 M#商务座 9

总结

需要替换的地方:

  • 用户名,密码。

  • 起始地点和目的地的 value 值,查 stations.txt 修改即可。

  • 出发时间。

  • 自己选择车次的 xpath 路径,路径不用变,变对应 id 即可。

  • 勾选用户的位置(如果只要一个用户,默认用:normalPassenger_0)。

  • 所选座位类别,默认为有票的类别里最便宜的种类。

其余的在测试中都相同,没有发现有变化,在使用前,可以测试一下代码,测试是注意注释掉提交订单的代码(下单有取消限制,每天好像只能取消三次),测试时网速正常。

有人说用浏览器执行速度会慢,确实对于可以直接识别验证码的脚本而言,没有界面的会更快一些,但是实际上所用时间为预定开始到结束,相同网络下,代码执行时间是要快于人工操作的,

另外,时间可以研究一下,之前研究过某宝的时间,秒杀时间是要比北京时间提前一点几秒的,感觉全国各地有微小时间差的。

完整脚本示例


#python3.6.1#data:2018-01-03#author:LGC247CG"""

说明:

1.该脚本主要是提供一个实现思路,实现方法有很多,可以优化的地方也有很多,触发机制也可以自己设置,代码以压缩到最短,只是为了让大家都可以看明白

2.正常网络状况下,不设置指定时间时,从点击确认验证码到下单基本上1秒左右,所以速度上还是没问题的

3.由于同时勾选多人和单人使用所需时间基本相同,希望该方法只用于技术交流,请勿作为黄牛使用

4.在作为技术交流的情况下,如果验证码可以实现将可以完全实现自动抢票:

--1>验证码有一定规律和数量,可以利用脚本获取所有图片,并加上相应标签

--2>将页面的文字和标签相匹配,再将图片进行相似度计算,对对应图片进行点击操作

--3>或是训练深度学习的图片识别模型,通过算法识别

"""from selenium import webdriverfrom selenium.webdriver.common.keys import Keysimport timefrom selenium.webdriver.support.select import Selectfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as EC

browser = webdriver.Firefox()

browser.get("https://kyfw.12306.cn/otn/login/init")

browser.find_element_by_id('username').clear()

browser.find_element_by_id('username').send_keys('xxxxxxx')

browser.find_element_by_id('password').send_keys('xxxxxxx')

time.sleep(10)try:

browser.find_element_by_id('loginSub').click()except:

browser.find_element_by_class_name('touclick-bgimg touclick-reload touclick-reload-normal').click()

time.sleep(15)

browser.find_element_by_id('loginSub').click()#跳转到车票预定页面time.sleep(2)

clickReserve = browser.find_element_by_link_text('车票预订').click()#出发地点和到达地点设置WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "fromStation")))

jsf = 'var a = document.getElementById("fromStation");a.value = "BJP"'browser.execute_script(jsf)

jst = 'var a = document.getElementById("toStation");a.value = "LZJ"'browser.execute_script(jst)

js = "document.getElementById('train_date').removeAttribute('readonly')"browser.execute_script(js)

browser.find_element_by_id('train_date').clear()

browser.find_element_by_id('train_date').send_keys('2018-02-02')

search = browser.find_element_by_id('query_ticket').click()#对于时间,我一直觉得网站计算的时间和自己获取的时间差一秒左右,这个根据不同环境自己测试start_time = "Thu Jan 04 10:00:00 2018"    #首先设置需要抢票的时间b = time.mktime(time.strptime(start_time,"%a %b %d %H:%M:%S %Y"))

print(time.strftime("%a %b %d %H:%M:%S %Y", time.localtime(b)) )  #此处是为了调试代码使用,可忽略,不影响使用a = float(b)-time.time()    #利用自己设置的时间减去当前时间的时间戳time.sleep(a)    #上一步骤得出的秒数就是需要等待抢票的时间browser.find_element_by_id('query_ticket').click()    #时间到了先点击查询刷新一下,以防找不到元素try:

WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "ticket_2400000Z550L")))

ticket = browser.find_element_by_xpath('//tr[@id="ticket_2400000Z550L"]/td[13]/a').click()except:

browser.find_element_by_id('query_ticket').click()

WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.ID, "ticket_250000K8880L")))

ticket = browser.find_element_by_xpath('//tr[@id="ticket_250000K8880L"]/td[13]/a').click()"""

normalPassenger_8 数字表示该账号下的第几位,默认从0开始如果是第一个则为normalPassenger_0

"""WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "normalPassenger_8")))

browser.find_element_by_id('normalPassenger_8').click()

s = Select(browser.find_element_by_id('seatType_1'))

s.select_by_value('6')

browser.find_element_by_id('submitOrder_id').click()

WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "qr_submit_id")))

browser.find_element_by_link_text('提交订单')#browser.find_element_by_id('qr_submit_id').click()

作者:LGC247CG

简介:大学时候就已经接触 Python,并研究 Python 爬虫,数据分析,文本处理,图像处理等,有五年应用经验,工作以后主要用在 Linux 系统运维脚本。

本文转载自51CTO技术栈,编辑:陶家龙、孙淑娟

推荐阅读

  • GIAC全球互联网架构大会2017上海站圆满结束,PPT合集下载

  • 爬虫工程师如何绕过验证码?寻找阿登高地之路

  • 为何大量网站不能抓取?爬虫突破封禁的6种常见方法

  • 全球直播的罗胖跨年演讲背后技术支撑故事——罗辑思维首席架构师方圆访谈

高可用架构

改变互联网的构建方式

长按二维码 关注「高可用架构」公众号

再不看就晚了,我用Python抢到了回家的火车票!相关推荐

  1. 有人用python抢到过吗_再不看就晚了,我用Python抢到了回家的火车票!

    原标题:再不看就晚了,我用Python抢到了回家的火车票! 不知不觉,一年一度的春运抢票大幕已经拉开,想快速抢到回家的车票吗?作为程序员,这些技术手段,你一定要知道. 为了让大家更快捷更便利的抢火车票 ...

  2. python12306抢学生票票下载_再不看就晚了,我用Python抢到了回家的火车票!

    不知不觉,一年一度的春运抢票大幕已经拉开,想快速抢到回家的车票吗?作为程序员,这些技术手段,你一定要知道. 为了让大家更快捷更便利的抢火车票,各种各样的抢票软件应需而生,这类软件大部分都是付费抢票的机 ...

  3. 时间加减计算器_初级会计职称考试不让带计算器?!手把手教你使用机考系统计算器,再不看就晚了!...

    备考初级会计的你是否还执着于做纸质习题?还在一次次的手动计算?偷偷告诉大家,别人已经开始模拟机考答题啦! 又有人问:"机考自带的计算器用不明白怎么办?",别急,奥奥将在本文为大家介 ...

  4. 用python抢票犯法吗_火车票抢票python代码公开揭秘!

    市场上很多火车票抢票软件大家应该非常熟悉,但很少有人研究具体是怎么实现的,所以觉得很神秘,其实很简单.下面使用Python模拟抢票程序,给大家揭秘抢票到底是怎么回事. 该代码仅供参考,主要用于大家沟通 ...

  5. python 编程该看那些书籍_初学者自学Python要看什么书?

    原标题:初学者自学Python要看什么书? 人工智能时代的来临让Python崭露头角,语法简洁.功能强大的特性更是吸引了很多人学习Python.由于某些条件的限制,有部分人选择自学Python,而需要 ...

  6. python中df head_10招!看骨灰级Pythoner如何玩转Python

    原标题:10招!看骨灰级Pythoner如何玩转Python pandas是基于numpy构建的,使数据分析工作变得更快更简单的高级数据结构和操作工具.本文为大家带来10个玩转Python的小技巧,学 ...

  7. “我不看春晚,但想要张小斐同款”

    来源/Vista氢商业 ID/Qingshangye666 作者/橘总 今年过年我妹干了两件事,一个是吐槽春晚,必带催婚生娃反躺平: 一个是边看边剁手,张小斐的孔雀蓝风衣没抢到,又去拔草沈腾的破烂毛衣 ...

  8. 春运能不能抢到票就看他了!开源的Python抢票神器

    作者 | 刘早起  责编 | 张文 出品 | 早起Python(ID:zaoqi-python) 春运即将到来,抢票回家又该提上日程了!在 Github 上也有很多优秀的开发者开源了一些基于 Pyth ...

  9. python新手入门代码-新手必看:手把手教你入门 Python

    原标题:新手必看:手把手教你入门 Python 本文为 AI 研习社编译的技术博客,原标题 : Learning Python: From Zero to Hero 翻译 |永恒如新的日常校对 | 酱 ...

最新文章

  1. leetcode--罗马数字转整数--python
  2. SQL中Group By的使用详解
  3. commit与rollback命令
  4. oracle创建表空间、用户
  5. php yii2模块,Yii2 之 frontend 子模块实践之四:路由美化
  6. Spring Data 开发环境搭建(二)
  7. Css/Js推荐类库
  8. 数据结构(树状结构-树)
  9. MySQL 复习笔记
  10. 【水果识别】基于matlab GUI形态学水果识别【含Matlab源码 1364期】
  11. 工作流任务的权限问题
  12. HpSocket HttpEasyClient 二次封装
  13. 昨晚《体育世界》LBJ在CCTV5
  14. AirSim在Windows下环境搭建
  15. ArcGIS GeoEvent 使用教程(二)
  16. 如何在Windows server 2012中设置文件夹共享和访问
  17. 计算机专业屏幕尺寸,简单查看电脑屏幕尺寸、配置
  18. 客服系统竞品分析报告Shein
  19. 基于Bootstrap的超酷jQuery开关按钮插件
  20. PL/SQL 中修改数据库中的列属性

热门文章

  1. 自然对数底e的来源1
  2. C/C++系列之变量声明与使用——新手必须掌握的基础技能二
  3. 百度搜索自动补全(百度搜索常见api)
  4. 字节流和一些字节流案例
  5. linux TTY子系统(3) - tty driver
  6. 关于nuxt.js和seo的实践我有话要说
  7. 阿里上线“娱乐宝”试水影视投资 大数据影视野心隐现
  8. 搜索中涉及的算法问题
  9. 编写一个Dog类,有狗的名字和年龄两个属性。定义一个Dog构造方法,接收并初始化属性。
  10. 期末项目——人力资源管理系统需求分析