这一篇文章,我将从头到尾教大家使用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, , 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('=')[].Replace("'", "").Replace(";", "").Split('|');

            for (int i = ; i < s.Length; i++)

            {

                if (i %  == )

                {

                    if ((i + ) < s.Length && (i + ) < s.Length)

                    {

                        string statename = s[i + ];

                        string code = s[i + ];

                        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("系统忙") > )
{
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") >= )
{
errorMsg = p.Value.Substring(, p.Value.IndexOf(","));
errorMsg = errorMsg.Replace("errMsg", "").Replace("'", "").Replace("=", "").Replace(";", "").Replace(",", "").Replace(":", ""); } return null;
}));

就差最后一步了。我们看看是不是生成订单号了。

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

看看返回的JSON结果里面有没有orderID,当orderID大于0,表示你的票已经抢到手了。赶紧登陆网站付款去吧。

注:原创作品,只作学习分享,转载请注明来源!

c# 模拟 网页实现12306登陆、自动刷票、自动抢票完全篇的更多相关文章

  1. python3.7之12306抢票脚本实现

    悲催的12306,彻底沦为各路抢票软件的服务提供方.元旦伊始,纯粹12306官网及APP抢票,愈一周的时间,仅到手一张凌晨3:55回家的站票.为远离脑残,无奈选择抢票软件,预购年后返沪车票.BTW,研 ...

  2. 谈网页游戏外挂之用python模拟游戏(热血三国2)登陆

    看web看多了,想写写页游的外挂,其实原理是一样的,就是端口不一样协议字段你不知道,而这也提高了点技术门槛,看我们来一点一点突破这些门槛,这次我们来用python发包模拟flash的客户端登陆. 以热 ...

  3. win10 uwp 模拟网页输入

    有时候需要获得网页的 js 执行后的源代码,或者模拟网页输入,如点按钮输入文字. 如果需要实现,那么就需要用 WebView ,使用方法很简单. 首先创建一个 WebView ,接下来的所有输入都需要 ...

  4. Python 12306登陆详细分析及操作

    前面的话: 1.第一次尝试爬虫,登陆12306,有不足的地方,望大家留言告知,谢谢. 2.前面引入了一个requests模块,我不多说,大家都知道干啥的.还有config是我的一个配置文件,因为其中涉 ...

  5. ASP.NET -- WebForm -- Cookie的使用 应用程序权限设计 权限设计文章汇总 asp.net后台管理系统-登陆模块-是否自动登陆 C# 读写文件摘要

    ASP.NET -- WebForm -- Cookie的使用 ASP.NET -- WebForm --  Cookie的使用 Cookie是存在浏览器内存或磁盘上. 1. Test3.aspx文件 ...

  6. winform中webBrowser模拟网页操作中遇到的问题

    我们通过网页上传一些特殊数据的时候,由于必填项众多,数量量大的时候,会发现工作相当繁琐,前段时间做了一个winform内嵌webBrowser模拟网页上传文档的小工具,发现了许多问题,总结一下: 先说 ...

  7. 抢票季:吐槽12306 & 分享抢票经验

    又是一年一度的春运抢票季,不管你是北上.南下或者东进,在外漂泊了一年,有钱没钱总是要回家过年的. [图片来源于网络] 吐槽:12306抢票的悲伤 据说12306改版了,新版本里面除了UI这些面儿上的改 ...

  8. 马后炮之12306抢票工具(四)--抢票Demo,2014年1月9日终结版

    时隔一年多,终于朋友的忽悠下吧抢票Demo的最后一步完善了,与2014年1月9日成功生成车票. Demo仅经过自己测试,并未在高峰期进行测试,代码质量很差,因为赶工,套用去年模板并未使用设计模式. 代 ...

  9. Python实例--12306的抢票功能

    基础知识学习 目标: 通过python程序实现自动登录下单功能 知识点: Selenium + 云打码 + Python 学习链接: 1. Python学习--Selenium模块 2. Python ...

随机推荐

  1. jquery 调用数据

    <body> <div id="aa" style="">hello</div> <div class="b ...

  2. Linux下使用fdisk扩展分区容量

    导读 我们管理的服务器可能会随着业务量的不断增长造成磁盘空间不足的情况,比如:共享文件服务器硬盘空间不足,在这个时候我们就需要增加磁盘空间,来满足线上的业务:又或者我们在使用linux的过程中, 有时 ...

  3. poj1258 Agri-Net 最小生成树

    Agri-Net Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 44032   Accepted: 18001 Descri ...

  4. [ruby on rails] 跟我学之(5)显示所有数据

    之前的index页,显示的是hello world,现在将其修改为显示我们在rails console里面录入的数据. 1. 修改action 如之前的章节<[ruby on rails] 跟我 ...

  5. TFS增加dataserver

    通过之前的努力,已经搭建好了一套基本的tfs环境,包括一台nameserver和一台dataserver以及独立的nginx-tfs,而在实际应用中的分布式文件系统,只有一台dataserver明显是 ...

  6. 深入了解PooledConnectionFactory CachingConnectionFactory Sin

    深入理解PooledConnectionFactory CachingConnectionFactory SingleConnectionFactory PooledConnectionFactory ...

  7. Rehashing

    The size of the hash table is not determinate at the very beginning. If the total size of keys is to ...

  8. dom 中事件

    阻止表单提交: function aa(){ return false; } function bb(event){ event.preventDefault(); } 事件不再派发: <!DO ...

  9. ECharts2.2.0 兼容IE8

    IE 8,ECharts2.2.0 版本,demo的各个功能均正常显示在IE8上面, 但是我在真正做的时候,我的html却不能显示,画面乱了,而且function也不能用, 都准备用1.4.1版本了, ...

  10. Android 和iOS中 View的滚动

    在最近的程序中用到了Android中的View的滚动,记录一下,待总结.