12306车票剩余数信息监控,国庆回家没买到票的可以看看哦~~
本帖最后由 朝夕忆浅 于 2018-9-13 17:50 编辑来吾爱也好久了,一直也没有发过贴,今天上班闲着无聊,想起自己从老家来的票还没买到,就写了个python的动车剩余票数监控,现分享给大家~~
新手发帖,有不好或不对的地方还请大家指出哦~~
以下为源代码
import requests
import json
import smtplib
import sys
import time
from email.mime.text import MIMEText
from email.header import Header
class tl12306:
def __init__(self):
self.num = 1
# 替换为你要查询的url地址
self.url = 'https://kyfw.12306.cn/otn/leftTicket/queryA?leftTicketDTO.train_date=2018-10-07&leftTicketDTO.from_station=LYS&leftTicketDTO.to_station=FZS&purpose_codes=ADULT'
def getlist(self):
try:
r = requests.get(self.url)
if r.status_code == 200:
j = json.loads(r.text)
k = j['data']
for r in k['result']:
# 以下共查询了5个车次信息,如需更多请自行添加
if "|D6577|" in r: # 获取D6577车次信息
# 查询站票、二等票是否还有票,不为无则视为有票发送邮件通知,如需监控一等票请自行添加
if r.split('|')[-7] != '无' or r.split('|')[-11] != '无':
str1 = 'D6577,有票啦~~'
print(str1)
self.send_mail(str1)
elif "|D6435|" in r:
if r.split('|')[-7] != '无' or r.split('|')[-11] != '无':
str2 = 'D6435,有票啦~~'
print(str2)
self.send_mail(str2)
elif "|D6437|" in r:
if r.split('|')[-7] != '无' or r.split('|')[-11] != '无':
str3 = 'D6437,有票啦~~'
print(str3)
self.send_mail(str3)
elif "|D6439|" in r:
if r.split('|')[-7] != '无' or r.split('|')[-11] != '无':
str4 = 'D6439,有票啦~~'
print(str4)
self.send_mail(str4)
elif "|D6441|" in r:
if r.split('|')[-7] != '无' or r.split('|')[-11] != '无':
str5 = 'D6441,有票啦~~'
print(str5)
self.send_mail(str5)
sys.stdout.write('\r')
sys.stdout.write('已查询%d次~' % self.num)
sys.stdout.flush()
self.num+=1
else:
msg = '获取车票信息失败~~'
print(msg)
self.send_mail(msg)
except Exception as e:
print(e)
def send_mail(self,str):
# 第三方 SMTP 服务
mail_host = "smtp.qq.com"# 设置服务器
mail_user = "xxxxxx@qq.com"# 用户名 发送者的邮箱
mail_pass = "xxxxxxxxxxxxxxxxx"# 口令 登录邮箱开启POP3/SMTP服务,输入授权码
sender = 'xxxxxxx@qq.com' #发送者的邮箱
receivers = ['111111111@qq.com']# 接收邮件,可设置为你的QQ邮箱或者其他邮箱
message = MIMEText(str, 'plain', 'utf-8')
message['From'] = Header("发送者的名字", 'utf-8')
message['To'] = Header("接收者的名字", 'utf-8')
subject = str
message['Subject'] = Header(subject, 'utf-8')
try:
smtpObj = smtplib.SMTP()
smtpObj.connect(mail_host, 25)# 25 为 SMTP 端口号
smtpObj.login(mail_user, mail_pass)
smtpObj.sendmail(sender, receivers, message.as_string())
print("邮件发送成功")
except smtplib.SMTPException:
print("Error: 无法发送邮件")
def input_second(self):
second = input("请输入查询间隔(单位/秒): ")
try:
return int(second)
except Exception:
print('您输入的不是数字,请重新输入~~')
return self.input_page()
if __name__ == '__main__':
c = tl12306()
print('程序正在运行~~关闭窗口即退出监控~')
second = c.input_second()
while True:
c.getlist()
time.sleep(second)
关于url的提取,如下~~
程序运行界面~~楼主把它打包成了EXE可执行文件~~
在查询了69次之后,终于有票了,哈哈哈哈~~,注意,如果你没关掉程序,程序会一直发送邮件哦~~
邮件发送成功~~快去买票啦~~
唉电脑没环境做成个exe多好 我是缺源码的人吗?我是缺打包成了EXE可执行文件的方法 本帖最后由 LemonX 于 2018-9-15 16:11 编辑
在楼主的基础上稍作修改,可输入 日期,起点站,终点站 自动匹配url获取余票信息.
变更余票提示为:
1、如果有余票数量,则显示余票数量,否则仅显示有票。
2、车次由原来的制定车次变更为遍历所有的车次信息,并返回有票的车次信息
3、加入header,模拟浏览器访问
4、加入根据起止站点去匹配站点编码,再将编码加入生成url 【再也不需要去浏览器打开调试,然后复制NetWork的Header中的URL粘贴过来】
4、等待时间变更为随机等待,听说能稍稍避免被反爬虫,{:1_901:}主要是懒,懒得去输入
5、邮件发送,由原来的每个车次发送结果变更为【每次查询仅发送一次】,强迫症讨厌被过分打扰
6、加入发送到微信,使用的是wxpy模块,需要先pip install wxpy .麻烦的是需要扫码登陆
import requests
import json
import smtplib
import time
from random import randint#随机函数
from email.mime.text import MIMEText
from email.header import Header
from wxpy import *
headers={
"Host": "kyfw.12306.cn",
"If-Modified-Since": "0",
"Referer": "https://kyfw.12306.cn/otn/leftTicket/init",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"
}
class tl12306(object):
def __init__(self):
self.num=1
#按需停止查询
self.finded_tiket=False#标识是否有票,有票则停止查询,避免对12306无效访问,造成带宽拥堵而且由ip封杀的风险【不给12306添堵了,反正咱要的信息已经有了】
self.station_name_url='https://kyfw.12306.cn/otn/resources/js/framework/station_name.js' #起始站点信息表,根据起止站点获取站点编码code
# 灵活修改为模拟输入【日期,起止站点由用户输入】
self.url = 'https://kyfw.12306.cn/otn/leftTicket/queryA?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT'
def get_station_code(self,from_station,to_statian):
'''根据起止站站点名去获取对应的站点编码'''
result={}
result['from_code']=''
result['to_code']=''
r = requests.get(url=self.station_name_url,verify=False).text
for i in r.split('@'):
# i 的格式为bjb|北京北|VAP|beijingbei|bjb|0
if len(i.split('|')) >= 3:#是否大于3个成员,排除返回起始无效字段ar station_names ='
stationText=i.split('|')
stationCode=i.split('|')
if stationText==from_station:
result['from_code']=stationCode
elif stationText==to_statian:
result['to_code']=stationCode
if result['from_code']!='' and result['to_code']!='':#找到起止code 后直接退出,避免无效等待
return result
def getlist(self,data,fromStaion,toStation):
'''根据传入的日期,起点站,终点站去查询余票信息'''
try:
r=requests.get(self.url.format(data,fromStaion,toStation),headers=headers)
if r.status_code==200:
j=json.loads(r.text)
k=j['data']
print(str(k['map']))#站点信息(含同城异站站点) 如:{'KMM': '昆明', 'KOM': '昆明南', 'NNZ': '南宁'}
last_result=[]#所有车次所有余票信息列表
for r in k['result']:
strarray=r.split('|')
train_num=str(strarray)#车次
seat1=str(strarray[-6])#一等座
seat2=str(strarray[-7])#二等座
seat3=str(strarray[-11])#站票
result=''
if seat1 != '无' andseat1 != '':
result +=' 一等座有票!{};'.format('' if seat1=='有' else '余票:{}'.format(seat1)) #pyrhon的三目运算,如果返回实际剩余票数,则显示余票数
if seat2 != '无' andseat2 != '':
result +=' 二等座有票!{};'.format('' if seat2=='有' else '余票:{}'.format(seat2))
if seat3 != '无' andseat3 != '':
result +=' 站票有票!{};'.format('' if seat3=='有' else '余票:{}'.format(seat3))
if result!= '':
self.finded_tiket=True#查到票就标记已有票,不再刷新页面,如果不是自己想要的车次,可以注释此行代码,或者修改判断逻辑
result = '{},有票啦~~;{}\n'.format(train_num,result)
last_result.append(result)
print(result)
self.send_mail(str(k['map'])+'\n'+''.join(last_result))
print('已查询%d次' %self.num)
self.num +=1
return last_result
else:
print('获取车票失败!')
self.send_mail('获取车票失败')
except Exception as e:
print(e)
def send_mail(self,str):
#第三方SMTP服务
mail_host="smtp.qq.com"#设置服务器
mail_user="xxxxxx@qq.com"#发送者邮箱
mail_pass='gsvwqbXXXXXtwjclwjbddcdfddjb'# 口令 登录邮箱开启POP3/SMTP服务,输入授权码
sender="XXXX@qq.com"
receivers=['XXXX@qq.com']# 接收邮件,可设置为你的QQ邮箱或者其他邮箱
message=MIMEText(str,'plain','utf-8')#邮件文本内容
message['From']=Header('Lemon','utf-8')
message['to']=Header('Lemon_too','utf-8')
message['Subject']=Header('余票查询结果','utf-8')#邮件标题
try:
smtpobj=smtplib.SMTP()
smtpobj.connect(mail_host,25)#25为端口号
smtpobj.login(mail_user,mail_pass)
smtpobj.sendmail(sender,receivers,message.as_string())
print('邮件发送成功')
except smtplib.SMTPException:
print('Error: 无法发送邮件{}'.format(smtplib.SMTPException))
#发送消息到微信
def send_msg_to_wachat(self,data):
bot = Bot()
my_friend = bot.friends().search(u'Lemon') #你朋友/自己的微信名称,不是备注,也不是微信帐号。。
my_friend.send(data)
def input_date(self):
return input('请输入查询日期【yyyy-MM-dd】:')
def input_from(self):
return input('请输入起点站站名:')
def input_to(self):
return input('请输入到站站名:')
if __name__=='__main__':
c=tl12306()
print('程序开始运行')
while c.finded_tiket==False: #如果找到票了,停止查询
#输入日期,制定格式为 2018-10-01
date=c.input_date()
#输入起点站 站名
fromStaion=c.input_from()
#输入终点站 站名
toStation=c.input_to()
#由起止站站名去匹配对应编码,用于生成实际访问url
stationCode_Info=c.get_station_code(fromStaion,toStation)
print(stationCode_Info)
last_result=c.getlist(date,stationCode_Info['from_code'],stationCode_Info['to_code'])
c.send_msg_to_wachat(last_result)
#随机等待,避免访问过快而被禁ip等受限访问结果
time.sleep(randint(5,10))
print('程序运行结束')
12306bypass了解一下 试试!!!!!!!!!!!!!!!! 貌似360浏览器有监控功能 楼主发出来的目的是交流学习,你们都看的是什么呢。。。
当然,楼主,我提个小建议,可以加入微信提醒更好,现在网上都有Python微信的源码了 厉害......... 本帖最后由 夏虫333 于 2018-9-13 19:36 编辑
感谢分享 很不错,不过票都提前买好了。感谢,笔芯 怎么没看见文件啊??