朝夕忆浅 发表于 2018-9-13 17:47

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次之后,终于有票了,哈哈哈哈~~,注意,如果你没关掉程序,程序会一直发送邮件哦~~

邮件发送成功~~快去买票啦~~



林夕丶 发表于 2018-9-13 18:02

唉电脑没环境做成个exe多好

shengzhigang 发表于 2018-9-13 19:15

我是缺源码的人吗?我是缺打包成了EXE可执行文件的方法

LemonX 发表于 2018-9-15 14:56

本帖最后由 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('程序运行结束')

羅少 发表于 2018-9-13 18:10

12306bypass了解一下

yoboboo 发表于 2018-9-13 18:11

试试!!!!!!!!!!!!!!!!

but 发表于 2018-9-13 18:13

貌似360浏览器有监控功能

ClearLover 发表于 2018-9-13 18:20

楼主发出来的目的是交流学习,你们都看的是什么呢。。。
当然,楼主,我提个小建议,可以加入微信提醒更好,现在网上都有Python微信的源码了

伴我99不88 发表于 2018-9-13 18:26

厉害.........

夏虫333 发表于 2018-9-13 18:52

本帖最后由 夏虫333 于 2018-9-13 19:36 编辑

感谢分享

会放电的皮卡丘 发表于 2018-9-13 19:00

很不错,不过票都提前买好了。感谢,笔芯

等哈不懂何必 发表于 2018-9-13 19:00

怎么没看见文件啊??
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 12306车票剩余数信息监控,国庆回家没买到票的可以看看哦~~