C#Http編程

c# 模擬 網頁實現12306登錄、自動刷票、自動搶票徹底篇(轉)

這一篇文章,我將從頭至尾教你們使用c#模擬網頁面登錄12306網站,自動刷票,選擇訂票人,到最後一步提交訂單。研究過HTTP協議的童鞋們都 知道,咱們在訪問網站時,是有兩種方式的,POST和GET方式,HTTP協議是TCP/IP的一部分,有興趣的可使用Socket通信能夠模擬出 HTTP的訪問機制。咱們再說POST和GET方式,在訪問一個頁面時,瀏覽器會提交一個本地cookie提交到網站服務器,cookie的做用能夠是保 存咱們登錄網站成功後取得的一串鑰匙,也能夠是其餘的一些重要的信息。這是相當重要的一步。讓咱們步入正題。html

咱們來了解12306的登錄方式,咱們使用http跟蹤發現他的登錄的地址web

https://kyfw.12306.cn/otn/login/loginAysnSuggestjson

 

在登錄過過程當中提交了一個表單數據,包括loginserDTO.user_name、userDTO.password、randCode。我第一次看見時都有點悲催了,這麼一個大的網站,密碼傳輸居然是明文的..c#

第一個參數是咱們的用戶名、第二是密碼、第三個是校驗碼。瀏覽器

接下來咱們要作的就是獲取登錄驗證碼了。咱們看到驗證碼的地址是服務器

 

這是一個圖片的地址,咱們將這個圖片地址指向咱們的picturebox控件的Image路徑。最終的登錄界面是這樣的cookie

新建一個HttpWebRequestExtension.cs 類,加入咱們核心代碼,包括提交訂單數據,獲取網頁內容,獲取校驗碼圖片。app

複製代碼
複製代碼
/// <summary>
    /// 模擬網頁操做,提交、獲取訂單頁面數據
    /// </summary>
    public class HttpWebRequestExtension
    {
        private static string contentType = "application/x-www-form-urlencoded";
        private static string accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/x-silverlight, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-silverlight-2-b1, */*";
        private static string userAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Zune 4.7; BOIE9;ZHCN)";
        private static string referer = "https://kyfw.12306.cn/";
        /// <summary>
        /// 提交訂單數據
        /// </summary>
        /// <param name="url"></param>
        /// <param name="cookie"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public static string PostWebContent(string url, CookieContainer cookie, string param)
        {
            byte[] bs = Encoding.ASCII.GetBytes(param);
            var httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
            httpWebRequest.CookieContainer = cookie;
            httpWebRequest.ContentType = contentType;
            httpWebRequest.Accept = accept;
            httpWebRequest.UserAgent = userAgent;
            httpWebRequest.Method = "POST";
            httpWebRequest.ContentLength = bs.Length;
            using (Stream reqStream = httpWebRequest.GetRequestStream())
            {
                reqStream.Write(bs, 0, bs.Length);
            }
            var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
            Stream responseStream = httpWebResponse.GetResponseStream();
            StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8);
            string html = streamReader.ReadToEnd();

            streamReader.Close();
            responseStream.Close();

            httpWebRequest.Abort();
            httpWebResponse.Close();

            return html;
        }

        /// <summary>
        /// 獲取頁面數據
        /// </summary>
        /// <param name="url"></param>
        /// <param name="cookie"></param>
        /// <returns></returns>
        public static string GetWebContent(string url, CookieContainer cookie)
        { 
            var httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
            httpWebRequest.CookieContainer = cookie;
            httpWebRequest.ContentType = contentType;
            httpWebRequest.Referer = referer;
            httpWebRequest.Accept = accept;
            httpWebRequest.UserAgent = userAgent;
            httpWebRequest.Method = "GET";
            httpWebRequest.ServicePoint.ConnectionLimit = int.MaxValue;

            var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
            Stream responseStream = httpWebResponse.GetResponseStream();
            StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8);
            string html = streamReader.ReadToEnd();

            streamReader.Close();
            responseStream.Close();

            httpWebRequest.Abort();
            httpWebResponse.Close();

            return html;
        }

        /// <summary>
        /// 獲取網頁驗證碼圖片
        /// </summary>
        /// <param name="url"></param>
        /// <param name="cookie"></param>
        /// <returns></returns>
        public static object GetWebImage(string url, CookieContainer cookie)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Referer = referer;
            request.UserAgent = "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36";
            request.Accept = "image/webp,*/*;q=0.8";
            request.CookieContainer = cookie;
            request.ContentType = contentType;
            request.KeepAlive = true;
            request.UseDefaultCredentials = true;
          //  request.Proxy = null;
            return request.GetResponse().GetResponseStream();
        }
    }
