1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 ''' 4 利用splinter写的一个手动过验证及自动抢票的例子, 5 大家可以自己扩展或者弄错窗体、web端。 6 本例子只做参考。 7 本代码发布于2018.12.18(如果报错请查看官网是否改动) 8 ''' 9 import re 10 from splinter.browser import Browser 11 from time import sleep 12 import sys 13 import httplib2 14 from urllib import parse 15 import smtplib 16 from email.mime.text import MIMEText 17 18 19 class BrushTicket(object): 20 """买票类及实现方法""" 21 22 def __init__(self,train_date, user_name, password, passengers, from_time, from_station, to_station, seat_type, receiver_mobile, receiver_email,isHave=False,): 23 """定义实例属性,初始化""" 24 # 有票就行 25 self.isHave = isHave 26 # 1206账号密码 27 self.user_name = user_name 28 self.password = password 29 # 乘客姓名 30 self.passengers = passengers 31 # 起始站和终点站 32 self.from_station = from_station 33 self.to_station = to_station 34 # 乘车日期 35 self.from_time = from_time 36 #发车时间 37 self.train_date=train_date 38 # 座位类型所在td位置 39 if seat_type == '商务座特等座': 40 seat_type_index = 1 41 seat_type_value = 9 42 elif seat_type == '一等座': 43 seat_type_index = 2 44 seat_type_value = 'M' 45 elif seat_type == '二等座': 46 seat_type_index = 3 47 seat_type_value = 0 48 elif seat_type == '高级软卧': 49 seat_type_index = 4 50 seat_type_value = 6 51 elif seat_type == '软卧': 52 seat_type_index = 5 53 seat_type_value = 4 54 elif seat_type == '动卧': 55 seat_type_index = 6 56 seat_type_value = 'F' 57 elif seat_type == '硬卧': 58 seat_type_index = 7 59 seat_type_value = 3 60 elif seat_type == '软座': 61 seat_type_index = 8 62 seat_type_value = 2 63 elif seat_type == '硬座': 64 seat_type_index = 9 65 seat_type_value = 1 66 elif seat_type == '无座': 67 seat_type_index = 10 68 seat_type_value = 1 69 elif seat_type == '其他': 70 seat_type_index = 11 71 seat_type_value = 1 72 else: 73 seat_type_index = 7 74 seat_type_value = 3 75 self.seat_type_index = seat_type_index 76 self.seat_type_value = seat_type_value 77 # 通知信息 78 self.receiver_mobile = receiver_mobile 79 self.receiver_email = receiver_email 80 # 主要页面网址 81 self.login_url = 'https://kyfw.12306.cn/otn/login/init' 82 self.init_my_url = 'https://kyfw.12306.cn/otn/view/index.html' 83 self.ticket_url = 'https://kyfw.12306.cn/otn/leftTicket/init' 84 # 浏览器驱动信息,驱动下载页:https://sites.google.com/a/chromium.org/chromedriver/downloads 85 self.driver_name = 'chrome' 86 #驱动的位置 87 self.executable_path = 'C:/Program Files (x86)/Google/Chrome/Application/chromedriver.exe' 88 89 def do_login(self): 90 """登录功能实现,手动识别验证码进行登录""" 91 self.driver.visit(self.login_url) 92 sleep(1) 93 self.driver.fill('loginUserDTO.user_name', self.user_name) 94 self.driver.fill('userDTO.password', self.password) 95 print('请输入验证码……') 96 while True: 97 if self.driver.url != self.init_my_url: 98 sleep(1) 99 else: 100 break 101 102 def start_brush(self): 103 """买票功能实现""" 104 self.driver = Browser(driver_name=self.driver_name,executable_path=self.executable_path) 105 # 浏览器窗口的大小 106 # self.driver.driver.set_window_size(900, 700) 107 self.do_login() 108 self.driver.visit(self.ticket_url) 109 try: 110 print('开始刷票……') 111 # 加载车票查询信息 112 self.driver.cookies.add({"_jc_save_fromStation": self.from_station})#出发位置 113 self.driver.cookies.add({"_jc_save_toStation": self.to_station})#目的地 114 self.driver.cookies.add({"_jc_save_fromDate": self.from_time})#出发时间 115 self.driver.reload() 116 count = 0 117 while self.driver.url.split('?')[0] == self.ticket_url: 118 try: 119 self.wait_time('query_ticket') 120 elemt= self.driver.find_by_xpath('//select[@id="cc_start_time"]//option[@value="'+str(self.train_date)+'"]',).first 121 elemt.click() 122 sleep(1) 123 self.driver.find_by_text('查询').click() 124 self.wait_time('train_type_btn_all') 125 count += 1 126 print('第%d次点击查询……' % count) 127 128 elems = self.driver.find_by_id('queryLeftTable')[0].find_by_xpath('//tr[starts-with(@id,"ticket_")]') 129 while len(elems)==0: 130 sleep(0.5) 131 elems = self.driver.find_by_id('queryLeftTable')[0].find_by_xpath('//tr[starts-with(@id,"ticket_")]') 132 #是不是有票就行 133 if(self.isHave): 134 for current_tr in elems: 135 if(current_tr.text==''): 136 print('没票') 137 continue 138 # 下标索引 139 if current_tr.find_by_tag('td')[self.seat_type_index].text == '--' or current_tr.find_by_tag('td')[self.seat_type_index].text == '无': 140 print('无此座位类型出售!') 141 continue 142 else: 143 # 有票,尝试预订 144 print('刷到票了(余票数:' + str(current_tr.find_by_tag('td')[self.seat_type_index].text) + '),开始尝试预订……') 145 current_tr.find_by_css('td.no-br>a')[0].click() 146 key_value = 1 147 # 等待页面加载完毕 148 self.wait_time('normalPassenger_' +str(int(key_value-1))) 149 150 for p in self.passengers: 151 # 选择用户 152 print('开始选择用户……') 153 self.driver.find_by_text(p).last.click() 154 # 选择座位类型 155 print('开始选择席别……') 156 if self.seat_type_value != 0: 157 sleep(1) 158 seat_select = self.driver.find_by_id("seatType_" + str(key_value))[0] 159 seat_select.find_by_xpath("//option[@value='" + str(self.seat_type_value) + "']")[0].click() 160 key_value += 1 161 if p[-1] == ')': 162 self.driver.find_by_id('dialog_xsertcj_ok').click() 163 print('正在提交订单……') 164 self.driver.find_by_id('submitOrder_id').click() 165 self.wait_time('content_checkticketinfo_id') 166 # 查看返回结果是否正常 167 submit_false_info = self.driver.find_by_id('orderResultInfo_id')[0].text 168 if submit_false_info != '': 169 print(submit_false_info) 170 self.driver.find_by_id('qr_closeTranforDialog_id').click() 171 self.driver.find_by_id('preStep_id').click() 172 continue 173 print('正在确认订单……') 174 # 等待加载完毕 175 self.wait_time('qr_submit_id') 176 self.driver.find_by_id('qr_submit_id').click() 177 print('预订成功,请及时前往支付……') 178 # 发送通知信息 179 self.send_mail(self.receiver_email, '恭喜您,抢到票了,请及时前往12306支付订单!') 180 self.send_sms(self.receiver_mobile, '恭喜您,抢到票了,请及时前往12306支付订单!') 181 else: 182 for current_tr in elems: 183 if(current_tr.text==''): 184 print('没票') 185 continue 186 else: 187 # 下标索引 188 print('判断车票是否存在') 189 if current_tr.find_by_tag('td')[self.seat_type_index].text == '--' or current_tr.find_by_tag('td')[self.seat_type_index].text == '无': 190 print('无此座位类型出售!') 191 continue 192 else: 193 # 有票,尝试预订 194 print('刷到票了(余票数:' + str(current_tr.find_by_tag('td')[self.seat_type_index].text) + '),开始尝试预订……') 195 current_tr.find_by_css('td.no-br>a')[0].click() 196 key_value = 1 197 # 等待页面加载完毕 198 self.wait_time('normalPassenger_' +str(int(key_value-1))) 199 200 for p in self.passengers: 201 # 选择用户 202 print('开始选择用户……') 203 self.driver.find_by_text(p).last.click() 204 # 选择座位类型 205 print('开始选择席别……') 206 if self.seat_type_value != 0: 207 seat_select = self.driver.find_by_id("seatType_" + str(key_value))[0] 208 seat_select.find_by_xpath("//option[@value='" + str(self.seat_type_value) + "']")[0].click() 209 key_value += 1 210 if p[-1] == ')': 211 self.driver.find_by_id('dialog_xsertcj_ok').click() 212 print('正在提交订单……') 213 self.driver.find_by_id('submitOrder_id').click() 214 self.wait_time('content_checkticketinfo_id') 215 # 查看返回结果是否正常 216 submit_false_info = self.driver.find_by_id('orderResultInfo_id')[0].text 217 if submit_false_info != '': 218 print(submit_false_info) 219 self.driver.find_by_id('qr_closeTranforDialog_id').click() 220 self.driver.find_by_id('preStep_id').click() 221 continue 222 print('正在确认订单……') 223 # 等待加载完毕 224 self.wait_time('qr_submit_id') 225 self.driver.find_by_id('qr_submit_id').click() 226 print('预订成功,请及时前往支付……') 227 # 发送通知信息 228 self.send_mail(self.receiver_email, '恭喜您,抢到票了,请及时前往12306支付订单!') 229 self.send_sms(self.receiver_mobile, '恭喜您,抢到票了,请及时前往12306支付订单!') 230 231 # self.driver.quit() 232 return 233 except Exception as error_info: 234 print(error_info) 235 except Exception as error_info: 236 print(error_info) 237 238 def wait_time(self, name): 239 while self.driver.is_element_present_by_id(name) == False: 240 sleep(1) 241 242 243 def send_sms(self, mobile, sms_info): 244 """发送手机通知短信,用的是-互亿无线-的测试短信""" 245 host = "106.ihuyi.com" 246 sms_send_uri = "/webservice/sms.php?method=Submit" 247 account = "C59782899" 248 pass_word = "19d4d9c0796532c7328e8b82e2812655" 249 params = parse.urlencode( 250 {'account': account, 'password': pass_word, 251 'content': sms_info, 'mobile': mobile, 'format': 'json'} 252 ) 253 headers = {"Content-type": "application/x-www-form-urlencoded", 254 "Accept": "text/plain"} 255 conn = httplib2.HTTPConnectionWithTimeout(host, port=80, timeout=30) 256 conn.request("POST", sms_send_uri, params, headers) 257 response = conn.getresponse() 258 response_str = response.read() 259 conn.close() 260 return response_str 261 262 def send_mail(self, receiver_address, content): 263 """发送邮件通知""" 264 # 连接邮箱服务器信息 265 host = 'smtp.qq.com' #QQ 266 sender = '[email protected]' # 你的发件邮箱号码 267 pwd = 'riebhcallwgzbfei' # 不是登陆密码,是客户端授权密码 268 # 发件信息 269 receiver = receiver_address 270 body = '<h2>温馨提醒:</h2><p>' + content + '</p>' 271 msg = MIMEText(body, 'html', _charset="utf-8") 272 msg['subject'] = '抢票成功通知!' 273 msg['from'] = sender 274 msg['to'] = receiver 275 s = smtplib.SMTP(host, port=465, timeout=30) 276 # 开始登陆邮箱,并发送邮件 277 s.login(sender, pwd) 278 s.sendmail(sender, receiver, msg.as_string()) 279 s.close() 280 281 282 if __name__ == '__main__': 283 # 12306用户名 284 user_name = 'XXXXX' 285 # 12306登陆密码 286 password = 'XXXXX' 287 # 乘客姓名 288 passengers = '张三' 289 # 乘车日期 290 from_time = '2018-12-21' 291 292 #发车时间 293 train_date={ 294 '00:00--24:00':'00002400', 295 '00:00--06:00':'00000600', 296 '06:00--12:00':'06001200', 297 '12:00--18:00':'12001800', 298 '18:00--24:00':'18002400', 299 } 300 301 # 城市cookie字典 302 city_list = {'成都': '%u6210%u90FD%2CCDW', 303 '重庆': '%u91CD%u5E86%2CCQW', 304 '北京': '%u5317%u4EAC%2CBJP', 305 '广州': '%u5E7F%u5DDE%2CGZQ', 306 '杭州': '%u676D%u5DDE%2CHZH', 307 '宜昌': '%u5B9C%u660C%2CYCN', 308 '郑州': '%u90D1%u5DDE%2CZZF', 309 '深圳': '%u6DF1%u5733%2CSZQ', 310 '西安': '%u897F%u5B89%2CXAY', 311 '大连': '%u5927%u8FDE%2CDLT', 312 '武汉': '%u6B66%u6C49%2CWHN', 313 '上海': '%u4E0A%u6D77%2CSHH', 314 '南京': '%u5357%u4EAC%2CNJH', 315 '合肥': '%u5408%u80A5%2CHFH'} 316 # 出发站 317 from_station = city_list['广州'] 318 # 终点站 319 to_station = city_list['武汉'] 320 # 座位类型 321 seat_type = '二等座' 322 # 抢票成功,通知该手机号码 323 receiver_mobile = 'xxxxxx' 324 #抢票成功,通知该邮件 325 receiver_email = '[email protected]' 326 # 开始抢票 327 ticket = BrushTicket(train_date['18:00--24:00'],user_name, password, passengers.split(","), from_time, from_station, 328 to_station, seat_type, receiver_mobile, receiver_email,True) 329 ticket.start_brush()
环境:python3,chromedriver(请下载对应的版本的浏览器驱动)
效果图:
本代码发布于2018.12.18(如果报错请查看包是否引用正确或官网是否改动)