1. 程式人生 > >c#模擬網頁實現12306登陸、自動刷票、自動搶票完全篇

c#模擬網頁實現12306登陸、自動刷票、自動搶票完全篇

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

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

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

 

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

第一個引數是我們的使用者名稱、第二是密碼、第三個是校驗碼。

接下來我們要做的就是獲取登陸驗證碼了。我們看到驗證碼的地址是

 

這是一個圖片的地址,我們將這個圖片地址指向我們的picturebox控制元件的Image路徑。最終的登陸介面是這樣的

新建一個HttpWebRequestExtension.cs 類,加入我們核心程式碼,包括提交訂單資料,獲取網頁內容,獲取校驗碼圖片。

/// <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了。

var loginRes = HttpWebRequestExtension.PostWebContent(TrainUrlConstant.AsynSugguestUrl, cookieContainer, "loginUserDTO.user_name=" + userLogin.UserName + "&&userDTO.password=" + userLogin.Password + "&&randCode=" + userLogin.VerifyCode );

登陸的結果是以JSON資料格式返回的。如果你看到有 loginCheck\":\"Y\",那麼恭喜,你已經登陸上網站了。

如果失敗了也無妨,返回的結果可以看到登陸失敗的原因,message:[“..”], ...表示返回的錯誤原因,這裡就不一一列出了。

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

然後獲取車站資訊。車站資訊儲存在一個JS裡面,我們需要解碼JS。

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

 

大家使用瀏覽器開啟看看,裡面是不是一個定義好的以|分隔的車站資訊,我們只需要提取出車站名稱和車站編碼。以下是我的解碼方式。

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,還有出發時間、軟臥數、硬臥數、軟座、硬座數等。

有了這些資料 我們就可以選擇自動刷票了。

 

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

選中車次後 確認提交我們選中的車次資訊,我們看一下他需要傳的引數資訊

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)); 

將我們上面找出來的表單資訊提交到網站校驗是不是有問題

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

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; }));


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

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


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

相關推薦

c#模擬網頁實現12306登陸自動自動全篇

 這一篇文章,我將從頭到尾教大家使用c#模擬網頁面登陸12306網站,自動刷票,選擇訂票人,到最後一步提交訂單。研究過HTTP協議的童鞋們都知道,我們在訪問網站時,是有兩種方式的,POST和GET方式,HTTP協議是TCP/IP的一部分,有興趣的可以使用Socket通訊可

C#模擬PrtScn實現截屏

title cat sin exception dal mage try rect ptr 有了之前的基礎知識了解,如今開始實現PrtScn和Alt+PrtScn。 首先新建一個WPF應用程序,命名為PrintscreenAndAltPrintScre

Java併發(十八):阻塞佇列BlockingQueue BlockingQueue(阻塞佇列)詳解 二叉堆(一)之 圖文解析 和 C語言的實現 多執行緒程式設計:阻塞併發佇列的使用總結 Java併發程式設計:阻塞佇列 java阻塞佇列 BlockingQueue(阻塞佇列)詳解

阻塞佇列(BlockingQueue)是一個支援兩個附加操作的佇列。 這兩個附加的操作是:在佇列為空時,獲取元素的執行緒會等待佇列變為非空。當佇列滿時,儲存元素的執行緒會等待佇列可用。 阻塞佇列常用於生產者和消費者的場景,生產者是往佇列裡新增元素的執行緒,消費者是從佇列裡拿元素的執行緒。阻塞佇列就是生產者

C++模擬委託實現

最近這段時間在仿造.net的部分庫,寫一個小型的C++類庫。既然要模仿.net,那麼委託是少不了的。 本來想直接使用boost::function, 但他沒有實現operator==, 不太好實現多播委託。 boost :: signal 庫需要呼叫編譯好的動態庫,不符合

前端工具-瀏覽器同步測試(自動熱加載)

escape star 熱加載 部分 rsync top class 訪問 請求 Browsersync 官網:https://www.browsersync.io/ 中文:http://www.browsersync.cn/ Gulp中使用 http://www.brow

CC++VCMFC網頁自動註冊登陸發帖留言 QQ註冊QQ申請器原始碼原始碼

參考資料: 說說這類軟體最常見的使用方式吧。 也許你經常看到有人釋出了以下這類日誌或訊息 1】今天又賺了兩百話費,趕快撥打110120去領取吧...... 2】淘寶員工內部購物通道...... 3】看片加110120...... 4】手機換號了,給我充點話費吧....... ....... 當然,

C#利用HttpWebRequestHttpWebResponse呼叫12306介面,實現登入

【免責申明】本文只為學習使用,若有用作商業、其他行為,與本人無關。 使用工具 - UI bootstrap - 後臺C# - 外掛 datetimepicker.js,select.js UI介面效果預覽 UI介面原始碼 <!DOCTYPE html&

