这一篇文章,我将从头到尾教大家使用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. :( Call to a member function Table() on a non-object 错误位置

    :( Call to a member function Table() on a non-object 错误位置 $Model不是模板,是你自己先前链接数据库返回的对象...我的是改为$Form

  2. 破解TP-Link路由-嗅探PPPoE拨号密码

    如果你平时都使用路由器直接上网,那么你还记得你的宽带(ADSL)帐户名和密码吗?忘记密码后又该如何找回呢?别急,本文带你一同找回遗忘的ADSL密码.1.安全性较差的路由器(例如腾达的某些路由器):这里 ...

  3. Shortcut key for CodeBlocks

    一.not only in CodeBlocks 13.12 Undo last action Ctrl + Z //后退 Redo last action Ctrl + Shift + Z //前进 ...

  4. 【Network】一张图看懂 Reactor 与 Proactor 模型的区别

    首先来看看Reactor模式,Reactor模式应用于同步I/O的场景.我们以读操作为例来看看Reactor中的具体步骤:读取操作:1. 应用程序注册读就需事件和相关联的事件处理器2. 事件分离器等待 ...

  5. 【云计算】实战-五个Docker监控工具的对比

    [实战]五个Docker监控工具的对比 阅读目录 Docker Stats命令 CAdvisor Scout Data Dog Sensu Monitoring Framework 总结 这篇文章作者 ...

  6. Java常用排序算法+程序员必须掌握的8大排序算法

    概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大, ...

  7. 《ASP.NET1200例》<ItemTemplate>标签在html里面有什么具体的作用

    严格的来说 <ItemTemplate> 在html中无意义,他只是针对诸如 Repeater.DataList.GridView中的一个模板 至于里面的含义,你可以这样想,既然Repea ...

  8. swfit 中的类型属性说明

    swift 中不叫做类属性,叫类型属性,因为在swift中,struct 和enum也是可以有这种属性的,叫类属性明显不准. 有以下注意事项: 对于值类型(指结构体和枚举)可以定义存储型和计算型类型属 ...

  9. Android 中的selector

    今天做程序时,发现了selector 选择器不单单能用系统的自定义属性(比如,  <item android:state_selected="true" android:co ...

  10. 虚拟机下安装ubuntu后root密码设置

    ctrl+alt+t:调出命令行. 问题描述: 在虚拟机下安装了ubuntu中要输入用户名,一般情况下大家都会输入一个自己的网名或绰号之类的,密码也在这时设置过了. 但是当安装成功之后,使用命令#su ...