C#調用12306API做餘票查詢

本文敘述的是使用C#調用12306的API做餘票查詢程序的方法。


先看一下程序運行截圖。本程序使用WPF。



1. 瞭解12306API

登陸12306網站,點擊餘票查詢,我們查詢從北京到上海的火車票。

抓取到的數據包如下:

請求報文頭:


這就是我們封裝查詢報文時需要模仿的格式。

返回報文爲一個json格式的字符串,下面是一個樣例:

{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"datas":[{"train_no":"240000T1090K","station_train_code":"T109","start_station_telecode":"BJP","start_station_name":"北京","end_station_telecode":"SHH","end_station_name":"上海","from_station_telecode":"BJP","from_station_name":"北京","to_station_telecode":"SHH","to_station_name":"上海","start_time":"19:33","arrive_time":"10:44","day_difference":"1","train_class_name":"","lishi":"15:11","canWebBuy":"Y","lishiValue":"911","yp_info":"10177531664047650000101775000060879500063030450000","control_train_day":"20201231","start_train_date":"20150110","seat_feature":"W343136333","yp_ex":"1040106030","train_seat_feature":"3","seat_types":"14163","location_code":"P4","from_station_no":"01","to_station_no":"11","control_day":59,"sale_time":"1000","is_support_card":"0","note":"","gg_num":"--","gr_num":"6","qt_num":"--","rw_num":"無","rz_num":"--","tz_num":"--","wz_num":"166","yb_num":"--","yw_num":"無","yz_num":"無","ze_num":"--","zy_num":"--","swz_num":"--"},{"train_no":"240000D3130B","station_train_code":"D313","start_station_telecode":"VNP","start_station_name":"北京南","end_station_telecode":"SHH","end_station_name":"上海","from_station_telecode":"VNP","from_station_name":"北京南","to_station_telecode":"SHH","to_station_name":"上海","start_time":"19:34","arrive_time":"07:27","day_difference":"1","train_class_name":"動車","lishi":"11:53","canWebBuy":"Y","lishiValue":"713","yp_info":"O030900114O0309030154061500346","control_train_day":"20301231","start_train_date":"20150110","seat_feature":"O343W3","yp_ex":"O0O040","train_seat_feature":"3","seat_types":"OO4","location_code":"P3","from_station_no":"01","to_station_no":"04","control_day":59,"sale_time":"1230","is_support_card":"0","note":"","gg_num":"--","gr_num":"--","qt_num":"--","rw_num":"346","rz_num":"--","tz_num":"--","wz_num":"15","yb_num":"--","yw_num":"--","yz_num":"--","ze_num":"114","zy_num":"--","swz_num":"--"},{"train_no":"240000D31102","station_train_code":"D311","start_station_telecode":"VNP","start_station_name":"北京南","end_station_telecode":"SHH","end_station_name":"上海","from_station_telecode":"VNP","from_station_name":"北京南","to_station_telecode":"SHH","to_station_name":"上海","start_time":"21:16","arrive_time":"08:58","day_difference":"1","train_class_name":"動車","lishi":"11:42","canWebBuy":"Y","lishiValue":"702","yp_info":"O030900158O0309030174061500296","control_train_day":"20301231","start_train_date":"20150110","seat_feature":"O343W3","yp_ex":"O0O040","train_seat_feature":"3","seat_types":"OO4","location_code":"P2","from_station_no":"01","to_station_no":"04","control_day":59,"sale_time":"1230","is_support_card":"0","note":"","gg_num":"--","gr_num":"--","qt_num":"--","rw_num":"296","rz_num":"--","tz_num":"--","wz_num":"17","yb_num":"--","yw_num":"--","yz_num":"--","ze_num":"158","zy_num":"--","swz_num":"--"},{"train_no":"240000D32109","station_train_code":"D321","start_station_telecode":"VNP","start_station_name":"北京南","end_station_telecode":"SHH","end_station_name":"上海","from_station_telecode":"VNP","from_station_name":"北京南","to_station_telecode":"SHH","to_station_name":"上海","start_time":"21:23","arrive_time":"09:12","day_difference":"1","train_class_name":"動車","lishi":"11:49","canWebBuy":"Y","lishiValue":"709","yp_info":"O030900210O0309030154061500236","control_train_day":"20301231","start_train_date":"20150110","seat_feature":"O343W3","yp_ex":"O0O040","train_seat_feature":"3","seat_types":"OO4","location_code":"P2","from_station_no":"01","to_station_no":"05","control_day":59,"sale_time":"1230","is_support_card":"0","note":"","gg_num":"--","gr_num":"--","qt_num":"--","rw_num":"236","rz_num":"--","tz_num":"--","wz_num":"15","yb_num":"--","yw_num":"--","yz_num":"--","ze_num":"210","zy_num":"--","swz_num":"--"}],"flag":true,"searchDate":"2015年01月10號  週六"},"messages":[],"validateMessages":{}}
在這個json串中包含了查詢到的車輛餘票信息,也包含查詢者的信息。我們只需要提取其中有用的部分。現將其中有用的部分列舉如下:


其中有一些字段沒有標註釋,目前意義未知

通過解析這個json串,就能完成餘票查詢的工作。


2. 具體操作方法

根據上面抓取到的數據包,我們知道API的地址爲:

string uri = @"https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes={0}&queryDate={1}&from_station={2}&to_station={3}";
uri = string.Format(uri, purpose, date, from, to);
可以看出,其中一共有4個參數,perpose表示票的種類(成人票,學生票等),date表示查詢火車開車的日期(格式爲:yyyy-mm-dd),from表示出發車站代碼,end表示到達車站代碼。代碼均爲3位數大寫英文字母,每一個代碼唯一對應一個車站。

車站代碼可以到下面鏈接處下載:

http://download.csdn.net/detail/xiahn1a/8348211

對於上面給出的鏈接中下載到的代碼的處理方法:

文檔中代碼是如下格式:

bjb|北京北|VAP|beijingbei|bjb|0@bjd|北京東|BOP|beijingdong|bjd|1
車站間使用@分割,每條信息間使用|分割。我們所需要的只是第2項名稱和第3項代碼,其他的拼音,編號等不是我們所需要的內容。

根據代碼文檔我們可以建立一個站名到代碼的映射關係。

給一個鏈接的例子,在2015年1月10日查詢從北京到上海的成人票:

https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate=2015-01-10&from_station=BJP&to_station=SHH
剩下的就是按照上面截圖的格式封裝報文即可,需要注意的是我們需要採用ssl,有一個驗證證書的過程,還需要建立一個CookieContainer。

對於返回的json格式串,使用NewtonSoft.Json類中的相應函數將json字符串轉換爲jclass對象,然後可以直接通過Linq語句查詢出所需要的對象。

相關的封裝報文步驟代碼如下:

private CookieContainer cc = new CookieContainer();

public void GetInfo(string purpose, string date, string from, string to)
{
    try
    {
        string uri = @"https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes={0}&queryDate={1}&from_station={2}&to_station={3}";
        uri = string.Format(uri, purpose, date, from, to);
        ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        request.CookieContainer = cc;
        request.ProtocolVersion = HttpVersion.Version10;
        request.Accept = @"*/*";
        request.UserAgent = @"Mozilla/5.0 (Windows NT 6.4; WOW64; Trident/7.0; rv:11.0) like Gecko";
        request.Referer = @"https://kyfw.12306.cn/otn/lcxxcx/init";
        request.ContentType = @"application/x-www-form-urlencoded";
        request.Method = "GET";
        using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
        {
            using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
            {
                string res = reader.ReadToEnd();
                jclass jc1 = JsonConvert.DeserializeObject<jclass>(res);
                getData(jc1);
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    return ;
}

private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
    return true; //總是接受  
}


本文完成於2015.1.10,到目前爲止,該API仍可用。

發佈了56 篇原創文章 · 獲贊 44 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章