C#实现12306自动登录的方法
作者:yangyoushan 发布时间:2023-11-07 13:20:27
标签:C#,登录
依然使用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();
}
}


猜你喜欢
- Struts2 * Struts2 * 的概念和Spring Mvc * 一样。1.Struts2 * 是在访问某个Action或Actio
- 1. 概述:将一个具体类的实例化交给一个静态工厂方法来执行,它不属于GOF的23种设计模式,但现实中却经常会用到2. 模式中的角色2.1 工
- 为了完成以上的需求,我们就需要模拟浏览器浏览网页,得到页面的数据在进行分析,最后把分析的结构,即整理好的数据写入数据库。那么我们的思路就是:
- 查了网上的资料,有比较全面的,但有一个问题就是容易出现一个文字和框子不符合的现象。(仔细看,蓝色字母和背景的灰色有空白)要消除这个空白,很简
- 如果对共享的可变数据的访问不能同步,其后果非常可怕,即使这个变量是原子可读写的。下面考虑一个线程同步方面的问题。对于线程同步,Java类库提
- 使用Kotlin的Lambda表达式,我们可以抛弃回调接口的使用。只需设置希望后面会被调用的函数即可。示例如下新建一个Kotlin类clas
- 本文以实例形式讲述了C#应用BindingSource实现数据同步的方法,对C#数据库程序开发来说具有一定的参考借鉴价值。具体实现方法如下:
- 在搜索引擎的开发中,我们需要对Html进行解析。本文介绍C#解析HTML的两种方法。AD: 在搜索引擎的开发中,我们需要对网页的Html内容
- 环境信息名称版本号Spring Boot2.4.5Idea2021.3.2服务端实现导入依赖<dependency>  
- 一、概述在微服务架构中,我们将系统拆分成了很多服务单元,各单元的应用间通过服务注册与订阅的方式互相依赖。由于每个单元都在不同的进程中运行,依
- Android RecyclerView小白今天第一次接触RecyclerView,前辈大神告诉我这是一个很神奇的控件,一看就是
- AndroidMaifest.xml中声明权限<!-- 声明所有需要的权限(包括普通权限和危险权限) --><uses-p
- strcpy函数详解如下1.函数介绍1.1.函数接口char * __cdecl strcpy(char * dst, const char
- 一、举个栗子public class BeanServiceImpl implements BeanService {}@Configura
- 简介CAS的原理其实很简单,为了保证在多线程环境下我们的更新是符合预期的,或者说一个线程在更新某个对象的时候,没有其他的线程对该对象进行修改
- 在Android实现没有标题栏的方法有两种:在代码中添加requestWindowFeature(Window.FEATURE_NO_TIT
- 1. 前言不知道小伙伴对于日期字段,在项目中都是如何处理的,是单独给每个字段都自定义日期格式还是做全局格式设置?这个我之前啊,是
- Android使用GridView实现日历功能示例,代码有点多,发个图先:如果懒得往下看的,可以直接下载源码吧,最近一直有人要,由于时间太久
- startJVM是加载jvm用的方法。在JPype,apache mod等等很多地方都用到。但凡要用其他语言来加载jvm进程,就要用到这个。
- 从功能上说,可以分为两部分,分布式功能和数据功能。分布式功能主要是节点集群及集群附属功能如restful借口、集群性能检测功能等,数据功能主