本文介绍的是如何用 Python 语言实现 12306 自动预定列车票,也就是坊间常说的“抢票”,但个人觉得,这不算是“抢”,只不过是一定程度的自动化。

总体设计

所谓抢票软件,本质上就是基于浏览器驱动,实现登录、预定、确认信息的自动化。购买列车票涉及4个网页,相应的基本流程如下:

  1. 登录:输入用户名、密码,识别验证码,点击“登录”;

  2. 基本信息填写:出发地,目的地,出发日期,车票类型(普通或学生),车次类型选择,点击“查询”,如果目标车次尚有余票则点击“预定”,否则再次点击查询……;

  3. 订单信息填写:乘车人选择,席别选择,票种选择,点击“提交订单”;

  4. 订单确认:选择座位位置,点击“确认”。

详细设计

总体设计理清了抢票的主要步骤,进一步需要明确每个步骤中需要注意的问题。

1. 登录

登录过程中,自动输入用户名和密码比较简单,难点在于识别验证码。截至目前,各种自动识别验证码的方案准确率都不高,因此,本文采用“人工辅助”识别验证码,即:识别验证码由人工完成,选择图形验证码后点击“登录”。

2. 基本信息填写、查询、预定

整体上没有难点,但需要注意,出发地和目的地可能有多个车次,每个车次有多种席别,乘车方案可能比较复杂,比如:路途较远的情况下,对于 G 字头、D字头列车,二等座及以上可接受;对于 K 字头、T 字头列车,硬卧及以上可接受……。如此,在抢票的时候,需要按优先级轮询各种方案。以杭州 -> 成都为例,有 5 个车次可选,如下所示:

3. 订单信息填写

乘车人列表中可能有多个人的信息(如果你曾经帮别人买过车票的话,注册信息会保留),需要选择正确的乘车人、票种和席别,如下例子所示:

4. 订单确认

这一步很简单,点击“确认”即可,毕竟春运期间抢票,一般不会在意位置,能抢到已是幸运。

准备工作

根据总体设计,可以将抢票程序规划为 5 个主要函数:

  • __init__():初始化

  • login_proc():登录模块

  • filling_proc():基本信息填写模块

  • booking_proc():查询、预订、订单信息填写模块

  • confirm_proc():订单确认模块

1. 浏览器驱动

本文介绍的抢票软件基于 Chrome 浏览器,因此,需要下载与之版本匹配的驱动 chromedriver(附:下载网址)。注意与自己的 Chrome 版本对应,步骤如下:

  • 首先,查看 Chrome 的版本,选择“设置” -> “关于 Chrome”,如下图版本为 66.x。

  • 然后,进入 chromedriver 下载网址,根据 notes.txt 文件提供的信息选择正确版本的驱动。如下图,chromedriver2.38 支持 Chrome 版本为65-67:

2. Selenium 模块准备

Selenium 是一个用于 Web 应用程序自动化测试的工具,可直接运行在浏览器中,模拟真实用户操作。支持的浏览器包括 IE、Mozilla Firefox、Safari、Chrome、Opera 等。由于其功能强大,被广泛应用于网络爬虫的开发,本文将用它作为抢票程序的核心模块(附:下载及安装方法)。

3. 必要信息准备

列车购票官网经过数次改革,出发地、目的地、车次、席别等都不是明文,而是以编码表示,因此,需要提前准备好这些信息。信息获取方法:谷歌浏览器打开 12306 官网购票页面,鼠标右键“查看”可以获取到上述信息,以杭州 -> 成都为例:

    #自定义变量区value_fromstation = '%u676D%u5DDE%2CHZH'  # 始发站(杭州)value_tostation = '%u6210%u90FD%2CCDW'  # 终点站(成都)value_date = '2018-05-10'  # 出发时间username=u"username" # 用户名password="password" # 密码#杭州-成都:车次&席别&预定#车次信息字典,数据分别表示车次、一等座ID、二等座ID、无座ID、对应车次的预定按钮IDtrain_info = {"D2222":[['ZY_56000D222251', 'ZE_56000D222251', 'WZ_56000D222251'], 'ticket_56000D222251'],"D2262":[['ZY_56000D226251', 'ZE_56000D226251', 'WZ_56000D226251'], 'ticket_56000D226251']}