Python-webbrowser實現自動開啟關定時開啟關閉網頁/重新整理網頁

webbrowser- 方便的Web瀏覽器控制器,是Python一個模組,可實現自動開啟關、定時開啟關閉網頁/重新整理網頁,在Unix下,圖形瀏覽器在X11下更受歡迎,但如果圖形瀏覽器不可用或X11顯示器不可用,則將使用文字模式瀏覽器。如果使用文字模式瀏覽器,則呼叫程序將阻塞

c++】模擬實現迴圈佇列 三種方法(標識浪費一個空間計數器)

什麼是迴圈佇列? 為充分利用向量空間,克服”假溢位“現象的方法:將向量空間想象為一個首尾相接的圓環,並稱這種向量為迴圈向量。儲存在其中的佇列稱為迴圈佇列(Circular Queue)。 假象成如圖: 但實際上儲存空間還是一段連續的空間。 空佇列: 當有元素入隊時:

C# 用tabcontrol實現窗體類似網頁排版的顯示

code star tar 做的 ide 切換 drop rri all 這裏做的比較簡陋,可以美化下 吧form設置位非頂級控件,直接放在tabcontro裏邊,然後實現tabcontrol的拖拽移除tabpage顯示form以及添加tabpage mousemove的觸

JAVAEE——struts2_04:自定義攔截器struts2標簽登陸功能和校驗登陸攔截器的實現

strac htm logs transacti 標識 area 返回 ftw jsp 一、自定義攔截器   1.架構      2.攔截器創建 //攔截器:第一種創建方式 //攔截器生命周期:隨項目的啟動而創建,隨項目關閉而銷毀 public class MyInt

基於C#實現的自動化測試框架:發布自動觸發自動化回歸測試

exc 時間流 測試用例 出現 服務器 text types filter txt 接口自動化測試用例完成以後,以前都是發布以後手動運行測試用例。雖然手動運行下腳本也就是一個F5的事情,但是離自動化測試的標準差得很遠。這兩天有了個大膽的想法,想要實現以下發布時直接觸發自動化

HTTP模擬工具【C#/Winform源碼】Json綁定TreeView控件使用了MetroModernUIRestSharpDapper.NetNewtonsoft.JsonSmartThreadPool這幾個主要開源框架

type form num -m 請求 resource dap bool dev HTTP模擬工具 開發語言:C#/Winform開發工具:Visual Studio 2017數據庫: SQLite使用框架:界面-MetroModernUI

使用C#實現鼠標進入按鍵範圍後按鍵自動窗體內位置移動

9.png new -1 poi 窗體程序 事件 技術 mage 最大 新建winform窗體程序 添加button控件 在事件中選擇 MouseEnter 代碼如下 private void button_MouseEnter(object sender, E

C語言中存儲類別又分為四類:自動(auto)靜態(static)寄存器的(register)和外部的(extern)。

字符變量 修飾 例如 register ext 進行 適合 sta -- 除法運算中註意: 如果相除的兩個數都是整數的話,則結果也為整數,小數部分省略,如8/3 = 2;而兩數中有一個為小數,結果則為小數,如:9.0/2 = 4.500000。 取余運算中註意: 該運算只適

fis3實現前端網頁實時新(自動新)

前端網 keyword nodejs spec 前端 自動 star running 只需要 默認已經裝好了node, 沒有裝的話跳轉 node官網 進行下載 首先安裝fis3 npm install -g fis3 安裝完成後執行 fis3 -v 判斷是否安裝成功,

Python模擬瀏覽器實現網頁訪問

Python 模擬瀏覽器訪問 模擬瀏覽器請求數據: import socket # 創建TCP鏈接 tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # DNS 解析和鏈接HTTP服務器 tcp_socket.connect

C# 模擬登入微博,實現分享

sps des item bject dto bstr 分享 ring 求分享 最近自己想模擬微博登入(無驗證碼的情況),想分享一些歌曲。很多代碼都是網上找到,現在整理一下。 1、加載我需要分享頁面的數據。 var httpReq =

Python3.6實現12306火車票自動,附源碼

python 爬蟲 web 編程 程序員Python(發音:英[?pa?θ?n],美[?pa?θɑ:n]),是一種面向對象、直譯式電腦編程語言,也是一種功能強大的通用型語言,已經具有近二十年的發展歷史,成熟且穩定。它包含了一組完善而且容易理解的標準庫,能夠輕松完成很多常見的任務。它的語法非常簡捷和清晰,與其它

MongoDB分片群集(實現分片服務啟用分片服務管理單點故障模擬

Opens 取數 page use chmod 組成 壓力 多個 clu MongoDB分片概述 1、什麽是分片 高數據量和吞吐量的數據庫應用會對單機的性能造成較大壓力,大的查詢量會將單機的CPU耗盡,大的數據量對單機的存儲壓力較大,最終會耗盡系統的內存而將壓力轉移到磁盤