提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、程序的整体的架构
  • 二、代码书写
    • 1.UI设计
    • 2.查询模块
    • 3.抢票模块
    • 4.selenium网页买票
  • 总结

前言

随着春节的临近,买票成为一大难题,因此自己写一个简单的买票小程序就变得很有意思了


提示:以下是本篇文章正文内容,下面案例可供参考

一、程序的整体的架构

在书写程序之前,我们应该要先来了解一下我们在12306买票的时候需要进行什么样的流程,我们对车票进行查询后,如果有票,那么我们就进行登录,登录成功后就进行买票了。大概的流程图如下:

有了这个流程图之后,我们就可以对我们的程序进行一些模块的设计了

二、代码书写

1.UI设计

通过designer来设计UI,此次共设计了三个UI

2.查询模块

查询模块的书写需要注意的是,我们用requests拿到地址后,还要有12306查票时需要提交的信息

#12306车票地址
tikes="https://kyfw.12306.cn/otn/leftTicket/query"
#需要提交的信息,用字典进行封装
query_params = {"leftTicketDTO.train_date": tran_date,"leftTicketDTO.from_station": from_station,"leftTicketDTO.to_station": to_station,"purpose_codes": purpose_code
}
如果还是没法拿到数据,那么我们可以在加一个头,将自己伪装成为一个浏览器(我使用的Chrome浏览器)
headrs = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36','Cookie': '_uab_collina=163689535304202042315973; JSESSIONID=C89EB44A4BDC97B8EB9A4E7955FCA94F; route=9036359bb8a8a461c164a04f8f50b252; BIGipServerotn=2280128778.24610.0000; RAIL_EXPIRATION=1637210970420; RAIL_DEVICEID=FBb96qPa1M1EqO-Nj9rP-_kseasv05MVTorGDOCS6EBOT-Dei-zT7_dxLz5I-ktyJJ_ZQeD8pa4BniUQSmBh2bNykBQh_ATcwDA0JEB3yQpJ59wbHORgQ9Y-rzNJoJnVhj2mGRtpgQJno_SPSGDhLreIOTfiZ5mc; guidesStatus=off; highContrastMode=defaltMode; cursorStatus=off; _jc_save_fromStation=%u5317%u4EAC%2CBJP; _jc_save_toStation=%u5929%u6D25%2CTJP; _jc_save_fromDate=2021-11-14; _jc_save_toDate=2021-11-14; _jc_save_wfdc_flag=dc'}

拿到的是json数据,因此我们需要将数据进行解析

resp = requests.get(API.QUERY_tikes, params=query_params, headers=headrs)resp.encoding = resp.apparent_encoding

接下来就是对数据进行简单的处理

items = resp.json()['data']['result']train_dicts = []for item in items:trainDict = {}trainInfo = item.split('|')if trainInfo[11] == 'Y':  # 是否可以预定trainDict['secret_str'] = trainInfo[0]  # 车次密文字符串(下订单时使用)trainDict['train_num'] = trainInfo[2]  # 车次编号 24000000Z30LtrainDict['train_name'] = trainInfo[3]  # 车次名称,如D352trainDict['from_station_code'] = trainInfo[6]  # 出发地电报码trainDict['to_station_code'] = trainInfo[7]  # 目的地地电报码trainDict['from_station_name'] = code2station[trainInfo[6]]  # 出发地名称 手动实现 北京trainDict['to_station_name'] = code2station[trainInfo[7]]  # 目的地名称 手动实现 上海trainDict['start_time'] = trainInfo[8]  # 出发时间trainDict['arrive_time'] = trainInfo[9]  # 到达时间trainDict['total_time'] = trainInfo[10]  # 总用时trainDict['left_ticket'] = trainInfo[12]  # 余票 wrlmtI6BmBd8izygoiCBbpr3%2B%2BGKdIk1SHpJdJ1f6w1p%2FhGFtrainDict['train_date'] = trainInfo[13]  # 火车日期 20190121trainDict['train_location'] = trainInfo[15]  # P4 后期用trainDict["vip_soft_bed"] = trainInfo[21]  # 高级软卧trainDict['other_seat'] = trainInfo[22]  # 其他trainDict["soft_bed"] = trainInfo[23]  # 软卧trainDict["no_seat"] = trainInfo[26]  # 无座trainDict["hard_bed"] = trainInfo[28]  # 硬卧trainDict['hard_seat'] = trainInfo[29]  # 硬座trainDict["second_seat"] = trainInfo[30]  # 二等座trainDict["first_seat"] = trainInfo[31]  # 一等座trainDict["business_seat"] = trainInfo[32]  # 商务座trainDict["move_bed"] = trainInfo[33]  # 动卧

