小工具:火車票查詢


      今天又到了搶火車票的時候,反正是每次搶票都是傻眼。於是寫個小工具幫助自己查詢火車票,如果有票的話給自己發個郵件提示購買。

一、准備工作

 

利用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         }

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM