依然使用IE9的捕獲參數,做了一個12306的登錄功能。參照了網上童鞋們的做法。
其他都和前面幾篇讀取余票、票價一樣,不過登錄要用到證書的問題,這個參考了一個網上的例子。
不過12306會隨時變化,下面的登錄不一定一直都能成功。如果12306有變化,大家可以根據變化對代碼做修改。總之使用的方法不變,就是捕獲參數和url,然後自己補充參數。
效果如下:
項目名稱:Test12306AutoLogin;
環境:.net 4.0,Visual studio 2010;
項目圖:
直接貼代碼了。
核心代碼如下,
信任證書代碼:
public class Messenger { public Messenger() { } public void Register(string message, Action callback) { this.Register(message, callback, null); } public void Register<T>(string message, Action<T> callback) { this.Register(message, callback, typeof(T)); } void Register(string message, Delegate callback, Type parameterType) { if (String.IsNullOrEmpty(message)) throw new ArgumentException("'message' cannot be null or empty."); if (callback == null) throw new ArgumentNullException("callback"); this.VerifyParameterType(message, parameterType); _messageToActionsMap.AddAction(message, callback.Target, callback.Method, parameterType); } [Conditional("DEBUG")] void VerifyParameterType(string message, Type parameterType) { Type previouslyRegisteredParameterType = null; if (_messageToActionsMap.TryGetParameterType(message, out previouslyRegisteredParameterType)) { if (previouslyRegisteredParameterType != null && parameterType != null) { if (!previouslyRegisteredParameterType.Equals(parameterType)) throw new InvalidOperationException(string.Format( "The registered action's parameter type is inconsistent with the previously registered actions for message '{0}'.\nExpected: {1}\nAdding: {2}", message, previouslyRegisteredParameterType.FullName, parameterType.FullName)); } else { // One, or both, of previouslyRegisteredParameterType or callbackParameterType are null. if (previouslyRegisteredParameterType != parameterType) // not both null? { throw new TargetParameterCountException(string.Format( "The registered action has a number of parameters inconsistent with the previously registered actions for message \"{0}\".\nExpected: {1}\nAdding: {2}", message, previouslyRegisteredParameterType == null ? 0 : 1, parameterType == null ? 0 : 1)); } } } } public void NotifyColleagues(string message, object parameter) { if (String.IsNullOrEmpty(message)) throw new ArgumentException("'message' cannot be null or empty."); Type registeredParameterType; if (_messageToActionsMap.TryGetParameterType(message, out registeredParameterType)) { if (registeredParameterType == null) throw new TargetParameterCountException(string.Format("Cannot pass a parameter with message '{0}'. Registered action(s) expect no parameter.", message)); } var actions = _messageToActionsMap.GetActions(message); if (actions != null) actions.ForEach(action => action.DynamicInvoke(parameter)); } public void NotifyColleagues(string message) { if (String.IsNullOrEmpty(message)) throw new ArgumentException("'message' cannot be null or empty."); Type registeredParameterType; if (_messageToActionsMap.TryGetParameterType(message, out registeredParameterType)) { if (registeredParameterType != null) throw new TargetParameterCountException(string.Format("Must pass a parameter of type {0} with this message. Registered action(s) expect it.", registeredParameterType.FullName)); } var actions = _messageToActionsMap.GetActions(message); if (actions != null) actions.ForEach(action => action.DynamicInvoke()); } private class MessageToActionsMap { internal MessageToActionsMap() { } internal void AddAction(string message, object target, MethodInfo method, Type actionType) { if (message == null) throw new ArgumentNullException("message"); if (method == null) throw new ArgumentNullException("method"); lock (_map) { if (!_map.ContainsKey(message)) _map[message] = new List<WeakAction>(); _map[message].Add(new WeakAction(target, method, actionType)); } } internal List<Delegate> GetActions(string message) { if (message == null) throw new ArgumentNullException("message"); List<Delegate> actions; lock (_map) { if (!_map.ContainsKey(message)) return null; List<WeakAction> weakActions = _map[message]; actions = new List<Delegate>(weakActions.Count); for (int i = weakActions.Count - 1; i > -1; --i) { WeakAction weakAction = weakActions[i]; if (weakAction == null) continue; Delegate action = weakAction.CreateAction(); if (action != null) { actions.Add(action); } else { // The target object is dead, so get rid of the weak action. weakActions.Remove(weakAction); } } // Delete the list from the map if it is now empty. if (weakActions.Count == 0) _map.Remove(message); } // Reverse the list to ensure the callbacks are invoked in the order they were registered. actions.Reverse(); return actions; } internal bool TryGetParameterType(string message, out Type parameterType) { if (message == null) throw new ArgumentNullException("message"); parameterType = null; List<WeakAction> weakActions; lock (_map) { if (!_map.TryGetValue(message, out weakActions) || weakActions.Count == 0) return false; } parameterType = weakActions[0].ParameterType; return true; } readonly Dictionary<string, List<WeakAction>> _map = new Dictionary<string, List<WeakAction>>(); } private class WeakAction { internal WeakAction(object target, MethodInfo method, Type parameterType) { if (target == null) { _targetRef = null; } else { _targetRef = new WeakReference(target); } _method = method; this.ParameterType = parameterType; if (parameterType == null) { _delegateType = typeof(Action); } else { _delegateType = typeof(Action<>).MakeGenericType(parameterType); } } internal Delegate CreateAction() { // Rehydrate into a real Action object, so that the method can be invoked. if (_targetRef == null) { return Delegate.CreateDelegate(_delegateType, _method); } else { try { object target = _targetRef.Target; if (target != null) return Delegate.CreateDelegate(_delegateType, target, _method); } catch { } } return null; } internal readonly Type ParameterType; readonly Type _delegateType; readonly MethodInfo _method; readonly WeakReference _targetRef; } readonly MessageToActionsMap _messageToActionsMap = new MessageToActionsMap(); }登錄的所有方法類:
public class Login12306Manager { private static readonly Messenger s_messenger = new Messenger(); public static Messenger SMessenger { get { return s_messenger; } } public const string APPEND_MESSAGE = "append_message"; public static string afterLoginCookie; private static string beforLoginCookie; static Login12306Manager() { SetCertificatePolicy(); } /// <summary> /// 登 錄 /// </summary> public static string Login(string userName,string password, string randomCode) { string resultHtml = string.Empty; try { string loginRand= DoGetLoginRand(); HttpWebRequest request = (HttpWebRequest)WebRequest.Create (@"https://dynamic.12306.cn/otsweb/loginAction.do?method=login"); request.Accept = @"text/html, application/xhtml+xml, */*"; request.UserAgent = @"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"; request.Referer = @"https://dynamic.12306.cn/otsweb/loginAction.do?method=init"; request.ContentType = @"application/x-www-form-urlencoded"; request.Headers[HttpRequestHeader.Cookie] = beforLoginCookie; request.Method = "POST"; byte[] buffer = new byte[0]; string parameter = @"loginRand={0}&refundLogin=N&refundFlag=Y&isClick=&form_tk=null&loginUser.user_name={1}&nameErrorFocus=&user.password={2}&passwordErrorFocus=&randCode={3}&randErrorFocus=&NDU0NzY4NA%3D%3D=Nzg4ZDAxMGNkYTZlMTRjZA%3D%3D&myversion=undefined"; parameter = string.Format(parameter, loginRand, userName, password, randomCode); buffer = Encoding.UTF8.GetBytes(parameter); request.ContentLength = buffer.Length; using (Stream writer = request.GetRequestStream()) { writer.Write(buffer, 0, buffer.Length); writer.Flush(); } using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { afterLoginCookie = response.GetResponseHeader("Set-cookie"); using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8)) { resultHtml = reader.ReadToEnd(); resultHtml = ProcessLoginResult(resultHtml); } } } catch{ } return resultHtml; } /// <summary> /// 刷新驗證碼 /// </summary> public static string RefreshCode() { string randImageUrl = string.Empty; try { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(@"https://dynamic.12306.cn/otsweb/passCodeNewAction.do?module=login&rand=sjrand&{0}", new Random().Next(10000, 1000000))); request.Accept = @"image/png, image/svg+xml, image/*;q=0.8, */*;q=0.5"; request.UserAgent = @"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"; request.Referer = @"https://dynamic.12306.cn/otsweb/loginAction.do?method=init"; request.Method = "GET"; using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { beforLoginCookie = response.GetResponseHeader("Set-cookie"); beforLoginCookie = Regex.Replace(beforLoginCookie, "path(?:[^,]+),?", "", RegexOptions.IgnoreCase); using (Stream reader = response.GetResponseStream()) { string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, new Random().Next(10000, 99999) + @"loginRandCode.JPEG"); using (FileStream file = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write)) { reader.CopyTo(file); } randImageUrl = path; } } } catch { } return randImageUrl; } private static string DoGetLoginRand() { string loginRand=string.Empty; try { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"https://dynamic.12306.cn/otsweb/loginAction.do?method=loginAysnSuggest"); request.Accept = @"application/json, text/javascript, */*"; request.UserAgent = @"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"; request.Referer = @"https://dynamic.12306.cn/otsweb/loginAction.do?method=init"; request.Headers[HttpRequestHeader.Cookie] = beforLoginCookie; request.Method = "POST"; byte[] buffer = new byte[0]; buffer = Encoding.UTF8.GetBytes(string.Empty); request.ContentLength = buffer.Length; using (Stream writer = request.GetRequestStream()) { writer.Write(buffer, 0, buffer.Length); writer.Flush(); } using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8)) { string result = reader.ReadToEnd(); var loginRandContent = JsonConvert.DeserializeObject<BeforLoginRnad>(result); loginRand = loginRandContent.loginRand; } } } catch {} return loginRand; } /// <summary> /// 處理登錄結果 /// </summary> /// <param name="html">登錄後返回的html文本</param> private static string ProcessLoginResult(string html) { string m_msgPattern = "message[^\"]+\"(?'message'[^\"]+)\";"; string m_isLoginPatter = "isLogin\\s*=\\s*(?<val>.+)\n"; string m_loginUserNamePattern = "u_name\\s*=\\s*['\"](?<name>.+)['\"]"; if (html.Contains("請輸入正確的驗證碼")) { return "驗證碼錯誤"; } else if (html.Contains("當前訪問用戶過多")) { return "當前訪問用戶過多,請稍後再試..."; } else { var match0 = Regex.Match(html, m_msgPattern, RegexOptions.Compiled); if (match0.Success) { string text = match0.Groups["message"].Value; if (text.Contains("密碼") || text.Contains("登錄名不存在")) { return "用戶名或者密碼錯誤"; } else { return text; } } var match = Regex.Match(html, m_isLoginPatter, RegexOptions.Compiled); if (match.Success && (match.Groups["val"].Value.Trim().ToLower() == "true")) { match = Regex.Match(html, m_loginUserNamePattern, RegexOptions.Compiled); if (match.Success) { string name = match.Groups["name"].Value; return "登錄成功:" + name; } else { return "登錄失敗,未知錯誤"; } } else { return "登錄失敗!!!"; } } } /// <summary> /// Sets the cert policy. /// </summary> private static void SetCertificatePolicy() { ServicePointManager.ServerCertificateValidationCallback += RemoteCertificateValidate; } /// <summary> /// Remotes the certificate validate. /// </summary> private static bool RemoteCertificateValidate( object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error) { SMessenger.NotifyColleagues(APPEND_MESSAGE, "信任任何證書..."); return true; } } public class BeforLoginRnad { public string loginRand { get; set; } public string randError { get; set; } }注意登錄時,主要的正文是:
<Window x:Class="Test12306AutoLogin.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow"> <StackPanel> <Grid> <Grid.Resources> <Style TargetType="TextBlock"> <Setter Property="FontFamily" Value="Microsoft YaHei"/> <Setter Property="FontSize" Value="20"/> <Setter Property="VerticalAlignment" Value="Center"/> </Style> <Style TargetType="TextBox"> <Setter Property="FontSize" Value="20"/> <Setter Property="MinWidth" Value="300"/> <Setter Property="Height" Value="50"/> <Setter Property="VerticalAlignment" Value="Center"/> </Style> <Style TargetType="PasswordBox"> <Setter Property="FontSize" Value="20"/> <Setter Property="MinWidth" Value="300"/> <Setter Property="Height" Value="50"/> <Setter Property="VerticalAlignment" Value="Center"/> </Style> </Grid.Resources> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"/> <ColumnDefinition Width="auto"/> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="用戶名:"/> <TextBox Grid.Row="0" Grid.Column="1" x:Name="txtUserName"/> <TextBlock Grid.Row="1" Grid.Column="0" Text="密 碼:"/> <PasswordBox Grid.Row="1" Grid.Column="1" x:Name="txtPassword"/> <TextBlock Grid.Row="3" Grid.Column="0" Text="驗證碼"/> <StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center"> <TextBox x:Name="txtRandCode" Width="150"/> <Image x:Name="imageRandCode" Width="70"/> <Button Content="刷新驗證碼" Height="30" Width="80" Click="ButtonRefreshRandCode_Click" /> </StackPanel> </Grid> <Button Content="登 錄" Width="150" Height="50" Click="ButtonLogin_Click" /> <RichTextBox x:Name="rtxResultContent" MinHeight="200"/> </StackPanel> </Window>後台代碼:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Loaded += new RoutedEventHandler(MainWindow_Loaded); } void MainWindow_Loaded(object sender, RoutedEventArgs e) { DoRefreshRandCode(); } private void DoRefreshRandCode() { string imageRandUrl = Login12306Manager.RefreshCode(); if (File.Exists(imageRandUrl)) { ImageSource src = (ImageSource)(new ImageSourceConverter().ConvertFromString(imageRandUrl)); this.imageRandCode.Source = src; } this.txtRandCode.Text = string.Empty; } /// <summary> /// 登錄 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ButtonLogin_Click(object sender, RoutedEventArgs e) { string userName = this.txtUserName.Text; string password = this.txtPassword.Password; string randCode = this.txtRandCode.Text; if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(randCode)) { MessageBox.Show("請填寫完整信息"); return; } string html = Login12306Manager.Login(userName, password, randCode); System.Windows.Documents.FlowDocument doc = this.rtxResultContent.Document; doc.Blocks.Clear(); this.rtxResultContent.AppendText(html); } /// <summary> /// 刷新驗證碼 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ButtonRefreshRandCode_Click(object sender, RoutedEventArgs e) { DoRefreshRandCode(); } }代碼下載