zoukankan      html  css  js  c++  java
  • Python 实现的12306抢票脚本

    Python12306抢票脚本


    本脚本使用一个类来实现所有代码,大体上分为以下几个模块及其步骤:
    - 初始化对象属性(在抢票前进行的属性初始化,包括初始化浏览器模拟对象,个人信息等)。
    - 建立模拟浏览器,模拟浏览器进行cookie等存储。
    - 验证模块:
        - 获取验证图片到本地
        - 将8个图片坐标位置改装成易于输入的1—8的位置编号,输入对应的位置号
        - 发送请求进行后台校验
    - 登录模块:
        - 输入账号密码,请求服务器
        - 获取apptk授权码
        - 授权通过,成功获取用户信息,将授权信息存储到cookie
    - 获取站点模块:
        - 获取所有站点名称
        - 获取所有站点码
    -  获取余票信息模块:
        - 输入起始站点与乘车时间,请求服务器,查询余票信息
        - 将余票信息进行格式化输出
        - 选择相应车次
    - 订单模块:
        - 注入起始点、日期,车次码信息,提交请求,返回状态信息
        - 获取该车次的详细信息,选择车票类型
        - 获取所有已添加乘客
        - 选择乘车乘客
        - 检查订单信息
        - 确认订单信息,占座成功,下单完成
        - 发送邮件,短信,提醒支付


    以下贴出所有源码,仅供参考,其中发送邮件与发送短信模块所需的参数须自行到相关网站获取。
      

      1 # -*- coding:utf-8 -*-
      2 from urllib import request
      3 from json import loads
      4 from prettytable import PrettyTable
      5 from colorama import init, Fore, Back, Style
      6 from email.mime.text import MIMEText
      7 from qcloudsms_py import SmsSingleSender
      8 from qcloudsms_py.httpclient import HTTPError
      9 import urllib.parse as parse
     10 import http.cookiejar as cookiejar
     11 import re
     12 import time
     13 import smtplib
     14 # 200 32
     15 # 150 23
     16 # 70  10
     17 init(autoreset=False)
     18 class Colored(object):
     19     #  前景色:红色  背景色:默认
     20     def red(self, s):
     21         return Fore.LIGHTRED_EX + s + Fore.RESET
     22     #  前景色:绿色  背景色:默认
     23     def green(self, s):
     24         return Fore.LIGHTGREEN_EX + s + Fore.RESET
     25     def yellow(self, s):
     26         return Fore.LIGHTYELLOW_EX + s + Fore.RESET
     27     def white(self, s):
     28         return Fore.LIGHTWHITE_EX + s + Fore.RESET
     29     def blue(self, s):
     30         return Fore.LIGHTBLUE_EX + s + Fore.RESET
     31 
     32 class train_ticket_purchase():
     33     """
     34         初始化对象属性
     35     """
     36     def __init__(self):
     37         self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'}
     38         self.opener = self.__get_opener()
     39         self.username = ""
     40         self.phone_number = "13781206061"#用于占座成功接收短信
     41         self.receive_email = "wsyjlly@foxmail.com"#用于占座成功接收邮件
     42         self.seatType = "1"         #1硬座
     43         self.seat_types_code = ["M","0","1","N","2","3","4","F","6","9"]
     44         self.ticketType = "1"       #成人票
     45         self.query_seats_count = 1  #查票次数
     46         self.passengers_name = ""   #乘车人字符串
     47         self.seat_list = ["yz_num","wz_num","rz_num","yw_num","rw_num", "dw_num", "gr_num","ze_num","zy_num", "swz_num"]
     48         self.ticketTypes = {"1":"成人票","2":"儿童票","3":"学生票","4":"残军票"}
     49         self.seatTypes = {
     50             "M":"一等座",
     51             "0":"二等座",
     52             "1":"硬座",
     53             "N":"无座",
     54             "2":"软座",
     55             "3":"硬卧",
     56             "4":"软卧",
     57             "F":"动卧",
     58             "6":"高等软卧",
     59             "9":"商务座"
     60         }
     61         self.seat_dict = {
     62             "yz_num":"硬座",
     63             "wz_num":"无座",
     64             "rz_num":"软座",
     65             "yw_num":"硬卧",
     66             "rw_num":"软卧",
     67             "dw_num":"动卧",
     68             "gr_num":"高级软卧",
     69             "ze_num":"二等座",
     70             "zy_num":"一等座",
     71             "swz_num":"商务特等座"
     72         }
     73 
     74 
     75 
     76 
     77 
     78 
     79 
     80 
     81 
     82 
     83     """
     84         建立模拟浏览器,模拟浏览器进行cookie存储
     85     """
     86     def __get_opener(self):
     87         c = cookiejar.LWPCookieJar()
     88         cookie = request.HTTPCookieProcessor(c)
     89         opener = request.build_opener(cookie)
     90         request.install_opener(opener)
     91         return opener
     92 
     93 
     94 
     95 
     96 
     97 
     98 
     99 
    100 
    101 
    102 
    103     """
    104         验证模块:
    105             1、获取验证图片到本地
    106             2、将8个图片坐标位置改装成易于输入的1—8的位置编号,输入对应的位置号
    107             3、发送请求进行后台校验
    108     """
    109     # 获取验证图片到本地
    110     def get_image(self):
    111         req_catch_image = request.Request('https://kyfw.12306.cn/passport/captcha/captcha-image')
    112         req_catch_image.headers = self.headers
    113         code_file = self.opener.open(req_catch_image).read()  # 此时为浏览器的open而不再是request.urlopen,下同
    114         with open('/code.jpg', 'wb')as f:
    115             f.write(code_file)
    116     # 图片校验
    117     def verify(self):
    118         answer = {
    119             "1": "40,40",
    120             "2": "110,40",
    121             "3": "180,40",
    122             "4": "260,40",
    123             "5": "40,120",
    124             "6": "110,120",
    125             "7": "180,120",
    126             "8": "260,120",
    127         }
    128         print("+----------+----------+----------+----------+")
    129         print("|    1     |    2     |    3     |    4     |")
    130         print("|----------|----------|----------|----------|")
    131         print("|    5     |    6     |    7     |    8     |")
    132         print("+----------+----------+----------+----------+")
    133         input_code = input("请在1—8中选择输入验证图片编号,以半角','隔开。(例如:1,3,5):")
    134         answer_code = ""
    135         try:
    136             for i in input_code.split(","):
    137                 answer_code += ("," + answer[i]) if (i is not input_code[0]) else answer[i]
    138         except:
    139             print("输入有误,请重新输入!")
    140             self.verify()
    141         # 进行图片验证码验证
    142         req_check = request.Request('https://kyfw.12306.cn/passport/captcha/captcha-check')
    143         req_check.headers = self.headers
    144         data = {
    145             'answer': answer_code,
    146             'login_site': 'E',
    147             'rand': 'sjrand'
    148         }
    149         data = parse.urlencode(data).encode()
    150         # 返回验证结果
    151         check_result = self.opener.open(req_check, data=data).read().decode()  # 读取出来是byts格式,转换为‘utf-8(默认)
    152         return loads(check_result)
    153     # 验证系统
    154     def sys_verify(self):
    155         self.get_image()
    156         verify_result = self.verify()
    157         while verify_result['result_code'] is not '4':
    158             print('验证失败,已重新下载图片,请重新验证!')
    159             self.get_image()
    160             verify_result = self.verify()
    161         print("验证通过!")
    162         return
    163 
    164 
    165 
    166 
    167 
    168 
    169 
    170 
    171 
    172 
    173     """
    174         登录模块:
    175             1、输入账号密码,请求服务器
    176             2、获取apptk授权码
    177             3、授权通过,成功获取用户信息,将授权信息存储到cookie
    178     """
    179     def login(self):
    180         req_login = request.Request('https://kyfw.12306.cn/passport/web/login')
    181         req_login.headers = self.headers
    182         name = input("请输入12306帐号:")
    183         pwd = input("请输入密码:")
    184         data = {
    185             'username': name,
    186             'password': pwd,
    187             'appid': 'otn'
    188         }
    189         data = parse.urlencode(data).encode()
    190         # 返回登录结果
    191         login_result = self.opener.open(req_login, data=data).read().decode()
    192         return loads(login_result)
    193     def get_tk(self):
    194         req = request.Request('https://kyfw.12306.cn/passport/web/auth/uamtk')
    195         req.headers = self.headers
    196         data = {
    197             "appid": "otn"
    198         }
    199         data = parse.urlencode(data).encode()
    200         # 返回登录结果
    201         result = self.opener.open(req, data=data).read().decode()
    202         return loads(result)
    203     def auth(self,newapptk):
    204         req = request.Request('https://kyfw.12306.cn/otn/uamauthclient')
    205         req.headers = self.headers
    206         data = {
    207             "tk": newapptk
    208         }
    209         data = parse.urlencode(data).encode()
    210         # 返回登录结果
    211         result = self.opener.open(req, data=data).read().decode()
    212         return loads(result)
    213     # 登陆系统
    214     def sys_login(self):
    215         self.login()
    216         result = self.get_tk()
    217         try:
    218             result = self.auth(result['newapptk'])
    219         except:
    220             print("登录失败,账号或密码错误!")
    221             self.sys_verify()
    222             self.sys_login()
    223         self.username = result["username"]
    224         print("欢迎用户",result["username"], "您已登录成功!") if result["result_code"]==0 else print(result["result_message"])
    225         return
    226 
    227 
    228 
    229 
    230 
    231 
    232 
    233 
    234 
    235 
    236     """
    237         获取站点模块:
    238             获取所有站点名称与站点码
    239     """
    240     def __get_city_result(self):
    241         req_city_code = request.Request(
    242             'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9093')
    243         req_city_code.headers = self.headers
    244         result = self.opener.open(req_city_code).read().decode()
    245         return result
    246     def get_city_code(self,name):
    247         result = self.__get_city_result()
    248         start = result.index(name)+len(name)
    249         result = result[start+1:start+4]
    250         # print(result)
    251         return result
    252     def get_station_names(self):
    253         result = self.__get_city_result()
    254         stations = re.findall(r'([u4e00-u9fa5]+)|([A-Z]+)', result)
    255         station_codes = dict(stations)
    256         station_names = dict(zip(station_codes.values(), station_codes.keys()))
    257         return station_names
    258 
    259 
    260 
    261 
    262 
    263 
    264 
    265 
    266     """
    267         获取余票信息模块:
    268             1、输入起始站点与乘车时间,请求服务器,查询余票信息
    269             2、将余票信息进行格式化输出
    270             3、选择相应车次
    271     """
    272     def get_tickets(self,from_station, to_station, train_date):
    273         url = 'https://kyfw.12306.cn/otn/leftTicket/queryX?'
    274         data = {
    275             "leftTicketDTO.train_date": train_date,
    276             "leftTicketDTO.from_station": from_station,
    277             "leftTicketDTO.to_station": to_station,
    278             "purpose_codes": "ADULT"
    279         }
    280         req = request.Request(url + parse.urlencode(data))
    281         req.headers = self.headers
    282         result = self.opener.open(req).read().decode()
    283         return loads(result)
    284     def get_ticket_format(self,from_station_name,from_station,to_station_name,to_station,train_date):
    285         print('为您查询到从', from_station_name, '', to_station_name, '的余票信息如下:')
    286         result = self.get_tickets(from_station, to_station, train_date)
    287         result_list = result['data']['result']
    288 
    289         station_names = self.get_station_names()
    290         table = PrettyTable(
    291             ["车次", "出发/到达车站", "出发/到达时间", "历时", "商务座", "一等座", "二等座", "高级软卧", "软卧", "动卧", "硬卧", "软座", "硬座", "无座", "其他",
    292              "备注"])
    293         for item in result_list:
    294             name = [
    295                 "station_train_code",
    296                 "from_station_name",
    297                 'start_time',
    298                 "lishi",
    299                 "swz_num",
    300                 "zy_num",
    301                 "ze_num",
    302                 "gr_num",
    303                 "rw_num",
    304                 "dw_num",
    305                 "yw_num",
    306                 "rz_num",
    307                 "yz_num",
    308                 "wz_num",
    309                 "qt_num",
    310                 "note_num"
    311             ]
    312             data = {
    313                 "station_train_code": '',
    314                 "from_station_name": '',
    315                 "to_station_name": '',
    316                 'start_time': '',
    317                 'end': '',
    318                 "lishi": '',
    319                 "swz_num": '',
    320                 "zy_num": '',
    321                 "ze_num": '',
    322                 "dw_num": '',
    323                 "gr_num": '',
    324                 "rw_num": '',
    325                 "yw_num": '',
    326                 "rz_num": '',
    327                 "yz_num": '',
    328                 "wz_num": '',
    329                 "qt_num": '',
    330                 "note_num": ''
    331             }
    332             item = item.split('|')  # 用"|"分割字符串
    333             data['station_train_code'] = item[3]  # 车次在3号位置
    334             data['from_station_name'] = item[6]  # 始发站信息在6号位置
    335             data['to_station_name'] = item[7]  # 终点站信息在7号位置
    336             data['start_time'] = item[8]  # 出发时间信息在8号位置
    337             data['arrive_time'] = item[9]  # 抵达时间在9号位置
    338             data['lishi'] = item[10]  # 经历时间在10号位置
    339             data['swz_num'] = item[32] or item[25]  # 特别注意:商务座在32或25位置
    340             data['zy_num'] = item[31]  # 一等座信息在31号位置
    341             data['ze_num'] = item[30]  # 二等座信息在30号位置
    342             data['gr_num'] = item[21]  # 高级软卧信息在31号位置
    343             data['rw_num'] = item[23]  # 软卧信息在23号位置
    344             data['dw_num'] = item[27]  # 动卧信息在27号位置
    345             data['yw_num'] = item[28]  # 硬卧信息在28号位置
    346             data['rz_num'] = item[24]  # 软座信息在24号位置
    347             data['yz_num'] = item[29]  # 硬座信息在29号位置
    348             data['wz_num'] = item[26]  # 无座信息在26号位置
    349             data['qt_num'] = item[22]  # 其他信息在22号位置
    350             data['note_num'] = item[1]  # 备注在1号位置
    351 
    352             color = Colored()  # 创建Colored对象
    353             data["note_num"] = color.white(item[1])
    354             # 如果没有信息用'-'代替
    355             for pos in name:
    356                 if data[pos] == '':
    357                     data[pos] = '-'
    358             tickets = []
    359             cont = []
    360             cont.append(data)
    361             for x in cont:
    362                 tmp = []
    363                 for y in name:
    364                     if y == "from_station_name":
    365                         s = color.green(station_names[data['from_station_name']]) + '
    ' + color.red(
    366                             station_names[data["to_station_name"]])
    367                         tmp.append(s)
    368                     elif y == "start_time":
    369                         s = color.green(data['start_time']) + '
    ' + color.red(data["arrive_time"])
    370                         tmp.append(s)
    371                     elif y == "station_train_code":
    372                         s = color.blue(data['station_train_code'])
    373                         tmp.append(s)
    374                     else:
    375                         tmp.append(data[y])
    376                 tickets.append(tmp)
    377             for ticket in tickets:
    378                 table.add_row(ticket)
    379         print(table)
    380     def get_secret_str(self,from_station, to_station, train_date):
    381         secret_str = {}
    382         result = self.get_tickets(from_station, to_station, train_date)
    383         result = result['data']['result']
    384         for item in result:
    385             msg = item.split("|")
    386             secret_str[msg[3]] = parse.unquote(msg[0])
    387         # print(secret_str)
    388         return secret_str
    389     def get_seats(self,station_train_code,from_station, to_station, train_date):
    390         seats = {}
    391         result = self.get_tickets(from_station, to_station, train_date)
    392         result = result['data']['result']
    393         for item in result:
    394             item = item.split("|")
    395             if item[3] == station_train_code :
    396                 seats['swz_num'] = item[32] or item[25]  # 特别注意:商务座在32或25位置
    397                 seats['zy_num'] = item[31]  # 一等座信息在31号位置
    398                 seats['ze_num'] = item[30]  # 二等座信息在30号位置
    399                 seats['gr_num'] = item[21]  # 高级软卧信息在31号位置
    400                 seats['rw_num'] = item[23]  # 软卧信息在23号位置
    401                 seats['dw_num'] = item[27]  # 动卧信息在27号位置
    402                 seats['yw_num'] = item[28]  # 硬卧信息在28号位置
    403                 seats['rz_num'] = item[24]  # 软座信息在24号位置
    404                 seats['yz_num'] = item[29]  # 硬座信息在29号位置
    405                 seats['wz_num'] = item[26]  # 无座信息在26号位置
    406         return seats
    407     def select_order_details(self):
    408         print("座位码对照表:")
    409         print("-----------------------")
    410         print("|  序号 |  座位类型   |")
    411         print("|   M   |   一等座    |")
    412         print("|   0   |   二等座    |")
    413         print("|   1   |    硬座     |")
    414         print("|   N   |    无座     |")
    415         print("|   2   |    软座     |")
    416         print("|   3   |    硬卧     |")
    417         print("|   4   |    软卧     |")
    418         print("|   F   |    动卧     |")
    419         print("|   6   |  高级软卧   |")
    420         print("|   9   |   商务座    |")
    421         print("-----------------------")
    422         seatType = input("请选择车座类型,enter键默认硬座(例如:1):")
    423         if seatType == '':
    424             self.seatType = "1"
    425         elif seatType in self.seat_types_code:
    426             self.seatType = seatType
    427         else :
    428             raise Exception("没有对应的车座类型!")
    429 
    430         print("车票类型对照表:")
    431         print("-----------------------")
    432         print("|  序号 |  座位类型  |")
    433         print("|   1   |   成人票   |")
    434         print("|   2   |   儿童票   |")
    435         print("|   3   |   学生票   |")
    436         print("|   4   |   残军票   |")
    437         print("-----------------------")
    438 
    439         ticketType = input("请选择车票类型,enter键默认成人票(例如:1):")
    440         self.ticketType = ticketType if seatType != '' else "1"
    441 
    442         passengers_name = input("请输入乘车人姓名,如有多人,请以英文','隔开(例如:晏沈威,晏文艳):")
    443         self.passengers_name = passengers_name if passengers_name!='' else '晏沈威'
    444 
    445         email = input("请输入发送提醒的邮箱(例如:wsyjlly@foxmai.com):")
    446         self.receive_email = email if email!='' else "wsyjlly@foxmail.com"
    447 
    448         phone_number = input("请输入发送提醒的手机号(例如:13781206061):")
    449         self.phone_number = phone_number if phone_number!='' else "13781206061"
    450     def query_ticket(self,seats,seat_msg):
    451         if ((seats[seat_msg] == "") | (seats[seat_msg] == "")):
    452             print("",self.seat_dict[seat_msg],"座位!")
    453             return False
    454         else:
    455             print("查询到",seats[seat_msg], self.seat_dict[seat_msg], "座位!")
    456             return True
    457     def sys_seek_tickets(self):
    458         while True:
    459             from_station_name = "郑州"
    460             from_station_name = input("出发站点(例:郑州):")
    461 
    462             to_station_name = "开封"
    463             to_station_name = input("到达站点(例:开封):")
    464 
    465             train_date = "2019-02-28"
    466             train_date = (input("乘车日期(例:2019-02-25):"))
    467 
    468             print("正在为您查询余票信息,请稍等...")
    469             from_station = self.get_city_code(from_station_name)
    470             to_station = self.get_city_code(to_station_name)
    471 
    472             self.get_ticket_format(from_station_name,from_station,to_station_name,to_station,train_date)
    473             if input("输入'1'可继续查询,输入enter键选择车次!")!="1": break
    474 
    475         station_train_code = "K464"
    476         station_train_code = input("乘车车次(例:K464):")
    477 
    478         # 选择座位类型与车票类型与乘车人姓名
    479         self.select_order_details()
    480 
    481         while True:
    482             seats = self.get_seats(station_train_code, from_station, to_station, train_date)
    483             print('第{}次查票!'.format(self.query_seats_count),seats)
    484             if(self.seatType=="1"):
    485                 if self.query_ticket(seats,"yz_num")==True :break
    486             elif(self.seatType=="N"):
    487                 if self.query_ticket(seats,"wz_num")==True :break
    488             elif(self.seatType=="2"):
    489                 if self.query_ticket(seats,"rz_num")==True :break
    490             elif(self.seatType=="3"):
    491                 if self.query_ticket(seats,"yw_num")==True :break
    492             elif(self.seatType=="4"):
    493                 if self.query_ticket(seats,"rw_num")==True :break
    494             elif(self.seatType=="6"):
    495                 if self.query_ticket(seats,"gr_num")==True :break
    496             elif(self.seatType=="0"):
    497                 if self.query_ticket(seats,"ze_num")==True :break
    498             elif(self.seatType=="M"):
    499                 if self.query_ticket(seats,"zy_num")==True :break
    500             elif(self.seatType=="F"):
    501                 if self.query_ticket(seats,"dw_num")==True :break
    502             elif(self.seatType=="9"):
    503                 if self.query_ticket(seats,"swz_num")==True :break
    504             else:
    505                 raise Exception("没有相应车次!")
    506                 break
    507             self.query_seats_count+=1
    508             time.sleep(2)
    509 
    510         # 获取相应车次的secret_str
    511         secret_str = self.get_secret_str(from_station, to_station, train_date)[station_train_code]
    512         # print(secret_str)
    513         result = {}
    514         result["from_station"]=from_station
    515         result["to_station"]=to_station
    516         result["train_date"]=train_date
    517         result["secret_str"]=secret_str
    518         return result
    519 
    520 
    521 
    522 
    523 
    524 
    525 
    526 
    527     """
    528         订单模块:
    529             1、注入起始点、日期,车次码信息,提交请求,返回状态信息
    530             2、获取该车次的详细信息,选择车票类型
    531             3、获取所有已添加乘客
    532             4、选择乘车乘客
    533             5、检查订单信息
    534             6、确认订单信息,占座成功,下单完成
    535             7、发送邮件,短信,提醒支付
    536     """
    537     # {'validateMessagesShowId': '_validatorMessage', 'status': True, 'httpstatus': 200, 'data': 'N', 'messages': [], 'validateMessages': {}}
    538     def get_train_number(self,tickets):
    539         secret_str = parse.unquote(tickets["secret_str"])
    540         from_station = tickets["from_station"]
    541         to_station = tickets["to_station"]
    542         train_date = tickets["train_date"]
    543         req = request.Request('https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest')
    544         req.headers = self.headers
    545         data = {
    546             "secretStr": secret_str,
    547             "train_date": train_date,
    548             "back_train_date": "",
    549             "tour_flag": "dc",
    550             "purpose_codes": "ADULT",
    551             "query_from_station_name": from_station,
    552             "query_to_station_name": to_station,
    553             "undefined": "",
    554         }
    555         data = parse.urlencode(data).encode()
    556         result = self.opener.open(req, data=data).read().decode()
    557         return loads(result)
    558     # 获取相应车次的信息
    559     def get_train_number_msg(self):
    560         req = request.Request('https://kyfw.12306.cn/otn/confirmPassenger/initDc')
    561         req.headers = self.headers
    562         data = {
    563             "_json_att": ""
    564         }
    565         data = parse.urlencode(data).encode()
    566         # 返回登录结果
    567         result = self.opener.open(req, data=data).read().decode()
    568         try:
    569             ticketInfoForPassengerForm = re.findall("var ticketInfoForPassengerForm=(.*?);", result)[0].replace("'", '"')
    570             globalRepeatSubmitToken = re.findall("globalRepeatSubmitToken = '(.*?)'", result)[0]
    571             key_check_isChange = re.findall("'key_check_isChange':'(.*?)'", result)[0]
    572         except:
    573             raise Exception("没有获取到车次信息!")
    574         ticketInfoForPassengerForm = loads(ticketInfoForPassengerForm)
    575         leftDetails = ticketInfoForPassengerForm["leftDetails"]
    576         leftTicketStr = ticketInfoForPassengerForm["leftTicketStr"]
    577         purpose_codes = ticketInfoForPassengerForm["queryLeftTicketRequestDTO"]["purpose_codes"]
    578         train_location = ticketInfoForPassengerForm["train_location"]
    579         print("该车次剩余车票详情如下:")
    580         for item in leftDetails:
    581             print("	",item)
    582         msg_order_finally_submit = {}
    583         msg_order_finally_submit["purpose_codes"] = purpose_codes
    584         msg_order_finally_submit["key_check_isChange"] = key_check_isChange
    585         msg_order_finally_submit["leftTicketStr"] = leftTicketStr
    586         msg_order_finally_submit["train_location"] = train_location
    587         msg_order_finally_submit["token"] = globalRepeatSubmitToken
    588 
    589         return msg_order_finally_submit
    590     # 获取所有已添加乘客
    591     def get_passengers(self,token):
    592         req = request.Request('https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs')
    593         req.headers = self.headers
    594         data = {
    595             "_json_att": "",
    596             "REPEAT_SUBMIT_TOKEN": token
    597         }
    598         data = parse.urlencode(data).encode()
    599         # 返回登录结果
    600         result = self.opener.open(req, data=data).read().decode()
    601         result = loads(result)
    602         normal_passengers = result["data"]["normal_passengers"]
    603         result = {}
    604         # print("已添加的乘车人如下:")
    605         for passenger in normal_passengers:
    606             result[passenger["passenger_name"]] = passenger
    607             # if passenger != normal_passengers[len(normal_passengers) - 1]:
    608             #     print(passenger["passenger_name"] + ",", end='')
    609             # else:
    610             #     print(passenger["passenger_name"])
    611         return result
    612     # 选择乘车人
    613     def select_passenger(self,passengers):
    614         ps = self.passengers_name
    615         oldPassengerStr = ''
    616         passengerTicketStr = ''
    617         seatType = 1 if self.seatType =="N" else self.seatType
    618         try:
    619             ps = ps.split(",")
    620             for p in ps:
    621                 oldPassengerStr += passengers[p]["passenger_name"] + "," + 
    622                                    passengers[p]["passenger_id_type_code"] + "," + 
    623                                    passengers[p]["passenger_id_no"] + "," + 
    624                                    passengers[p]["passenger_type"] + "_"
    625                 # seatType 座位类型:硬座1软座2硬卧3软卧4
    626                 # passenger_flag 乘客标记:0
    627                 # ticketType 车票类型: 成人票1儿童票2学生票3残军票4
    628                 # passenger_name 乘客姓名
    629                 # passenger_id_type_code 证件类型 中国居民身份证1
    630                 # passenger_id_no 身份证号
    631                 # mobile_no 手机号
    634                 ticketStr = "{},{},{},{},{},{},{},N".format(seatType,
    635                                                             passengers[p]["passenger_flag"],
    636                                                             self.ticketType,
    637                                                             passengers[p]["passenger_name"],
    638                                                             passengers[p]["passenger_id_type_code"],
    639                                                             passengers[p]["passenger_id_no"],
    640                                                             passengers[p]["mobile_no"])
    641                 passengerTicketStr += ticketStr + '_' if p != ps[len(ps) - 1] else ticketStr
    642         except:
    643             print("输入有误!")
    644         result = {}
    645         result["oldPassengerStr"] = oldPassengerStr
    646         result["passengerTicketStr"] = passengerTicketStr
    647         return result
    648     # 检查订单信息
    649     def order_submit(self,msg_passenger, token):
    650         req = request.Request('https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo')
    651         req.headers = self.headers
    652         data = {
    653             "cancel_flag": "2",
    654             "bed_level_order_num": "000000000000000000000000000000",
    655             "passengerTicketStr": msg_passenger["passengerTicketStr"],
    656             "oldPassengerStr": msg_passenger["oldPassengerStr"],
    657             "tour_flag": "dc",
    658             "randCode": "",
    659             "whatsSelect": "1",
    660             "_json_att": "",
    661             "REPEAT_SUBMIT_TOKEN": token
    662         }
    663         data = parse.urlencode(data).encode()
    664         # 返回登录结果
    665         result = self.opener.open(req, data=data).read().decode()
    666         return loads(result)
    667     # 确认订单
    668     def order_ensure(self,msg_passenger,train_number_msg):
    669         purpose_codes = train_number_msg["purpose_codes"]
    670         key_check_isChange = train_number_msg["key_check_isChange"]
    671         leftTicketStr = train_number_msg["leftTicketStr"]
    672         train_location = train_number_msg["train_location"]
    673         token = train_number_msg["token"]
    674         req = request.Request('https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue')
    675         req.headers = self.headers
    676         data = {
    677             "passengerTicketStr": msg_passenger["passengerTicketStr"],
    678             "oldPassengerStr": msg_passenger["oldPassengerStr"],
    679             "randCode": "",
    680             "purpose_codes": purpose_codes,
    681             "key_check_isChange": key_check_isChange,
    682             "leftTicketStr": leftTicketStr,
    683             "train_location": train_location,
    684             "choose_seats": "",
    685             "seatDetailType": "000",
    686             "whatsSelect": "1",
    687             "roomType": "00",
    688             "dwAll": "N",
    689             "_json_att": "",
    690             "REPEAT_SUBMIT_TOKEN": token
    691         }
    692         data = parse.urlencode(data).encode()
    693         # 返回登录结果
    694         result = self.opener.open(req, data=data).read().decode()
    695         return loads(result)
    696     # 发送email
    697     def send_email(self):
    698         # 第三方SMTP服务
    699         mail_host = "smtp.qq.com"
    700         mail_user = "*******@foxmail.com"
    701         mail_pass = "****************"
    702 
    703         sender = "wsyjlly@foxmail.com"
    704         receiver = self.receive_email
    705 
    706         message = MIMEText("席位已锁定,快去支付!")
    707         message["From"] = sender
    708         message["To"] = receiver
    709         message["Subject"] = "Python 12306 抢票!"
    710         try:
    711             server = smtplib.SMTP()
    712             server.connect(mail_host)
    713             server.login(mail_user, mail_pass)
    714             server.sendmail(sender, receiver, message.as_string())
    715             server.close()
    716             print("邮件发送成功,已提醒用户",receiver,"付款!")
    717         except Exception as e:
    718             print("邮件发送失败!", e)
    719     # 发送短信
    720     def send_short_message(self):
    721         name = self.username
    722         phone_number = self.phone_number
    723         seat_type = self.seatTypes[self.seatType]
    724         ticketType = self.ticketTypes[self.ticketType]
    725         appid = 1400******  # SDK AppID是1400开头
    726         appkey = "********************************"
    727         phone_numbers = [phone_number]
    728         # phone_numbers = ["13781206061", "18337735150", "15660039893"]
    729         template_id = ******
    730         sms_sign = "简单点网"
    731 
    732         ssender = SmsSingleSender(appid, appkey)
    733         params = [name,ticketType,seat_type]
    734         try:
    735             result = ssender.send_with_param(86, phone_numbers[0], template_id, params, sign=sms_sign, extend="",ext="")
    736         except HTTPError as e:
    737             print(e)
    738         except Exception as e:
    739             print(e)
    740         # print(result)
    741         if result["errmsg"] == "OK":
    742             print("短信发送成功,已提醒用户", name, "付款!")
    743     def sys_order(self,tickets):
    744         # 1、注入起始点、日期,车次码信息,提交请求,返回状态信息
    745         result = self.get_train_number(tickets)
    746         if result["status"]==True :print("查询车次信息成功!")
    747         # 2、获取该车次的详细信息
    748         train_number_msg = self.get_train_number_msg()
    749         # 3、获取乘客信息
    750         passengers = self.get_passengers(train_number_msg["token"])
    751         # 4、选择乘客
    752         msg_passenger = self.select_passenger(passengers)
    753         # print(msg_passenger)
    754         # 5、下单
    755         result = self.order_submit(msg_passenger, train_number_msg["token"])
    756         if result["status"] == True :print("检查订单信息正确,即将确认订单!")
    757 
    758         print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))
    759         # 6、确认订单
    760         result = self.order_ensure(msg_passenger, train_number_msg)
    761         if result["status"] == True :
    762             print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())), ":下单成功,已占座,请尽快付款!")
    763             self.send_email()
    764             self.send_short_message()
    765         # print(result)
    766         input("按任意键继续!")
    767 
    768 
    769 
    770 
    771 
    772 
    773 
    774 
    775 
    776 
    777 
    778     def run(self):
    779         # 验证码验证
    780         self.sys_verify()
    781         # 登录
    782         self.sys_login()
    783         # 查余票
    784         tickets = self.sys_seek_tickets()
    785         # 下订单
    786         self.sys_order(tickets)
    787 
    788 
    789 
    790 
    791 
    792 
    793 
    794 
    795 
    796 
    797 
    798 
    799 if __name__ == '__main__':
    800     while True:
    801         train_ticket_purchase().run()
  • 相关阅读:
    9.8
    9.6
    9.5
    树状数组
    逆序对
    tab标签切换(无炫效果,简单的显示隐藏)
    JQuery 的选择器
    简单的JQuery top返回顶部
    Hello Word!
    java Data 计算自己活了多少天
  • 原文地址:https://www.cnblogs.com/wsyjlly/p/10490175.html
Copyright © 2011-2022 走看看