Python查询12306车票和使用selenium进行买票
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、程序的整体的架构
- 二、代码书写
- 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进行买票相关推荐
- (五)Python查询12306余票:添加票价信息
前一篇–>docopt实现参数的输入-–查询任意时间任意车站余票 票价的查询根据前面result的解析是无法获取的,票价需要另外发送请求获取. 请求链接: https://kyfw.12306. ...
- 带有界面的12306!无限自动查询并购票的脚本!年关买票了吗
分享记录一个带有GUI界面的12306(默认二等座)无限自动查询并购票的脚本(购票成功发送邮件) from tkinter import * #编写GUI界面 import threading #引入 ...
- (六)Python查询12306余票:升级版-----图形化界面代码
上一篇–>添加票价信息 实现效果: 主要代码观看 12306.py: from tkinter import * import re import tkinter.messagebox as m ...
- python查询12306余票_Python脚本实现12306火车票查询系统
最近我看到看到使用python实现火车票查询,我自己也实现了,感觉收获蛮多的,下面我就把每一步骤都详细给分享出来.(注意使用的是python3) 首先我将最终结果给展示出来: 在cmd命令行执行:py ...
- (四)Python查询12306余票:docopt实现参数的输入-----查询任意时间任意车站余票
前一篇–>漂亮的输出-–prettytable和colorama的使用 在前面的文章中我们能实现很漂亮的输出了,可是只是固定时间固定车站之间的车票信息,在本次中我们使用docopt来 ...
- (三)Python查询12306余票:漂亮的输出-----prettytable和colorama的使用
前一篇–>联网查询余票信息并解析 在前面的代码中我们已经实现了对result结果的解析,并且能把车票信息打印出来了.可是输出的格式很不好控制,这时我们需要使用prettytable来实现将车票信 ...
- Python查询12306车次信息
代码采用python3编写,本来用PyQt5做了一个方便的查询界面,不过这篇文章主要来介绍爬取功能,就不把Qt代码贴出来了,可以先看一下效果.运行后只需输入起始站.终点站和日期就可以看到如下效果,和火 ...
- (二)、Python查询12306余票:联网查询余票信息并解析
前一篇–>实现始发站.终点站和出发日期的合法性检验 以下如果有的变量不明白说明在前面的文章已经介绍了,切记! 首先我们来看看每查询一条信息12306网站返回的信息: 请求链接: https:// ...
- (一)Python查询12306余票:实现始发站、终点站和出发日期的合法性检验
项目介绍首页 1.创建字典info存放查询信息(始发站.终点站.出发日期): info = {'from_station': '','to_station': '','from_date': '' } ...
最新文章
- 导入语句 python_Python导入语句说明
- 收藏 | 图像识别的可视化解释神经网络
- 攻防世界 ——crypto
- 去哪儿-08-city-search
- php最难,那个PHP中号称最难的‘递归函数’
- 细数魅族metal电信版手机3宗罪。
- 计算机应用基础 教学工作总结,四年级下期计算机教学工作总结(共3篇)
- pip install XXX总是报错,例如:Exception: Traceback (most recent call last):这种错误怎么办?
- oliver什么意思java_Oliver是什么意思?
- python 因子分析 权重计算_Python与量化多因子——因子权重优化
- 自动化测试po模式是什么?自动化测试po分层如何实现?-附详细源码
- MMC,SD,TF各是什么
- 在线打包app平台以及流程平台分析(AndroidiOS)
- Java -- 乒乓球 乒乓弹球游戏
- 今日份安利:视频变声的软件有哪些?
- Jquery实现淘宝服饰精品案例
- Oracle数据库中的级联查询、级联删除、级联更新操作教程
- 容器-Docker《二》命令帮助镜像管理
- 零知识证明(zero knowledge validation)
- 怎么远程计算机控制系统,电脑远程控制怎么弄 电脑进行远程控制详细教程