複製代碼
複製代碼

 

而後咱們就能夠模擬登錄12306了。ide

var loginRes = HttpWebRequestExtension.PostWebContent(TrainUrlConstant.AsynSugguestUrl, cookieContainer,

                              "loginUserDTO.user_name=" + userLogin.UserName + "&&userDTO.password=" + userLogin.Password + "&&randCode=" + userLogin.VerifyCode

                              );

 

 

登錄的結果是以JSON數據格式返回的。若是你看到有 loginCheck\":\"Y\",那麼恭喜,你已經登錄上網站了。post

若是失敗了也無妨,返回的結果能夠看到登錄失敗的緣由,message:[「..」], ...表示返回的錯誤緣由,這裏就不一一列出了。

 

注意:登錄成功後保存cookie的狀態,前面強調過這是最重要的一個環節。

 

而後獲取車站信息。車站信息保存在一個JS裏面,咱們須要解碼JS。

https://kyfw.12306.cn/otn/resources/js/framework/station_name.js

 

你們使用瀏覽器打開看看,裏面是否是一個定義好的以|分隔的車站信息,咱們只須要提取出車站名稱和車站編碼。如下是個人解碼方式。

 

[csharp]  view plain copy
 
  1. var s = station.Split('=')[1].Replace("'", "").Replace(";", "").Split('|'); for (int i = 0; i < s.Length; i++) { if (i % 5 == 0) { if ((i + 1) < s.Length && (i + 4) < s.Length) { string statename = s[i + 1]; string code = s[i + 2]; cacheDic.Add(statename, code); cmbstartStation.Items.Add(statename); cmbendStation.Items.Add(statename); } } }  

 

再進一下獲取購票人信息,咱們的聯繫人的URL是 https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs,這是一個JSON數據。

 

 

JSON數據裏面包含有全部聯繫人的信息內容,包括電話、身份證號、出生年月、是否學生、性別等。有了這些基礎數據咱們就能夠刷票、購票了。讓咱們先看看剩餘的票數吧

https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={0}&leftTicketDTO.from_station={1}&leftTicketDTO.to_station={2}&purpose_codes=ADULT

 

URL裏面傳遞的數據有始發站、終點站、出發日期等信息。 咱們使用Get 方式獲取餘票信息

 var url = string.Format(TrainUrlConstant.TrainleftTicketInfo, startTime, from, to);

            var trainleftTicketInfoRes = HttpWebRequestExtension.GetWebContent(url, cookieContainer);

 

 

返回的JSON數據裏面包含有車票的標誌位,也就是上圖的secretStr,還有出發時間、軟臥數、硬臥數、軟座、硬座數等。

有了這些數據 咱們就能夠選擇自動刷票了。

 

 

 

接下來選定好坐席,車次開始搶票。

選中車次後 確認提交咱們選中的車次信息,咱們看一下他須要傳的參數信息

 

 

[csharp]  view plain copy
 
  1. var param = "secretStr=" + selectedTrainView.SrcetStr + "&train_date=" + selectedTrainView.Time + "&back_train_date=" + selectedTrainView.TotalTime + "&tour_flag=dc&purpose_codes=ADULT&query_from_station_name=" + selectedTrainView.From + "&query_to_station_name=" + selectedTrainView.To + ""; var confirmParmRes = HttpWebRequestExtension.PostWebContent(TrainUrlConstant.SubmitOrderPredicateUrl, cookieContainer, param);  

 

 

 