查询模块基本到这就结束了,接下来就是抢票模块

3.抢票模块

在抢票模块中加了一个如果选择了某一座位类型的车次之后,查询模块会筛选数据,具体代码如下:

                if seattype == None:train_dicts.append(trainDict)else:print(seattype)key = config.seat_type_map_dic[seattype]print(key)print(trainDict[key])if trainDict[key] == "有" or trainDict[key].isdigit():train_dicts.append(trainDict)print(trainDict)

完成的查询代码

    def que_titkes(cls, tran_date, from_station, to_station, purpose_code, seattype=None):code2station = APItool.get_all_stations_revers()query_params = {"leftTicketDTO.train_date": tran_date,"leftTicketDTO.from_station": from_station,"leftTicketDTO.to_station": to_station,"purpose_codes": purpose_code}headrs = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36','Cookie': '_uab_collina=163689535304202042315973; JSESSIONID=C89EB44A4BDC97B8EB9A4E7955FCA94F; route=9036359bb8a8a461c164a04f8f50b252; BIGipServerotn=2280128778.24610.0000; RAIL_EXPIRATION=1637210970420; RAIL_DEVICEID=FBb96qPa1M1EqO-Nj9rP-_kseasv05MVTorGDOCS6EBOT-Dei-zT7_dxLz5I-ktyJJ_ZQeD8pa4BniUQSmBh2bNykBQh_ATcwDA0JEB3yQpJ59wbHORgQ9Y-rzNJoJnVhj2mGRtpgQJno_SPSGDhLreIOTfiZ5mc; guidesStatus=off; highContrastMode=defaltMode; cursorStatus=off; _jc_save_fromStation=%u5317%u4EAC%2CBJP; _jc_save_toStation=%u5929%u6D25%2CTJP; _jc_save_fromDate=2021-11-14; _jc_save_toDate=2021-11-14; _jc_save_wfdc_flag=dc'}resp = requests.get(API.QUERY_tikes, params=query_params, headers=headrs)resp.encoding = resp.apparent_encodingitems = resp.json()['data']['result']train_dicts = []for item in items:trainDict = {}trainInfo = item.split('|')if trainInfo[11] == 'Y':  # 是否可以预定trainDict['secret_str'] = trainInfo[0]  # 车次密文字符串(下订单时使用)trainDict['train_num'] = trainInfo[2]  # 车次编号 24000000Z30LtrainDict['train_name'] = trainInfo[3]  # 车次名称,如D352trainDict['from_station_code'] = trainInfo[6]  # 出发地电报码trainDict['to_station_code'] = trainInfo[7]  # 目的地地电报码trainDict['from_station_name'] = code2station[trainInfo[6]]  # 出发地名称 手动实现 北京trainDict['to_station_name'] = code2station[trainInfo[7]]  # 目的地名称 手动实现 上海trainDict['start_time'] = trainInfo[8]  # 出发时间trainDict['arrive_time'] = trainInfo[9]  # 到达时间trainDict['total_time'] = trainInfo[10]  # 总用时trainDict['left_ticket'] = trainInfo[12]  # 余票 wrlmtI6BmBd8izygoiCBbpr3%2B%2BGKdIk1SHpJdJ1f6w1p%2FhGFtrainDict['train_date'] = trainInfo[13]  # 火车日期 20190121trainDict['train_location'] = trainInfo[15]  # P4 后期用trainDict["vip_soft_bed"] = trainInfo[21]  # 高级软卧trainDict['other_seat'] = trainInfo[22]  # 其他trainDict["soft_bed"] = trainInfo[23]  # 软卧trainDict["no_seat"] = trainInfo[26]  # 无座trainDict["hard_bed"] = trainInfo[28]  # 硬卧trainDict['hard_seat'] = trainInfo[29]  # 硬座trainDict["second_seat"] = trainInfo[30]  # 二等座trainDict["first_seat"] = trainInfo[31]  # 一等座trainDict["business_seat"] = trainInfo[32]  # 商务座trainDict["move_bed"] = trainInfo[33]  # 动卧if seattype == None:train_dicts.append(trainDict)else:print(seattype)key = config.seat_type_map_dic[seattype]print(key)print(trainDict[key])if trainDict[key] == "有" or trainDict[key].isdigit():train_dicts.append(trainDict)print(trainDict)return train_dicts

在查询模块中,我把代码封装到了一个类的函数中,因此在抢票模块中,我们定时器设置,只需要定时的执行查询模块,查询模块执行完成后,会对相应的作为数据进行判断,如果有座位,那么就会进入后续的一个登录的界面,输入自己账号和密码后,就进入selenium网页自动进行登录和抢票。

下面是定时器的函数:

self.timer=QTimer(self)
self.timer.timeout.connect(self.buy_tikit)

登录模块的代码:

    def checklogin(self):self.account = self.comboBox.currentText()self.password = self.password_le.text()self.cursor.execute(self.sql, (self.account, self.password))sel_qiang = selinu_qiang.Qiangpiao(self.from_station, self.to_station,         self.depart_time, self.train_num,self.passenger, self.account, self.password)sel_qiang.run()

4.selenium网页买票

该模块我参考的是以下大佬的博客

​​​​​​​ 最新12306抢票爬虫_阿牛的博客-CSDN博客_12306爬虫自动抢票

经过简单的修改和UI传参,实现了多个模块之间的联系

具体的代码如下:

import time
import datetimefrom PyQt5.QtWidgets import QMessageBox
from selenium import webdriver
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver import ChromeOptions
import Login_Pane
class Qiangpiao():def __init__(self,from_station,to_station,depart_time,train_num,passenger,username,password):self.login_url = 'https://kyfw.12306.cn/otn/resources/login.html'self.init_my_url = 'https://kyfw.12306.cn/otn/view/index.html'self.order_url = 'https://kyfw.12306.cn/otn/confirmPassenger/initDc'self.train_num = train_numself.passenger = passengerself.userna=usernameself.password=passwordself.Login_ti=Login_Pane.LoginPane#获取当前月份self.now_month = datetime.date.today().monthprint(self.now_month)def _login(self):option = ChromeOptions()option.add_experimental_option('excludeSwitches', ['enable-automation'])self.driver = webdriver.Chrome(options=option)self.driver.maximize_window()# 使用selenium打开登陆页面self.driver.get('https://kyfw.12306.cn/otn/resources/login.html')script = 'Object.defineProperty(navigator,"webdriver",{get:()=>undefined,});'self.driver.execute_script(script)self.driver.maximize_window()# 谷歌关于验证码的显示位置自己试的时候出现了错位的情况,所以多写了两个跳转刷新的操作time.sleep(3)self.driver.find_element_by_xpath('//*[@id="J-userName"]').send_keys("19102870304")time.sleep(2)self.driver.find_element_by_xpath('//*[@id="J-password"]').send_keys("tlmm123")time.sleep(2)self.driver.find_element_by_xpath('//*[@id="J-login"]').click()time.sleep(3)# 滑块验证span = self.driver.find_element_by_xpath('//*[@id="nc_1_n1z"]')time.sleep(1)action = ActionChains(self.driver)time.sleep(2)action.click_and_hold(span)action.drag_and_drop_by_offset(span, 300, 0).perform()time.sleep(5)# WebDriverWait(self.driver, 1000).until(EC.url_to_be(self.init_my_url))print('登录成功!')def _pop_window(self):time.sleep(1)self.driver.find_element_by_xpath('//*[@class="dzp-confirm"]/div[2]/div[3]/a').click()def _enter_order_ticket(self):action = ActionChains(self.driver)element = self.driver.find_element_by_link_text('车票')# 鼠标移动到 '车票' 元素上的中心点action.move_to_element(element).perform()# 点击'单程'self.driver.find_element_by_xpath('//*[@id="J-chepiao"]/div/div[1]/ul/li[1]/a').click()# 消除第二次弹窗self.driver.find_element_by_link_text('确认').click()def _search_ticket(self):#出发地输入self.driver.find_element_by_id("fromStationText").click()self.driver.find_element_by_id("fromStationText").send_keys(self.from_station)self.driver.find_element_by_id("fromStationText").send_keys(Keys.ENTER)#目的地输入self.driver.find_element_by_id("toStationText").click()self.driver.find_element_by_id("toStationText").send_keys(self.to_station)self.driver.find_element_by_id("toStationText").send_keys(Keys.ENTER)#出发日期输入self.driver.find_element_by_id("date_icon_1").click()#等待查询按钮是否可用WebDriverWait(self.driver,1000).until(EC.element_to_be_clickable((By.ID,"query_ticket")))#执行点击事件search_btn = self.driver.find_element_by_id("query_ticket")search_btn.click()#等待查票信息加载WebDriverWait(self.driver, 1000).until(EC.presence_of_element_located((By.XPATH, '//*[@id="queryLeftTable"]/tr')))def _order_ticket(self):train_num_list = []train_num_ele_list = self.driver.find_elements_by_xpath('//tr/td[1]/div/div[1]/div/a')for t in train_num_ele_list:train_num_list.append(t.text)tr_list = self.driver.find_elements_by_xpath('//*[@id="queryLeftTable"]/tr[not(@datatran)]')if self.train_num in train_num_list:for tr in tr_list:train_num = tr.find_element_by_xpath("./td[1]/div/div[1]/div/a").textif self.train_num == train_num:#动车二等座余票信息text_1 = tr.find_element_by_xpath("./td[4]").text# 火车二等座余票信息text_2 = tr.find_element_by_xpath("./td[8]").textif (text_1 == "有" or text_1.isdigit()) or (text_2 == "有" or text_2.isdigit()):#点击预订按钮order_btn = tr.find_element_by_class_name("btn72")order_btn.click()#等待订票页面WebDriverWait(self.driver,1000).until(EC.url_to_be(self.order_url))# 选定乘车人self.driver.find_element_by_xpath(f'//*[@id="normal_passenger_id"]/li/label[contains(text(),"{self.passenger}")]').click()print("选定乘车人")#如果乘客是学生,对提示点击确定if EC.presence_of_element_located((By.XPATH, '//div[@id="dialog_xsertcj"]')):print("订单提交")# 提交订单# self.driver.find_element_by_id('submitOrder_id').click()time.sleep(2)# 点击确认订单# self.driver.find_element_by_id('qr_submit_id').click()self.Login_ti.tixing(self)else:# 提交订单print("else订单提交")self.driver.find_element_by_id('submitOrder_id').click()self.Login_ti.tixing(self)time.sleep(2)# 点击确认# self.driver.find_element_by_id('qr_submit_id').click()print("购票成功!")breakelse:print("二等座无票!")else:print("无此列车!")def run(self):#登录self._login()#消除登录后(第一次)的弹窗self._pop_window()#进入购票页面self._enter_order_ticket()#查票self._search_ticket()#订票self._order_ticket()#关闭浏览器time.sleep(6)self.driver.quit()if __name__ == '__main__':qiangpiao = Qiangpiao("成都","重庆","2021-12-15","G8502","赵沁","19102870304","tlmm123")qiangpiao.run()

以下代码段是为了避免浏览器识别出我是一个程序的

option = ChromeOptions()option.add_experimental_option('excludeSwitches', ['enable-automation'])self.driver = webdriver.Chrome(options=option)

被识别出来是测试软件后,会出现这样的字样,这个时候是无法进行验证的,但是加上以上代码段后就不会被识别出来了


总结

通过对多个模块的书写和调试,程序终于能够成功运行和买票了,但是还有很多的bug,正在不断的修改中,会在后续进行一些简单的更新

Python查询12306车票和使用selenium进行买票相关推荐

  1. (五)Python查询12306余票:添加票价信息

    前一篇–>docopt实现参数的输入-–查询任意时间任意车站余票 票价的查询根据前面result的解析是无法获取的,票价需要另外发送请求获取. 请求链接: https://kyfw.12306. ...

  2. 带有界面的12306!无限自动查询并购票的脚本!年关买票了吗

    分享记录一个带有GUI界面的12306(默认二等座)无限自动查询并购票的脚本(购票成功发送邮件) from tkinter import * #编写GUI界面 import threading #引入 ...

  3. (六)Python查询12306余票:升级版-----图形化界面代码

    上一篇–>添加票价信息 实现效果: 主要代码观看 12306.py: from tkinter import * import re import tkinter.messagebox as m ...

  4. python查询12306余票_Python脚本实现12306火车票查询系统

    最近我看到看到使用python实现火车票查询,我自己也实现了,感觉收获蛮多的,下面我就把每一步骤都详细给分享出来.(注意使用的是python3) 首先我将最终结果给展示出来: 在cmd命令行执行:py ...

  5. (四)Python查询12306余票:docopt实现参数的输入-----查询任意时间任意车站余票

    前一篇–>漂亮的输出-–prettytable和colorama的使用       在前面的文章中我们能实现很漂亮的输出了,可是只是固定时间固定车站之间的车票信息,在本次中我们使用docopt来 ...

  6. (三)Python查询12306余票:漂亮的输出-----prettytable和colorama的使用

    前一篇–>联网查询余票信息并解析 在前面的代码中我们已经实现了对result结果的解析,并且能把车票信息打印出来了.可是输出的格式很不好控制,这时我们需要使用prettytable来实现将车票信 ...

  7. Python查询12306车次信息

    代码采用python3编写,本来用PyQt5做了一个方便的查询界面,不过这篇文章主要来介绍爬取功能,就不把Qt代码贴出来了,可以先看一下效果.运行后只需输入起始站.终点站和日期就可以看到如下效果,和火 ...

  8. (二)、Python查询12306余票:联网查询余票信息并解析

    前一篇–>实现始发站.终点站和出发日期的合法性检验 以下如果有的变量不明白说明在前面的文章已经介绍了,切记! 首先我们来看看每查询一条信息12306网站返回的信息: 请求链接: https:// ...

  9. (一)Python查询12306余票:实现始发站、终点站和出发日期的合法性检验

    项目介绍首页 1.创建字典info存放查询信息(始发站.终点站.出发日期): info = {'from_station': '','to_station': '','from_date': '' } ...