车次、票种编码,主要网页 URL 是固定的,如下:

    #车票类型字典,"学生票"和"普通票"对应的IDticket_type_dict = {'student': '//input[@name="sf" and @id="sf1"]','common': '//input[@name="sf" and @id="sf2"]'}#车次类型字典train_type_dict = {'T': '//input[@name="cc_type" and @value="T"]',  # 特快'G': '//input[@name="cc_type" and @value="G"]',  # 高铁'D': '//input[@name="cc_type" and @value="D"]',  # 动车'Z': '//input[@name="cc_type" and @value="Z"]'}  # 直达#登陆页面urllogin_url = 'https://kyfw.12306.cn/otn/login/init'#个人信息页面urlinitmy_url = "https://kyfw.12306.cn/otn/index/initMy12306"  #订票页面urlbook_url = 'https://kyfw.12306.cn/otn/leftTicket/init'   #乘客选择页面urlconfirm_url = 'https://kyfw.12306.cn/otn/confirmPassenger/initDc'

登陆模块 login_proc() 设计

请参见下面代码:

def __init__(self):"""Info:构造函数,创建一个浏览器对象"""print(u"欢迎使用列车订票工具")self.driver = webdriver.Chrome(self.driver_path)self.driver.implicitly_wait(300)def login_proc(self):"""Info:登陆过程处理函数,其中图形验证码需要手动选择"""self.driver.get(self.login_url)# sign in the user nametry:self.driver.find_element_by_id("username").send_keys(self.username)self.driver.find_element_by_id("password").send_keys(self.password)except Exception  as err:print(u"输入用户名或密码失败!",err)#点击验证码,人工辅助,目前识别图形验证码比较困难,因此选择人工辅助  print(u"请自行选择验证码,点击登陆")while True:if(self.driver.current_url != self.initmy_url):time.sleep(1)else:print('Login finished!')break

基本信息填写模块 filling_proc() 设计

请参见下面代码:

def filling_proc(self,train_type,ticket_type):"""Info:填写起始站,终点站,出发时间,车次类型,车票类型等信息"""print (u'列车类型:', train_type)print (u'车票类型:', ticket_type)# 打开订票网页self.driver.get(self.book_url)# 选择始发站self.driver.add_cookie({"name": "_jc_save_fromStation", "value": self.value_fromstation})# 选择终点站self.driver.add_cookie({"name": "_jc_save_toStation", "value": self.value_tostation})# 选择出发日期self.driver.add_cookie({"name": "_jc_save_fromDate", "value": self.value_date})self.driver.refresh()# 选择车次类型                if (train_type == 'T' or train_type == 'G' or train_type == 'D' or train_type == 'Z'):self.driver.find_element_by_xpath(self.train_type_dict[train_type]).click()else:print (u"车次类型异常或未选择!(train_type=%s)" % train_type)# 选择车票类型if (ticket_type == 'student' or ticket_type == 'common'):self.driver.find_element_by_xpath(self.ticket_type_dict[ticket_type]).click()else:print (u"车票类型异常或未选择!(train_type=%s)" % ticket_type)

查询、预订、订单信息填写模块 booking_proc() 设计

请参见下面代码:

def booking_proc(self,refresh_interval=0):"""Info:订票处理过程,循环查询符合条件的车次,如果存在则点击“预定”"""book_ticket_flag = False# 循环查询while True:time.sleep(refresh_interval)# 点击“查询”按钮,刷新页面开始查询,查询按钮的ID="query_ticket"search_btn = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, '//*[@id="query_ticket"]')))search_btn.click()# 扫描查询结果,根据自定义车次字典train_info提供的信息,逐一查询try:for train in self.train_info:print(u"当前查询车次为:"+train)# 根据车次查询对应的席别:商务,一等,二等,无座等seat_list= self.train_info.get(train)for seat in seat_list[0]:ticket_seat_id = '//*[@id="' + seat + '"]'# 席别IDtic_tb_item = 'default'# 获取车票数量信息:"-","无","数字"tic_tb_item = WebDriverWait(self.driver, 2).until(EC.presence_of_element_located((By.XPATH, ticket_seat_id)))tic_ava_num = tic_tb_item.text# 无票或未开售,则结束当前查询if(tic_ava_num == u'无' or tic_ava_num == u'*'):  continue# 如果车次有票,则点击对应车次的“预定”按钮else:book_ticket_btn = '//*[@id="' + seat_list[1] + '"]/td[13]/a'self.driver.find_element_by_xpath(book_ticket_btn).click()book_ticket_flag = Trueprint(u"开始预定")breakif (book_ticket_flag):breakexcept Exception as err:  print(err)# 网络状态不好的时候,点击查询按钮,可能返回查询结果失败,对此异常可再次点击search_btn.click()if (book_ticket_flag):break

