12306搶票腳本
本腳本使用一個類來實現所有代碼,大體上分爲以下幾個模塊及其步驟:
- 初始化對象屬性(在搶票前進行的屬性初始化,包括初始化瀏覽器模擬對象,個人信息等)。
- 建立模擬瀏覽器,模擬瀏覽器進行cookie等存儲。
-
驗證模塊:
- 獲取驗證圖片到本地
- 將8個圖片座標位置改裝成易於輸入的1—8的位置編號,輸入對應的位置號
- 發送請求進行後臺校驗
-
登錄模塊:
- 輸入賬號密碼,請求服務器
- 獲取apptk授權碼
- 授權通過,成功獲取用戶信息,將授權信息存儲到cookie
-
獲取站點模塊:
- 獲取所有站點名稱
- 獲取所有站點碼
-
獲取餘票信息模塊:
- 輸入起始站點與乘車時間,請求服務器,查詢餘票信息
- 將餘票信息進行格式化輸出
- 選擇相應車次
-
訂單模塊:
- 注入起始點、日期,車次碼信息,提交請求,返回狀態信息
- 獲取該車次的詳細信息,選擇車票類型
- 獲取所有已添加乘客
- 選擇乘車乘客
- 檢查訂單信息
- 確認訂單信息,佔座成功,下單完成
- 發送郵件,短信,提醒支付
以下貼出所有源碼,僅供參考,其中發送郵件與發送短信模塊所需的參數須自行到相關網站獲取。
# -*- coding:utf-8 -*-
from urllib import request
from json import loads
from prettytable import PrettyTable
from colorama import init, Fore, Back, Style
from email.mime.text import MIMEText
from qcloudsms_py import SmsSingleSender
from qcloudsms_py.httpclient import HTTPError
import urllib.parse as parse
import http.cookiejar as cookiejar
import re
import time
import smtplib
# 200 32
# 150 23
# 70 10
init(autoreset=False)
class Colored(object):
# 前景色:紅色 背景色:默認
def red(self, s):
return Fore.LIGHTRED_EX + s + Fore.RESET
# 前景色:綠色 背景色:默認
def green(self, s):
return Fore.LIGHTGREEN_EX + s + Fore.RESET
def yellow(self, s):
return Fore.LIGHTYELLOW_EX + s + Fore.RESET
def white(self, s):
return Fore.LIGHTWHITE_EX + s + Fore.RESET
def blue(self, s):
return Fore.LIGHTBLUE_EX + s + Fore.RESET
class train_ticket_purchase():
"""
初始化對象屬性
"""
def __init__(self):
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'}
self.opener = self.__get_opener()
self.username = ""
self.phone_number = "13781206061"#用於佔座成功接收短信
self.receive_email = "[email protected]"#用於佔座成功接收郵件
self.seatType = "1" #1硬座
self.seat_types_code = ["M","0","1","N","2","3","4","F","6","9"]
self.ticketType = "1" #成人票
self.query_seats_count = 1 #查票次數
self.passengers_name = "" #乘車人字符串
self.seat_list = ["yz_num","wz_num","rz_num","yw_num","rw_num", "dw_num", "gr_num","ze_num","zy_num", "swz_num"]
self.ticketTypes = {"1":"成人票","2":"兒童票","3":"學生票","4":"殘軍票"}
self.seatTypes = {
"M":"一等座",
"0":"二等座",
"1":"硬座",
"N":"無座",
"2":"軟座",
"3":"硬臥",
"4":"軟臥",
"F":"動臥",
"6":"高等軟臥",
"9":"商務座"
}
self.seat_dict = {
"yz_num":"硬座",
"wz_num":"無座",
"rz_num":"軟座",
"yw_num":"硬臥",
"rw_num":"軟臥",
"dw_num":"動臥",
"gr_num":"高級軟臥",
"ze_num":"二等座",
"zy_num":"一等座",
"swz_num":"商務特等座"
}
"""
請輸入帳號:18337735150
請輸入密碼:han123456789
"""
"""
建立模擬瀏覽器,模擬瀏覽器進行cookie存儲
"""
def __get_opener(self):
c = cookiejar.LWPCookieJar()
cookie = request.HTTPCookieProcessor(c)
opener = request.build_opener(cookie)
request.install_opener(opener)
return opener
"""
驗證模塊:
1、獲取驗證圖片到本地
2、將8個圖片座標位置改裝成易於輸入的1—8的位置編號,輸入對應的位置號
3、發送請求進行後臺校驗
"""
# 獲取驗證圖片到本地
def get_image(self):
req_catch_image = request.Request('https://kyfw.12306.cn/passport/captcha/captcha-image')
req_catch_image.headers = self.headers
code_file = self.opener.open(req_catch_image).read() # 此時爲瀏覽器的open而不再是request.urlopen,下同
with open('/code.jpg', 'wb')as f:
f.write(code_file)
# 圖片校驗
def verify(self):
answer = {
"1": "40,40",
"2": "110,40",
"3": "180,40",
"4": "260,40",
"5": "40,120",
"6": "110,120",
"7": "180,120",
"8": "260,120",
}
print("+----------+----------+----------+----------+")
print("| 1 | 2 | 3 | 4 |")
print("|----------|----------|----------|----------|")
print("| 5 | 6 | 7 | 8 |")
print("+----------+----------+----------+----------+")
input_code = input("請在1—8中選擇輸入驗證圖片編號,以半角','隔開。(例如:1,3,5):")
answer_code = ""
try:
for i in input_code.split(","):
answer_code += ("," + answer[i]) if (i is not input_code[0]) else answer[i]
except:
print("輸入有誤,請重新輸入!")
self.verify()
# 進行圖片驗證碼驗證
req_check = request.Request('https://kyfw.12306.cn/passport/captcha/captcha-check')
req_check.headers = self.headers
data = {
'answer': answer_code,
'login_site': 'E',
'rand': 'sjrand'
}
data = parse.urlencode(data).encode()
# 返回驗證結果
check_result = self.opener.open(req_check, data=data).read().decode() # 讀取出來是byts格式,轉換爲‘utf-8(默認)
return loads(check_result)
# 驗證系統
def sys_verify(self):
self.get_image()
verify_result = self.verify()
while verify_result['result_code'] is not '4':
print('驗證失敗,已重新下載圖片,請重新驗證!')
self.get_image()
verify_result = self.verify()
print("驗證通過!")
return
"""
登錄模塊:
1、輸入賬號密碼,請求服務器
2、獲取apptk授權碼
3、授權通過,成功獲取用戶信息,將授權信息存儲到cookie
"""
def login(self):
req_login = request.Request('https://kyfw.12306.cn/passport/web/login')
req_login.headers = self.headers
name = input("請輸入12306帳號:")
pwd = input("請輸入密碼:")
data = {
'username': name,
'password': pwd,
'appid': 'otn'
}
data = parse.urlencode(data).encode()
# 返回登錄結果
login_result = self.opener.open(req_login, data=data).read().decode()
return loads(login_result)
def get_tk(self):
req = request.Request('https://kyfw.12306.cn/passport/web/auth/uamtk')
req.headers = self.headers
data = {
"appid": "otn"
}
data = parse.urlencode(data).encode()
# 返回登錄結果
result = self.opener.open(req, data=data).read().decode()
return loads(result)
def auth(self,newapptk):
req = request.Request('https://kyfw.12306.cn/otn/uamauthclient')
req.headers = self.headers
data = {
"tk": newapptk
}
data = parse.urlencode(data).encode()
# 返回登錄結果
result = self.opener.open(req, data=data).read().decode()
return loads(result)
# 登陸系統
def sys_login(self):
self.login()
result = self.get_tk()
try:
result = self.auth(result['newapptk'])
except:
print("登錄失敗,賬號或密碼錯誤!")
self.sys_verify()
self.sys_login()
self.username = result["username"]
print("歡迎用戶",result["username"], "您已登錄成功!") if result["result_code"]==0 else print(result["result_message"])
return
"""
獲取站點模塊:
獲取所有站點名稱與站點碼
"""
def __get_city_result(self):
req_city_code = request.Request(
'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9093')
req_city_code.headers = self.headers
result = self.opener.open(req_city_code).read().decode()
return result
def get_city_code(self,name):
result = self.__get_city_result()
start = result.index(name)+len(name)
result = result[start+1:start+4]
# print(result)
return result
def get_station_names(self):
result = self.__get_city_result()
stations = re.findall(r'([\u4e00-\u9fa5]+)\|([A-Z]+)', result)
station_codes = dict(stations)
station_names = dict(zip(station_codes.values(), station_codes.keys()))
return station_names
"""
獲取餘票信息模塊:
1、輸入起始站點與乘車時間,請求服務器,查詢餘票信息
2、將餘票信息進行格式化輸出
3、選擇相應車次
"""
def get_tickets(self,from_station, to_station, train_date):
url = 'https://kyfw.12306.cn/otn/leftTicket/queryX?'
data = {
"leftTicketDTO.train_date": train_date,
"leftTicketDTO.from_station": from_station,
"leftTicketDTO.to_station": to_station,
"purpose_codes": "ADULT"
}
req = request.Request(url + parse.urlencode(data))
req.headers = self.headers
result = self.opener.open(req).read().decode()
return loads(result)
def get_ticket_format(self,from_station_name,from_station,to_station_name,to_station,train_date):
print('爲您查詢到從', from_station_name, '到', to_station_name, '的餘票信息如下:')
result = self.get_tickets(from_station, to_station, train_date)
result_list = result['data']['result']
station_names = self.get_station_names()
table = PrettyTable(
["車次", "出發/到達車站", "出發/到達時間", "歷時", "商務座", "一等座", "二等座", "高級軟臥", "軟臥", "動臥", "硬臥", "軟座", "硬座", "無座", "其他",
"備註"])
for item in result_list:
name = [
"station_train_code",
"from_station_name",
'start_time',
"lishi",
"swz_num",
"zy_num",
"ze_num",
"gr_num",
"rw_num",
"dw_num",
"yw_num",
"rz_num",
"yz_num",
"wz_num",
"qt_num",
"note_num"
]
data = {
"station_train_code": '',
"from_station_name": '',
"to_station_name": '',
'start_time': '',
'end': '',
"lishi": '',
"swz_num": '',
"zy_num": '',
"ze_num": '',
"dw_num": '',
"gr_num": '',
"rw_num": '',
"yw_num": '',
"rz_num": '',
"yz_num": '',
"wz_num": '',
"qt_num": '',
"note_num": ''
}
item = item.split('|') # 用"|"分割字符串
data['station_train_code'] = item[3] # 車次在3號位置
data['from_station_name'] = item[6] # 始發站信息在6號位置
data['to_station_name'] = item[7] # 終點站信息在7號位置
data['start_time'] = item[8] # 出發時間信息在8號位置
data['arrive_time'] = item[9] # 抵達時間在9號位置
data['lishi'] = item[10] # 經歷時間在10號位置
data['swz_num'] = item[32] or item[25] # 特別注意:商務座在32或25位置
data['zy_num'] = item[31] # 一等座信息在31號位置
data['ze_num'] = item[30] # 二等座信息在30號位置
data['gr_num'] = item[21] # 高級軟臥信息在31號位置
data['rw_num'] = item[23] # 軟臥信息在23號位置
data['dw_num'] = item[27] # 動臥信息在27號位置
data['yw_num'] = item[28] # 硬臥信息在28號位置
data['rz_num'] = item[24] # 軟座信息在24號位置
data['yz_num'] = item[29] # 硬座信息在29號位置
data['wz_num'] = item[26] # 無座信息在26號位置
data['qt_num'] = item[22] # 其他信息在22號位置
data['note_num'] = item[1] # 備註在1號位置
color = Colored() # 創建Colored對象
data["note_num"] = color.white(item[1])
# 如果沒有信息用'-'代替
for pos in name:
if data[pos] == '':
data[pos] = '-'
tickets = []
cont = []
cont.append(data)
for x in cont:
tmp = []
for y in name:
if y == "from_station_name":
s = color.green(station_names[data['from_station_name']]) + '\n' + color.red(
station_names[data["to_station_name"]])
tmp.append(s)
elif y == "start_time":
s = color.green(data['start_time']) + '\n' + color.red(data["arrive_time"])
tmp.append(s)
elif y == "station_train_code":
s = color.blue(data['station_train_code'])
tmp.append(s)
else:
tmp.append(data[y])
tickets.append(tmp)
for ticket in tickets:
table.add_row(ticket)
print(table)
def get_secret_str(self,from_station, to_station, train_date):
secret_str = {}
result = self.get_tickets(from_station, to_station, train_date)
result = result['data']['result']
for item in result:
msg = item.split("|")
secret_str[msg[3]] = parse.unquote(msg[0])
# print(secret_str)
return secret_str
def get_seats(self,station_train_code,from_station, to_station, train_date):
seats = {}
result = self.get_tickets(from_station, to_station, train_date)
result = result['data']['result']
for item in result:
item = item.split("|")
if item[3] == station_train_code :
seats['swz_num'] = item[32] or item[25] # 特別注意:商務座在32或25位置
seats['zy_num'] = item[31] # 一等座信息在31號位置
seats['ze_num'] = item[30] # 二等座信息在30號位置
seats['gr_num'] = item[21] # 高級軟臥信息在31號位置
seats['rw_num'] = item[23] # 軟臥信息在23號位置
seats['dw_num'] = item[27] # 動臥信息在27號位置
seats['yw_num'] = item[28] # 硬臥信息在28號位置
seats['rz_num'] = item[24] # 軟座信息在24號位置
seats['yz_num'] = item[29] # 硬座信息在29號位置
seats['wz_num'] = item[26] # 無座信息在26號位置
return seats
def select_order_details(self):
print("座位碼對照表:")
print("-----------------------")
print("| 序號 | 座位類型 |")
print("| M | 一等座 |")
print("| 0 | 二等座 |")
print("| 1 | 硬座 |")
print("| N | 無座 |")
print("| 2 | 軟座 |")
print("| 3 | 硬臥 |")
print("| 4 | 軟臥 |")
print("| F | 動臥 |")
print("| 6 | 高級軟臥 |")
print("| 9 | 商務座 |")
print("-----------------------")
seatType = input("請選擇車座類型,enter鍵默認硬座(例如:1):")
if seatType == '':
self.seatType = "1"
elif seatType in self.seat_types_code:
self.seatType = seatType
else :
raise Exception("沒有對應的車座類型!")
print("車票類型對照表:")
print("-----------------------")
print("| 序號 | 座位類型 |")
print("| 1 | 成人票 |")
print("| 2 | 兒童票 |")
print("| 3 | 學生票 |")
print("| 4 | 殘軍票 |")
print("-----------------------")
ticketType = input("請選擇車票類型,enter鍵默認成人票(例如:1):")
self.ticketType = ticketType if seatType != '' else "1"
passengers_name = input("請輸入乘車人姓名,如有多人,請以英文','隔開(例如:晏沈威,晏文豔):")
self.passengers_name = passengers_name if passengers_name!='' else '晏沈威'
email = input("請輸入發送提醒的郵箱(例如:[email protected]):")
self.receive_email = email if email!='' else "[email protected]"
phone_number = input("請輸入發送提醒的手機號(例如:13781206061):")
self.phone_number = phone_number if phone_number!='' else "13781206061"
def query_ticket(self,seats,seat_msg):
if ((seats[seat_msg] == "") | (seats[seat_msg] == "無")):
print("無",self.seat_dict[seat_msg],"座位!")
return False
else:
print("查詢到",seats[seat_msg], self.seat_dict[seat_msg], "座位!")
return True
def sys_seek_tickets(self):
while True:
from_station_name = "鄭州"
from_station_name = input("出發站點(例:鄭州):")
to_station_name = "開封"
to_station_name = input("到達站點(例:開封):")
train_date = "2019-02-28"
train_date = (input("乘車日期(例:2019-02-25):"))
print("正在爲您查詢餘票信息,請稍等...")
from_station = self.get_city_code(from_station_name)
to_station = self.get_city_code(to_station_name)
self.get_ticket_format(from_station_name,from_station,to_station_name,to_station,train_date)
if input("輸入'1'可繼續查詢,輸入enter鍵選擇車次!")!="1": break
station_train_code = "K464"
station_train_code = input("乘車車次(例:K464):")
# 選擇座位類型與車票類型與乘車人姓名
self.select_order_details()
while True:
seats = self.get_seats(station_train_code, from_station, to_station, train_date)
print('第{}次查票!'.format(self.query_seats_count),seats)
if(self.seatType=="1"):
if self.query_ticket(seats,"yz_num")==True :break
elif(self.seatType=="N"):
if self.query_ticket(seats,"wz_num")==True :break
elif(self.seatType=="2"):
if self.query_ticket(seats,"rz_num")==True :break
elif(self.seatType=="3"):
if self.query_ticket(seats,"yw_num")==True :break
elif(self.seatType=="4"):
if self.query_ticket(seats,"rw_num")==True :break
elif(self.seatType=="6"):
if self.query_ticket(seats,"gr_num")==True :break
elif(self.seatType=="0"):
if self.query_ticket(seats,"ze_num")==True :break
elif(self.seatType=="M"):
if self.query_ticket(seats,"zy_num")==True :break
elif(self.seatType=="F"):
if self.query_ticket(seats,"dw_num")==True :break
elif(self.seatType=="9"):
if self.query_ticket(seats,"swz_num")==True :break
else:
raise Exception("沒有相應車次!")
break
self.query_seats_count+=1
time.sleep(2)
# 獲取相應車次的secret_str
secret_str = self.get_secret_str(from_station, to_station, train_date)[station_train_code]
# print(secret_str)
result = {}
result["from_station"]=from_station
result["to_station"]=to_station
result["train_date"]=train_date
result["secret_str"]=secret_str
return result
"""
訂單模塊:
1、注入起始點、日期,車次碼信息,提交請求,返回狀態信息
2、獲取該車次的詳細信息,選擇車票類型
3、獲取所有已添加乘客
4、選擇乘車乘客
5、檢查訂單信息
6、確認訂單信息,佔座成功,下單完成
7、發送郵件,短信,提醒支付
"""
# {'validateMessagesShowId': '_validatorMessage', 'status': True, 'httpstatus': 200, 'data': 'N', 'messages': [], 'validateMessages': {}}
def get_train_number(self,tickets):
secret_str = parse.unquote(tickets["secret_str"])
from_station = tickets["from_station"]
to_station = tickets["to_station"]
train_date = tickets["train_date"]
req = request.Request('https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest')
req.headers = self.headers
data = {
"secretStr": secret_str,
"train_date": train_date,
"back_train_date": "",
"tour_flag": "dc",
"purpose_codes": "ADULT",
"query_from_station_name": from_station,
"query_to_station_name": to_station,
"undefined": "",
}
data = parse.urlencode(data).encode()
result = self.opener.open(req, data=data).read().decode()
return loads(result)
# 獲取相應車次的信息
def get_train_number_msg(self):
req = request.Request('https://kyfw.12306.cn/otn/confirmPassenger/initDc')
req.headers = self.headers
data = {
"_json_att": ""
}
data = parse.urlencode(data).encode()
# 返回登錄結果
result = self.opener.open(req, data=data).read().decode()
try:
ticketInfoForPassengerForm = re.findall("var ticketInfoForPassengerForm=(.*?);", result)[0].replace("'", '"')
globalRepeatSubmitToken = re.findall("globalRepeatSubmitToken = '(.*?)'", result)[0]
key_check_isChange = re.findall("'key_check_isChange':'(.*?)'", result)[0]
except:
raise Exception("沒有獲取到車次信息!")
ticketInfoForPassengerForm = loads(ticketInfoForPassengerForm)
leftDetails = ticketInfoForPassengerForm["leftDetails"]
leftTicketStr = ticketInfoForPassengerForm["leftTicketStr"]
purpose_codes = ticketInfoForPassengerForm["queryLeftTicketRequestDTO"]["purpose_codes"]
train_location = ticketInfoForPassengerForm["train_location"]
print("該車次剩餘車票詳情如下:")
for item in leftDetails:
print("\t",item)
msg_order_finally_submit = {}
msg_order_finally_submit["purpose_codes"] = purpose_codes
msg_order_finally_submit["key_check_isChange"] = key_check_isChange
msg_order_finally_submit["leftTicketStr"] = leftTicketStr
msg_order_finally_submit["train_location"] = train_location
msg_order_finally_submit["token"] = globalRepeatSubmitToken
return msg_order_finally_submit
# 獲取所有已添加乘客
def get_passengers(self,token):
req = request.Request('https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs')
req.headers = self.headers
data = {
"_json_att": "",
"REPEAT_SUBMIT_TOKEN": token
}
data = parse.urlencode(data).encode()
# 返回登錄結果
result = self.opener.open(req, data=data).read().decode()
result = loads(result)
normal_passengers = result["data"]["normal_passengers"]
result = {}
# print("已添加的乘車人如下:")
for passenger in normal_passengers:
result[passenger["passenger_name"]] = passenger
# if passenger != normal_passengers[len(normal_passengers) - 1]:
# print(passenger["passenger_name"] + ",", end='')
# else:
# print(passenger["passenger_name"])
return result
# 選擇乘車人
def select_passenger(self,passengers):
ps = self.passengers_name
oldPassengerStr = ''
passengerTicketStr = ''
seatType = 1 if self.seatType =="N" else self.seatType
try:
ps = ps.split(",")
for p in ps:
oldPassengerStr += passengers[p]["passenger_name"] + "," + \
passengers[p]["passenger_id_type_code"] + "," + \
passengers[p]["passenger_id_no"] + "," + \
passengers[p]["passenger_type"] + "_"
# seatType 座位類型:硬座1軟座2硬臥3軟臥4
# passenger_flag 乘客標記:0
# ticketType 車票類型: 成人票1兒童票2學生票3殘軍票4
# passenger_name 乘客姓名
# passenger_id_type_code 證件類型 中國居民身份證1
# passenger_id_no 身份證號
# mobile_no 手機號
# 3, 0, 2, 晏文豔, 1, 412728199608184527,, N_1, 0, 4, 晏沈威, 1, 412728199708124556, 13781206061, N_4, 0, 1, 晏建立, 1, 412728196802124575,, N
# 1, 0, 1, 晏沈威, 1, 412728199708124556, 13781206061, N_1, 0, 1, 晏建立, 1, 412728196802124575,, N_1, 0, 1, 晏文豔, 1, 412728199608184527,, N
ticketStr = "{},{},{},{},{},{},{},N".format(seatType,
passengers[p]["passenger_flag"],
self.ticketType,
passengers[p]["passenger_name"],
passengers[p]["passenger_id_type_code"],
passengers[p]["passenger_id_no"],
passengers[p]["mobile_no"])
passengerTicketStr += ticketStr + '_' if p != ps[len(ps) - 1] else ticketStr
except:
print("輸入有誤!")
result = {}
result["oldPassengerStr"] = oldPassengerStr
result["passengerTicketStr"] = passengerTicketStr
return result
# 檢查訂單信息
def order_submit(self,msg_passenger, token):
req = request.Request('https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo')
req.headers = self.headers
data = {
"cancel_flag": "2",
"bed_level_order_num": "000000000000000000000000000000",
"passengerTicketStr": msg_passenger["passengerTicketStr"],
"oldPassengerStr": msg_passenger["oldPassengerStr"],
"tour_flag": "dc",
"randCode": "",
"whatsSelect": "1",
"_json_att": "",
"REPEAT_SUBMIT_TOKEN": token
}
data = parse.urlencode(data).encode()
# 返回登錄結果
result = self.opener.open(req, data=data).read().decode()
return loads(result)
# 確認訂單
def order_ensure(self,msg_passenger,train_number_msg):
purpose_codes = train_number_msg["purpose_codes"]
key_check_isChange = train_number_msg["key_check_isChange"]
leftTicketStr = train_number_msg["leftTicketStr"]
train_location = train_number_msg["train_location"]
token = train_number_msg["token"]
req = request.Request('https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue')
req.headers = self.headers
data = {
"passengerTicketStr": msg_passenger["passengerTicketStr"],
"oldPassengerStr": msg_passenger["oldPassengerStr"],
"randCode": "",
"purpose_codes": purpose_codes,
"key_check_isChange": key_check_isChange,
"leftTicketStr": leftTicketStr,
"train_location": train_location,
"choose_seats": "",
"seatDetailType": "000",
"whatsSelect": "1",
"roomType": "00",
"dwAll": "N",
"_json_att": "",
"REPEAT_SUBMIT_TOKEN": token
}
data = parse.urlencode(data).encode()
# 返回登錄結果
result = self.opener.open(req, data=data).read().decode()
return loads(result)
# 發送email
def send_email(self):
# 第三方SMTP服務
mail_host = "smtp.qq.com"
mail_user = "*******@foxmail.com"
mail_pass = "****************"
sender = "[email protected]"
receiver = self.receive_email
message = MIMEText("席位已鎖定,快去支付!")
message["From"] = sender
message["To"] = receiver
message["Subject"] = "Python 12306 搶票!"
try:
server = smtplib.SMTP()
server.connect(mail_host)
server.login(mail_user, mail_pass)
server.sendmail(sender, receiver, message.as_string())
server.close()
print("郵件發送成功,已提醒用戶",receiver,"付款!")
except Exception as e:
print("郵件發送失敗!", e)
# 發送短信
def send_short_message(self):
name = self.username
phone_number = self.phone_number
seat_type = self.seatTypes[self.seatType]
ticketType = self.ticketTypes[self.ticketType]
appid = 1400****** # SDK AppID是1400開頭
appkey = "********************************"
phone_numbers = [phone_number]
# phone_numbers = ["13781206061", "18337735150", "15660039893"]
template_id = ******
sms_sign = "簡單點網"
ssender = SmsSingleSender(appid, appkey)
params = [name,ticketType,seat_type]
try:
result = ssender.send_with_param(86, phone_numbers[0], template_id, params, sign=sms_sign, extend="",ext="")
except HTTPError as e:
print(e)
except Exception as e:
print(e)
# print(result)
if result["errmsg"] == "OK":
print("短信發送成功,已提醒用戶", name, "付款!")
def sys_order(self,tickets):
# 1、注入起始點、日期,車次碼信息,提交請求,返回狀態信息
result = self.get_train_number(tickets)
if result["status"]==True :print("查詢車次信息成功!")
# 2、獲取該車次的詳細信息
train_number_msg = self.get_train_number_msg()
# 3、獲取乘客信息
passengers = self.get_passengers(train_number_msg["token"])
# 4、選擇乘客
msg_passenger = self.select_passenger(passengers)
# print(msg_passenger)
# 5、下單
result = self.order_submit(msg_passenger, train_number_msg["token"])
if result["status"] == True :print("檢查訂單信息正確,即將確認訂單!")
print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))
# 6、確認訂單
result = self.order_ensure(msg_passenger, train_number_msg)
if result["status"] == True :
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())), ":下單成功,已佔座,請儘快付款!")
self.send_email()
self.send_short_message()
# print(result)
input("按任意鍵繼續!")
def run(self):
# 驗證碼驗證
self.sys_verify()
# 登錄
self.sys_login()
# 查餘票
tickets = self.sys_seek_tickets()
# 下訂單
self.sys_order(tickets)
if __name__ == '__main__':
while True:
train_ticket_purchase().run()