编程乐趣:C#实现12306自动登录(2013年11月27)

来源:互联网 发布:类似记事本的软件 编辑:程序博客网 时间:2024/06/04 18:21
依然使用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; }    }
注意登录时,主要的正文是:

                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",它有三个参数,登录时的随机码,用户名,密码和验证码组成。


调用如下:
前台wpf代码:
<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();        }    }
代码下载
原创粉丝点击