12306提交訂單使用的是一個訂單一個隨機的token信息,那麼在這以前咱們就必須先要獲取Token信息了

 

那麼這個表單裏面的token、key_check_ischange、leftTicketStr、train_location從哪裏來呢?這 就到了考驗耐心的時候了,通過仔細的查找發現,原來這些信息是隱藏在網頁的JS裏面。頁面地址是 https://kyfw.12306.cn/otn/confirmPassenger/initDc 不仔細看還真看不出來啊。

  

var submitPassagerRes = HttpWebRequestExtension.PostWebContent(TrainUrlConstant.SubmitOrderInitialUrl, cookieContainer, "_json_att=");

 

 

 

 

 

另外兩個參數passengerTicketStr、oldPassengerStr 是咱們選中購票人,仔細分析這串字符串,發現其中是有規律的,每個購票人是以_分隔的。逗號前第一個數據表明的是座席號,逗號的第四個數據是聯繫人,記 住須要用URL編碼格式,第6個是身份證號,第7個是手機號。

而後再獲取提交訂單前的校驗碼 https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew?module=passenger&rand=randp

pbxOrderCode.Image = Image.FromStream((Stream)HttpWebRequestExtension.GetWebImage(TrainUrlConstant.OrderValidateCodeUrl, cookie));

 

 

將咱們上面找出來的表單信息提交到網站校驗是否是有問題

 

 

[csharp]  view plain copy
 
  1. param = @"cancel_flag=2&bed_level_order_num=000000000000000000000000000000&" + selectedticketcontacts + "&" + selectedoldpasswordticketcontacts + "&tour_flag=dc&randCode=" + verifyCode + "&_json_att=&REPEAT_SUBMIT_TOKEN=" + token.Trim(); var checkOrderRes = HttpWebRequestExtension.PostWebContent(TrainUrlConstant.CheckOrderUrl, cookieContainer, param); var checkOrderRes = HttpWebRequestExtension.PostWebContent(TrainUrlConstant.CheckOrderUrl, cookieContainer, param); if (checkOrderRes != null && checkOrderRes.IndexOf("系統忙") > 0) { return; }  

 

正確的訂單返回的結果

 

 

若是以上都沒有問題的話,接下來就能夠進入到真正意義的搶票過程了。咱們看一下搶票的URL

https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue

 

 

[csharp]  view plain copy
 
  1. param = selectedticketcontacts + "&" + selectedoldpasswordticketcontacts + "&randCode=" + verifyCode + "&purpose_codes=00&key_check_isChange=" + key_check + "&leftTicketStr=" + lefttick.Trim() + "&train_location=" + train_location + "&_json_att=&REPEAT_SUBMIT_TOKEN=" + token.Trim() + ""; var confirmOrderRes = HttpWebRequestExtension.PostWebContent(TrainUrlConstant.ConfirmOrderUrl, cookieContainer, param); var errorMsg = string.Empty; Regex.Replace(submitPassagerRes, "(errMsg).*,", new MatchEvaluator(p => { if (p.Value.IndexOf("errMsg") >= 0) { errorMsg = p.Value.Substring(0, p.Value.IndexOf(",")); errorMsg = errorMsg.Replace("errMsg", "").Replace("'", "").Replace("=", "").Replace(";", "").Replace(",", "").Replace(":", ""); } return null; }));  

 

就差最後一步了。咱們看看是否是生成訂單號了。

 

  1. //等待訂單完成 var waitorderurl = string.Format(TrainUrlConstant.WaitOrderStateUrl, j, token); int id = 0; var ultimateRes = HttpWebRequestExtension.GetWebContent(waitorderurl, cookieContainer);  



 

看看返回的JSON結果裏面有沒有orderID,當orderID大於0,表示你的票已經搶到手了。趕忙登錄網站付款去吧。

相關文章
相關標籤/搜索