Python查询12306车票和使用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]  # 车次编号 24000000Z30L
                trainDict['train_name'] = trainInfo[3]  # 车次名称,如D352
                trainDict['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%2FhGF
                trainDict['train_date'] = trainInfo[13]  # 火车日期 20190121
                trainDict['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_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]  # 车次编号 24000000Z30L
                trainDict['train_name'] = trainInfo[3]  # 车次名称,如D352
                trainDict['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%2FhGF
                trainDict['train_date'] = trainInfo[13]  # 火车日期 20190121
                trainDict['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 datetime

from 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_num
        self.passenger = passenger
        self.userna=username
        self.password=password
        self.Login_ti=Login_Pane.LoginPane

        #获取当前月份
        self.now_month = datetime.date.today().month
        print(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").text
                if self.train_num == train_num:
                    #动车二等座余票信息
                    text_1 = tr.find_element_by_xpath("./td[4]").text
                    # 火车二等座余票信息
                    text_2 = tr.find_element_by_xpath("./td[8]").text
                    if (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("购票成功!")
                            break
                    else:
                        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,正在不断的修改中,会在后续进行一些简单的更新

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值