zoukankan      html  css  js  c++  java
  • 小工具:火车票查询

          今天又到了抢火车票的时候,反正是每次抢票都是傻眼。于是写个小工具帮助自己查询火车票,如果有票的话给自己发个邮件提示购买。

    一、准备工作

    利用firebug等工具,我们可以获取到当我们单击查询时调用的Get请求。

    请求地址:
    https://kyfw.12306.cn/otn/leftTicket/queryX?leftTicketDTO.train_date=2017-09-01&leftTicketDTO.from_station=TJP&leftTicketDTO.to_station=XHP&purpose_codes=ADULT
    分析一下参数,得到以下结果:
    • leftTicketDTO.from_station=TJP              出发站
    • leftTicketDTO.to_station=XHP                 到达站
    • leftTicketDTO.train_date=2017-09-01      出发日期
    • purpose_codes=ADULT                          乘车人类型

     接下来我们要对返回的json进行分析,返回的json如下:

    {
      "validateMessagesShowId": "_validatorMessage",
      "status": true,
      "httpstatus": 200,
      "data": {
        "result": [
          "oSIL3O3WIe6kfzF42Zlz%2B%2BsPFUuDm4BbNP13vXPnMnwSVXDgZ03onlMGao1RqJYjWimJDOiBa9Xm%0A59BctbygQhG6xM5mfTPPb9dLuvYg43VQYKGBpYzHere%2B4diEqyOfA64i6OcUFpe8ZJ4ccCEiQK7d%0AsFWdn6qDxY5PDQ2zG%2BCCNeAG216PdgO%2Fv%2F0PIrQTTK%2FTft8oZIxGT9VYlkHeh3TUUdxenyGAts4J%0AJ278LrM%3D|预订|260000Y5160F|Y516|VVP|ZMP|TXP|XHP|12:46|19:00|06:14|Y|rC9CEREwzGPqGayEjfr9YPwYtGX%2B45X6aKXyL0VaY0Bauu4t|20170901|3|P2|11|19|0|0|||||无||有|||有|||||101020|112",
          "wIpyiHG7PkQUnuOjE7gTxQAW39ihKOVttmAMx1lyhE10OyzjXjCe0EzyOsu28FOvw0Yc6CZF4CAQ%0AhR6L%2BAgyc6ca%2FYubiLZ4HuKw%2B0XmN5Us9fmA%2FajP3P%2FZgSoacqyPHPXEhWZ8pfGWSkmLMRNpisgd%0A48kWkp8rhjVIDljoncMagr6t5il3t5FaP8otqcN4ZtnTL1KfslpZt%2B2gxw7GwNFZH0QGTq2uoIuu%0AnYTSCB11w3Ok|预订|25000011360J|1136|TJP|WXC|TJP|XHP|20:52|01:55|05:03|Y|ZhbR6hnByAgL1ejqxJ1AIYiWcW2q08ZgO%2Bfk6UjtmX5mYLHGGlNBCiPVZDc%3D|20170901|3|P2|01|04|0|0||||无|||有||无|无|||||10401030|1413",
          "yG8TT6qCWjH0Aa2LVzxfCFGHf2q0syxOx7RfcXmsbFNyfq2VlWOS3q0oODd5tWAY2I5ddZfSBvnl%0A1GT%2BQCpZAhTPSLyRfDKCvwNMykAFe%2BuRULRPxd1WsQ57uIWepqDtElMcpGR8m7HB6KTnlzXXL9te%0AkumIJNMRMtA0ukPZto5G3sOGSSHgmQdcAxRmRdCVBFEsS9dNprjSQBwy%2BsxXq7KfAovJcemtY%2B3H%0AC7Kx4hnW5HXZ|预订|250000K8880H|K888|TJP|XNO|TJP|XHP|21:38|02:38|05:00|Y|uwEONiPztkLymI5okpF%2FJhWo7bzZA1zPcjbuofQXIXLCubD2gxSlXoz8nI8%3D|20170901|3|PB|01|04|0|0||||无|||有||无|有|||||10401030|1413"
        ],
        "flag": "1",
        "map": {
          "TXP": "天津西",
          "XHP": "宣化",
          "TJP": "天津"
        }
      },
      "messages": [],
      "validateMessages": {}
    }

     网页显示:

    以Y516车次数据为例,我们主要是搞清楚以下内容的含义:
    "oSIL3O3WIe6kfzF42Zlz%2B%2BsPFUuDm4BbNP13vXPnMnwSVXDgZ03onlMGao1RqJYjWimJDOiBa9Xm%0A59BctbygQhG6xM5mfTPPb9dLuvYg43VQYKGBpYzHere%2B4diEqyOfA64i6OcUFpe8ZJ4ccCEiQK7d%0AsFWdn6qDxY5PDQ2zG%2BCCNeAG216PdgO%2Fv%2F0PIrQTTK%2FTft8oZIxGT9VYlkHeh3TUUdxenyGAts4J%0AJ278LrM%3D|预订|260000Y5160F|Y516|VVP|ZMP|TXP|XHP|12:46|19:00|06:14|Y|rC9CEREwzGPqGayEjfr9YPwYtGX%2B45X6aKXyL0VaY0Bauu4t|20170901|3|P2|11|19|0|0|||||无||有|||有|||||101020|112"
     
     
    观察这段字符串,可以发现有一定规律,可以通过|对字符串切割,再对切割后的内容进行分析;
    分析中......
    经过若干数据对比,得出以下结论:
    • oSIL3O3WIe6kfzF42Zlz%2B%2BsPFUuDm4BbNP13vXPnMnwSVXDgZ03onlMGao1RqJYjWimJDOiBa9Xm%0A59BctbygQhG6xM5mfTPPb9dLuvYg43VQYKGBpYzHere%2B4diEqyOfA64i6OcUFpe8ZJ4ccCEiQK7d%0AsFWdn6qDxY5PDQ2zG%2BCCNeAG216PdgO%2Fv%2F0PIrQTTK%2FTft8oZIxGT9VYlkHeh3TUUdxenyGAts4J%0AJ278LrM%3D|            作为之后预订车票的参数使用
    • 预订|                 
    • 260000Y5160F|
    • Y516|              车次
    • VVP|               始发站
    • ZMP|              终点站
    • TXP|               出发站   
    • XHP|              到达站
    • 12:46|             出发时间  
    • 19:00|             到达时间
    • 06:14|             历时
    • Y|                   是否有票 N:无票  Y:有票
    • rC9CEREwzGPqGayEjfr9YPwYtGX%2B45X6aKXyL0VaY0Bauu4t|           未知内容
    • 20170901|     未知内容
    • 3|                 未知内容
    • P2|               未知内容
    • 11|                出发站站序
    • 19|               到达站站序
    • 0|                未知内容
    • 0|                未知内容
    • |                  未知内容
    • |                  未知内容
    • |                  未知内容
    • |                   软卧
    • 无|                软座
    • |                   未知内容
    • 有|                无座
    • |                   硬卧
    • |                   未知内容
    • 有|                硬座
    • |                   二等座
    • |                   一等座
    • |                   商务座
    • |                   未知内容
    • 101020|         未知内容
    • 112               未知内容

       好了我们的准备工作差不多就这样了。 

     二、编写代码

          现在的任务就是数据的获取,代码如下:

            /// <summary>
            /// 车票查询数据获取
            /// </summary>
            /// <param name="goTime">出行日期</param>
            /// <param name="fromStationCode">出发站编码</param>
            /// <param name="toStationCode">到达站编码</param>
            /// <param name="errorMsg">错误消息</param>
            /// <returns></returns>
            public TicketSearchJson GetSearchTicketData(DateTime goTime, string fromStationCode, string toStationCode, ref string errorMsg)
            {
                string srcString = string.Empty;
                try
                {
                    using (WebClient client = new WebClient())
                    {
                        string url = string.Format("https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={0}&leftTicketDTO.from_station={1}&leftTicketDTO.to_station={2}&purpose_codes=ADULT",
                            goTime.ToString("yyyy-MM-dd"), fromStationCode, toStationCode);
                        Uri uri = new Uri(url);
                        ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
                        byte[] responseData = client.DownloadData(uri);
                        srcString = Encoding.UTF8.GetString(responseData);
                        TicketSearchJson ticketSearch = JsonConvert.DeserializeObject<TicketSearchJson>(srcString);
                        return ticketSearch;
                    }
                }
                catch (Exception ex)
                {
                    errorMsg = ex.Message;
                    return null;
                }
            }
    
            /// <summary>
            /// 服务器证书回调方法
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="certificate"></param>
            /// <param name="chain"></param>
            /// <param name="sslPolicyErrors"></param>
            /// <returns></returns>
            private bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
            {
                return true;
            }

       邮件发送功能可见这篇博客小工具:邮件发送

    三、运行截图

       切勿过于频繁调用接口!!!

    程序下载:https://pan.baidu.com/s/1hrC2CPQ

     四、更新

    2018.1.2 获取数据后解析乱码问题

    分析后得知 响应数据经过压缩:Content-Encoding: gzip

    解决方法(加粗代码):

     1     public TicketSearchJson GetSearchTicketData_WebRequest(DateTime goTime, string fromStationCode, string toStationCode, ref string errorMsg)
     2         {
     3             string srcString = string.Empty;
     4             if (string.IsNullOrEmpty(fromStationCode) || string.IsNullOrEmpty(toStationCode)) { errorMsg = "出发站编码、到达站编码不能为空"; return null; }
     5             if (fromStationCode == toStationCode) { errorMsg = "出发站和到达站不能相同!"; return null; }
     6             try
     7             {
     8                 string url = string.Empty;
     9                 try
    10                 {
    11                     url = string.Format(System.Configuration.ConfigurationManager.AppSettings["Url"].ToString(),
    12                        goTime.ToString("yyyy-MM-dd"), fromStationCode, toStationCode);
    13                 }
    14                 catch { LogHelper.WriteErrorLog("读取url失败!"); }
    15                 Uri uri = new Uri(url);
    16                 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    17                 string cookie = string.Empty;
    18                 try { cookie = CreateCookie(fromStationCode, toStationCode); }
    19                 catch { LogHelper.WriteInfoLog("读取Cookie失败!"); errorMsg = "读取Cookie失败!"; }
    20                 HttpWebRequestHelper.SetRequestHeader(request, "kyfw.12306.cn", cookie);
    21                 ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
    22                 WebResponse response = request.GetResponse();
    23                 Stream respStream = response.GetResponseStream();
    24                 //如果响应头中包含Content-Encoding: gzip 数据需要解压缩
    25                 bool isNeedDeCompress = response.Headers.ToString().Contains("Content-Encoding: gzip");
    26                 if (isNeedDeCompress)
    27                     respStream = new GZipStream(respStream, CompressionMode.Decompress);
    28                 using (StreamReader reader = new StreamReader(respStream, Encoding.UTF8))
    29                 {
    30                     srcString = reader.ReadToEnd();
    31                     if (srcString.Contains("!DOCTYPE html"))
    32                     {
    33                         errorMsg = "网络可能存在问题,请您重试一下!";
    34                         return null;
    35                     }
    36                 }
    37                 TicketSearchJson ticketSearch = JsonConvert.DeserializeObject<TicketSearchJson>(srcString);
    38                 return ticketSearch;
    39             }
    40             catch (Exception ex)
    41             {
    42                 errorMsg = ex.Message;
    43                 LogHelper.WriteErrorLog("数据获取异常:" + ex.Message);
    44                 return null;
    45             }
    46         }

     

  • 相关阅读:
    【leetcode】49. 字母异位词分组
    【leetcode】48. 旋转图像
    【leetcode】48. 全排列 2
    Day4前端学习之路——背景边框列表链接和更复杂的选择器
    Day3前端学习之路——CSS基本知识
    Day2前端学习之路——HTML基本知识
    Day1前端学习之路——概述
    Axure实现抽奖转盘(二)
    Axure实现百度登录页面(一)
    线性代数课程较好的资料
  • 原文地址:https://www.cnblogs.com/LikeHeart/p/7466849.html
Copyright © 2011-2022 走看看