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']]) + '\n' + 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']) + '\n' + 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("\t",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()

转载于:https://www.cnblogs.com/wsyjlly/p/10490175.html

Python 实现的12306抢票脚本相关推荐

  1. python编程实践(3):python+selenium实现12306抢票脚本

    又到了一年一度的春运时节,抢个票? 1.设计思路 如果我们要买一张火车票,我们会怎么做?打开12306,登陆,输入出发地和目的地,选择出行日期,然后点击查询,有余票的话就下单购买,没有票就点刷新或者等 ...

  2. python+selenium实现12306抢票

    python+selenium实现12306抢票 一.准备工作 1.要先下载相关的包,selenium.interval.最好使用国内清华源 pip install (which package) - ...

  3. Python版实现12306抢票功能,真的能帮你抢到春运回家的票吗?

    背景 每逢佳节倍思亲,年关将近,思乡的情绪是不是愈发强烈了,筒子们是不是又要准备开始抢票了,还是在找黄牛吗?但是,今年在考虑是否能抢到票以外,还需要考虑是否能回得去,没错,因为疫情,需要全国人民同心协 ...

  4. python3+selenium实现12306抢票脚本

    文章目录 一. 环境配置 windows环境 mac.linux环境 二. 实现思路及代码 1. 查询票数 2. 登录 关于自动登录 3. 提交订单 4. 邮件提醒 5. 短信提醒 三.总结与分析 一 ...

  5. 12306抢票脚本 python_如何使用Python实现12306抢票?摆脱无票可买的窘境

    前言 十一已经过去一个星期了,下一个假期就是元旦啦,每一次假期购票都得抢到"头破血流",所以小编经历过这次十一之后就在想做一个抢票小助手,经过几天的构思后,终于写了出来. 一.爬虫 ...

  6. 12306抢票脚本 python_春运抢票靠加速包?试试这个 Python 开源项目吧

    作者 | 非主流 出品 | Python大本营 又是一年春运,又到了拼手速.拼人品的时刻. 然而随着抢票软件的日益流行,拼加速包已经成为新的流行趋势.不过花钱买来的加速包真的有用吗? <工人日报 ...

  7. Python操作12306抢票脚本

    有一段时间没有使用Python了,前几天经朋友提起一篇关于用Python实现抢火车票的文章,百度了实现抢火车票的技术细节,网上却有不少资料,也不是新鲜的东西.在了解了一些技术手段,阅读了一些大神的博文 ...

  8. python自动刷新12306抢票

    #!/usr/bin/env python #-*- coding: utf-8 -*- """ 通过splinter刷12306火车票 可以自动填充账号密码,同时,在登 ...

  9. 想去看演唱却总是抢不到票?教你用Python制作一个自动抢票脚本

    前言 嗨喽!大家好,这里是魔王!! 大麦网,是中国综合类现场娱乐票务营销平台,业务覆盖演唱会. 话剧.音乐剧.体育赛事等领域. 但是因为票数有限,还有黄牛们不能丢了饭碗,所以导致了,很多人都抢不到票 ...

最新文章

  1. 2022-2028年中国降解塑料聚酯行业运行动态及投资机会分析报告
  2. 《翻译》Intel 64 与 IA-32 架构软件开发者手册卷1翻译
  3. go语言for的三种形式
  4. ftp安装遇到的问题
  5. 机器学习笔记 RNN初探 LSTM
  6. 公共服务领域英文译写规范_公共领域日:对版权和公共领域重要性的思考
  7. 泰山站和泰安站怎么区分?
  8. 幸亏有这本623页的微服务框架实战笔记,面试篇
  9. 数据分析展现工具SmartBI
  10. 努比亚 N1 (Nubia NX541J) 解锁BootLoader 并刷入recovery
  11. 发票识别 表格票据识别
  12. 基于服务的多源异构数据整合平台解决方案
  13. 直流可调稳压电源电压设计电路
  14. 大数据分析师岗位是青春饭
  15. 2011系列服务器,2011年中服务器领域大事件盘点
  16. 数码相机变焦镜头故障解析与解决
  17. Redis灵魂百问(入门详细基础教程)
  18. 使用SVN构建自己的本地代码库
  19. 网络语言为你打c,“想打定话给你”是什么梗
  20. canvas元素简易教程(3)(大部分转自火狐,自己只写了简单的代码分析)

热门文章

  1. python抓取抖音评论_一篇文章教会你用Python抓取抖音app热点数据
  2. TI IPNC_RDK_V3.8.0开发环境建立步骤备忘
  3. 清华5天内5则讣告,校方:把守护好老同志的生命健康作为当前疫情防控的重点...
  4. 直播报名|Flutter在「饿了么 ICBU」 的研发与沉淀
  5. 记录配置微信外链跳转小程序踩坑(H5跳转小程序)
  6. 基于点云数据的 Mesh重建与处理
  7. 360金融第一季度财报:收入32亿,疫情极限测试下走出的优质公司
  8. Linux用户管理— 用户管理命令
  9. 微信小程序开发如何实现微信支付
  10. 文件无法访问,提示没有权限