订单确认模块 confirm_proc() 设计

请参见下面代码:

def confirm_proc(self):"""Info:点击“预定”之后,需要确认乘客信息和座位信息"""# 判断页面跳是否转至乘客选择页面      while True:if (self.driver.current_url == self.confirm_url):print (u'页面跳转成功!')breakelse:print (u'等待页面跳转...')time.sleep(1)# 乘车人选择:针对乘车人列表多于一人的情况print(u"选择乘客")while True:try:# 选择乘车人列表中的第二个人self.driver.find_element_by_xpath('//*[@id="normalPassenger_1"]').click()breakexcept Exception as err:print (u'等待常用联系人列表。。。',err)               time.sleep(0.5)try:print(u"提交订票信息")self.driver.find_element_by_xpath('//*[@id="submitOrder_id"]').click()time.sleep(1.5)print(u"确认订票信息")self.driver.find_element_by_xpath('//*[@id="qr_submit_id"]').click()           except Exception as err:print (err)

如需查看完整代码和详细讲解,欢迎订阅专栏。

你的收获

  • 掌握学习编程语言的方法。与市面上那些力求面面俱到、动辄数百页的辅导书不同,本专栏将编程语言的学习路线提炼为基础、中级、高级三层,并依此循序渐进,此路线亦可用于学习其它编程语言。

  • 掌握 Python 编程语言核心概念,包括数据结构、语句、函数、类等。丰富实例助力读者编程实践,快速入门 Python。

  • 掌握高级知识点,包括模块、标准库、文件、流、数据库、网络编程、图形界面等,并具备基于这些知识点编写小应用的能力。

专栏设计

  • 零基础学习,循序渐进

  • 内容全面,提炼要义

  • 理论结合实践,提升学习效率

本专栏的目标是带领读者快速掌握 Python 要义,进而上手实践。专栏分为四个部分:基础篇、中级篇、高级篇以及扩展篇,对于初学者,建议按照顺序阅读。

第一部分:基础篇。从 Python 的起源、现状和前景讲起,首先带领读者搭建开发环境,随后介绍 Python 的基础知识和基本概念,包括列表、元组、字符串、字典以及各种语句,以简练而生动的文字引导读者步入 Python 的世界。

第二部分:中级篇。循序渐进地介绍一些对初学者来说相对难以理解的内容,包括函数、类、继承、函数重写、作用域、参数传递、错误及异常等内容。

第三部分:高级篇。这部分立足于实践,并将介绍一些相对高级的主题,包括模块、标准库、文件、流、数据库、网络编程、图形界面等。通过学习这部分内容,读者将掌握模块的原理和利用标准库将 Python 与数据库、网络、图形界面等工具结合使用。

第四部分:扩展篇。结合实际应用场景,带领读者完成两个小程序的设计和实现。


Python 已被多家平台评选为 2020 年最值得掌握的编程语言第一名!相信精通这门语音,一定会让你在未来获得更大的发展空间!

即日起至 2 月 23 日, 《Python 开发 14 天快速入门》限时特惠!现在订阅本专栏,即可开启 Python 学习之路,还能进群和作者一起交流学习哦~

订阅专栏,即可进群和作者交流