最新文章

  1. 导入语句 python_Python导入语句说明
  2. 收藏 | 图像识别的可视化解释神经网络
  3. 攻防世界 ——crypto
  4. 去哪儿-08-city-search
  5. php最难,那个PHP中号称最难的‘递归函数’
  6. 细数魅族metal电信版手机3宗罪。
  7. 计算机应用基础 教学工作总结,四年级下期计算机教学工作总结(共3篇)
  8. pip install XXX总是报错,例如:Exception: Traceback (most recent call last):这种错误怎么办?
  9. oliver什么意思java_Oliver是什么意思?
  10. python 因子分析 权重计算_Python与量化多因子——因子权重优化
  11. 自动化测试po模式是什么?自动化测试po分层如何实现?-附详细源码
  12. MMC,SD,TF各是什么
  13. 在线打包app平台以及流程平台分析(AndroidiOS)
  14. Java -- 乒乓球 乒乓弹球游戏
  15. 今日份安利:视频变声的软件有哪些?
  16. Jquery实现淘宝服饰精品案例
  17. Oracle数据库中的级联查询、级联删除、级联更新操作教程
  18. 容器-Docker《二》命令帮助镜像管理
  19. 零知识证明(zero knowledge validation)
  20. 怎么远程计算机控制系统,电脑远程控制怎么弄 电脑进行远程控制详细教程

热门文章

  1. 使用 iview 实现PC端生成推广海报与二维码并下载的功能,基于iview Modal 对话框 与 Carousel 走马灯组件实现
  2. 【SwiftUI模块】0007、SwiftUI新手指引-新手指示-聚光灯介绍说明
  3. 知乎 API v4 整理
  4. 剑桥标准英语教程听力资源1-4级
  5. 李宁Matplotlib视频课程作业
  6. Linux下安装和使用杀毒软件AntiVir(ZZ)
  7. MySQL可重复读隔离级别为何没有解决幻读(MVCC原理简介)
  8. linux下gcc编译的四个步骤,linux:gcc编译程序的四个阶段
  9. UUID去横杠的5种方式
  10. 深入理解java虚拟机-笔记