C#Http编程
c# 模拟 网页实现12306登陆、自动刷票、自动抢票完全篇(转)
这一篇文章,我将从头到尾教大家使用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, 0, 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('=')[1].Replace("'", "").Replace(";", "").Split('|'); for (int i = 0; i < s.Length; i++) { if (i % 5 == 0) { if ((i + 1) < s.Length && (i + 4) < s.Length) { string statename = s[i + 1]; string code = s[i + 2]; 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("系统忙") > 0) { 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") >= 0) { errorMsg = p.Value.Substring(0, p.Value.IndexOf(",")); errorMsg = errorMsg.Replace("errMsg", "").Replace("'", "").Replace("=", "").Replace(";", "").Replace(",", "").Replace(":", ""); } return null; }));
就差最后一步了。我们看看是不是生成订单号了。
- //等待订单完成 var waitorderurl = string.Format(TrainUrlConstant.WaitOrderStateUrl, j, token); int id = 0; var ultimateRes = HttpWebRequestExtension.GetWebContent(waitorderurl, cookieContainer);
看看返回的JSON结果里面有没有orderID,当orderID大于0,表示你的票已经抢到手了。赶紧登陆网站付款去吧。
C#Http编程的更多相关文章
- 从直播编程到直播教育:LiveEdu.tv开启多元化的在线学习直播时代
2015年9月,一个叫Livecoding.tv的网站在互联网上引起了编程界的注意.缘于Pingwest品玩的一位编辑在上网时无意中发现了这个网站,并写了一篇文章<一个比直播睡觉更奇怪的网站:直 ...
- JavaScript之父Brendan Eich,Clojure 创建者Rich Hickey,Python创建者Van Rossum等编程大牛对程序员的职业建议
软件开发是现时很火的职业.据美国劳动局发布的一项统计数据显示,从2014年至2024年,美国就业市场对开发人员的需求量将增长17%,而这个增长率比起所有职业的平均需求量高出了7%.很多人年轻人会选择编 ...
- 读书笔记:JavaScript DOM 编程艺术(第二版)
读完还是能学到很多的基础知识,这里记录下,方便回顾与及时查阅. 内容也有自己的一些补充. JavaScript DOM 编程艺术(第二版) 1.JavaScript简史 JavaScript由Nets ...
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- C#异步编程(一)
异步编程简介 前言 本人学习.Net两年有余,是第一次写博客,虽然写的很认真,当毕竟是第一次,肯定会有很多不足之处, 希望大家照顾照顾新人,有错误之处可以指出来,我会虚心接受的. 何谓异步 与同步相对 ...
- UE4新手之编程指南
虚幻引擎4为程序员提供了两套工具集,可共同使用来加速开发的工作流程. 新的游戏类.Slate和Canvas用户接口元素以及编辑器功能可以使用C++语言来编写,并且在使用Visual Studio 或 ...
- C#与C++的发展历程第三 - C#5.0异步编程巅峰
系列文章目录 1. C#与C++的发展历程第一 - 由C#3.0起 2. C#与C++的发展历程第二 - C#4.0再接再厉 3. C#与C++的发展历程第三 - C#5.0异步编程的巅峰 C#5.0 ...
- 猫哥网络编程系列:HTTP PEM 万能调试法
注:本文内容较长且细节较多,建议先收藏再阅读,原文将在 Github 上维护与更新. 在 HTTP 接口开发与调试过程中,我们经常遇到以下类似的问题: 为什么本地环境接口可以调用成功,但放到手机上就跑 ...
- 关于如何提高Web服务端并发效率的异步编程技术
最近我研究技术的一个重点是java的多线程开发,在我早期学习java的时候,很多书上把java的多线程开发标榜为简单易用,这个简单易用是以C语言作为参照的,不过我也没有使用过C语言开发过多线程,我只知 ...
- 异步编程 In .NET
概述 在之前写的一篇关于async和await的前世今生的文章之后,大家似乎在async和await提高网站处理能力方面还有一些疑问,博客园本身也做了不少的尝试.今天我们再来回答一下这个问题,同时我们 ...
随机推荐
- EL表达式使用之类switch语句
http://blacksonny.iteye.com/admin/blogs/1879878
- testng 提供参数
获取页面元素属性,并把属性作为参数传递个测试方法,两桶不同的写法 1. @DataProvider public Iterator<Object[]> dp() { mySleep(500 ...
- HTML之一天学会html(常用标签+网页架构)
1. 网页文件的创建 新建一个文本文件,将其命名为xxx.html或者xxx.htm(注意后缀名) 2. 简单的html页面的编写 在网页中都是通过标签来指定相应的显示内容,所有的页面内容都必须在 ...
- 使用Express创建一个简单的示例
1.安装Express 使用npm包安装工具来安装Express安装包,打开npm命令行,输入: npm install -g express 2.创建一个工程 本示例是在windows下创建的,项目 ...
- tomcat配置数据源
1.修改conf下的context.xml,在<context>标签中添加: <Resource name="jdbc/soa" auth="Conta ...
- JavaScript基础学习
什么是变量! 什么是变量?从字面上看,变量是可变的量;从编程角度讲,变量是用于储存某种/某些数值的存储器.我们可以把变量看做一个盒子, 为了区分盒子,可以用BOX1,BOX2等名称代表不同盒子,BOX ...
- Palindrome Number 回文数
判断一个数字是否是回文数,尝试不用其他额外空间. 注意: 负数也有可能成为回文数吗? 如果你想让int转为string,注意不用其他空间这个约束. 你也可以翻转一个int,但是有可能会溢出. ...
- iOS开发-清理缓存功能的实现
移动应用在处理网络资源时,一般都会做离线缓存处理,其中以图片缓存最为典型,其中很流行的离线缓存框架为SDWebImage. 但是,离线缓存会占用手机存储空间,所以缓存清理功能基本成为资讯.购物.阅读类 ...
- OpenGL ES 2.0 摄像机与投影
1.摄像机的设置 摄像机的位置坐标 摄像机的位置 摄像机up方向 Matrix.setLookAtM( mVMatrix, //存储生成矩阵元素的float[]类型数组 0, //填充起始偏移量 cx ...
- C++中的int和short int
#include <iostream> #include <string> #include <cstring> //strcpy #include <cst ...