收藏!用 Python 写一个抢票软件相关推荐

  1. 第13课:实战之用 Python 写一个抢票软件

    本文将介绍如何用 Python 语言实现 12306 自动预定列车票,也就是坊间常说的"抢票",但个人觉得,这不算是"抢",只不过是一定程度的自动化. 总体设计 ...

  2. 用python写一个抢票脚本

    写一个抢票脚本需要了解一些网络爬虫的知识,以及如何使用 Python 进行网络请求. 首先,你需要找到票务网站的 API,并了解其请求方式.请求参数等信息.然后,你可以使用 Python 的第三方库如 ...

  3. python抢票软件代码_Python 写一个抢票软件

    总体设计 所谓抢票软件,本质上就是基于浏览器驱动,实现登录.预定.确认信息的自动化.购买列车票涉及4个网页,相应的基本流程如下: 登录:输入用户名.密码,识别验证码,点击"登录": ...

  4. python软件界面-用Python写一个语音播放软件

    原标题:用Python写一个语音播放软件 单位经常使用广播进行临时事项的通知(将文字转换为语音然后通过功放广播),但是市面上多数语音播放软件都是收费的,要么发音失真,要么不够稳定--经常出现莫名其妙的 ...

  5. python编写一个软件-python写一个随机点名软件的实例

    最近有个随机点名软件的需求,故写了一个,上代码:github地址 # -*- coding: utf-8 -*- # @Time : 18-12-31 下午4:21 # @Author : Felix ...

  6. python写软件实例-python写一个随机点名软件的实例

    最近有个随机点名软件的需求,故写了一个,上代码:github地址 # -*- coding: utf-8 -*- # @Time : 18-12-31 下午4:21 # @Author : Felix ...

  7. 给我编写一个抢票软件

    在编写抢票软件之前,你需要先了解抢票的基本原理.抢票软件是通过自动化地发送大量的 HTTP 请求到服务器,试图在票务系统中抢到购票资格. 在编写抢票软件时,你需要考虑以下几个方面: 如何发送 HTTP ...

  8. python语音播报-用Python写一个语音播放软件

    单位经常使用广播进行临时事项的通知(将文字转换为语音然后通过功放广播),但是市面上多数语音播放软件都是收费的,要么发音失真,要么不够稳定--经常出现莫名其妙的故障,容易给工作带来被动.学Python这 ...

  9. python读音播报-用Python写一个语音播放软件

    单位经常使用广播进行临时事项的通知(将文字转换为语音然后通过功放广播),但是市面上多数语音播放软件都是收费的,要么发音失真,要么不够稳定--经常出现莫名其妙的故障,容易给工作带来被动.学Python这 ...

最新文章

  1. 禅道Docker安装包发布
  2. python可以做什么工作好-Python可以做什么工作?Python有哪些方向?
  3. 计算机中的信息表示 ppt模板,计算机中信息的表示.ppt
  4. 关于python3中的包operator(支持函数式编程的包)
  5. 安全,从写第一行代码开始!
  6. 三次多项式曲线php,多项式计算的效率测试,多项式计算效率_PHP教程
  7. html5 字母单词拖拽,HTML5拖拽
  8. ArcGIS制图(一)之流向图
  9. rtmp中flv和flv文件的区别
  10. maya导入abc动画_如此导出ABC缓存,扩展秘籍(二)!!!
  11. splunk 日志分析软件 简介
  12. BAT的校园大赛,都秀出了哪些肌肉?
  13. c语言ans作用,ANS标准定义C语言是什么?
  14. 星期五五–大数据,Doppio和假Linus Torvalds
  15. 如何制作优秀的数据可视化报告(学习篇)
  16. 天翼云打造国云安全品牌 铸牢企业云上安全防线
  17. Nginx源码安装及调优配置(二)
  18. React基础(叁)———事件处理
  19. w7计算机u盘在哪里,win7电脑无法发现u盘怎么解决
  20. MCU基础以及RTOS原理知识分享

热门文章

  1. 2020年招商银行FinTech数据赛道比赛总结
  2. 安卓开发者!Android中高级面试必知必会,详细的Android学习指南
  3. 应届毕业生网上求职攻略
  4. [Python设计模式] 第23章 烤串的哲学——命令模式
  5. 机械3D设计软件快速入门技巧:零件设计功能详解
  6. 国网技术学院MySQL课堂练习
  7. 国产同轴电缆型号分类
  8. MediaPlayer的基本使用-播放音乐/视频
  9. SUSE15不支持ifconfig指令解决方案(切换SUSE15源到阿里源)
  10. 背景资料:GSM与CDMA之比较