准备工作

  1. 下载用于解析JSON的类库(Newtonsoft.Json.dll)(下载地址:Newtonsoft.Json.dll);
  2. 在C#项目中添加对其的引用。
  3. 在程序中导入命名空间:
    1
    2
    3
    4
    5
    
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    using System.Net;
    using System.Text.RegularExpressions;
    using System.Xml;
    

思路

API地址

在浏览器中打开12306主页,点击余票查询,并使用浏览器中的开发者工具,进行抓包。这里,我们查询的是2018.06.25的上海至长沙的车票信息。

此时浏览器发送请求的地址为:
https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2018-06-25&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=CSQ&purpose_codes=ADULT
由此,我们不难分析出API地址为:

1
2
string url="https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={0}&leftTicketDTO.from_station={1}&leftTicketDTO.to_station={2}&purpose_codes={3}";
url=string.Format(url,date,from,to,id);

其中,date代表出发日(格式为yyyy-MM-dd),from代表出发站的代码,to代表到达站的代码,id代表车票类型(成人为ADULT,学生为OX00);

车站代码

车站对应的代码可在此下载:https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9055
我们可以使用正则表达式从中提取出关键信息,并保存在一个XML文档中,便于以后的使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
     public static void StationNameXml(){string nowpath = Environment.CurrentDirectory;if (Directory.Exists(nowpath+"/Data")==false)Directory.CreateDirectory(nowpath + "/Data");//创建新路径if (File.Exists(nowpath + "/Data/StationName.xml") == true)return;HttpWebRequest request;HttpWebResponse response;string url=@"https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9055";request = (HttpWebRequest)WebRequest.Create(url);request.Method = "GET";request.Credentials = CredentialCache.DefaultCredentials;request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36";string str = string.Empty;try{response = (HttpWebResponse)request.GetResponse();if (response.StatusCode == HttpStatusCode.OK){StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("UTF-8"));str = sr.ReadToEnd();sr.Close();response.Close();//关闭response响应流}elsethrow new Exception();}catch(Exception e){Console.WriteLine(e.Message);}string pattern = @"@\S{2,5}?[|](?<Name>\S{1,8}?)[|](?<Code>\S{3,5}?)[|]";Regex r = new Regex(pattern, RegexOptions.ExplicitCapture);XmlDocument xmldoc = new XmlDocument();XmlDeclaration xmldecl;xmldecl = xmldoc.CreateXmlDeclaration("1.0", "UTF-8", null);//加入XML的声明段落xmldoc.AppendChild(xmldecl);XmlElement root = xmldoc.CreateElement("", "Dict", "");//加入一个根元素xmldoc.AppendChild(root);MatchCollection mc = r.Matches(str);foreach (Match m in mc){GroupCollection group = m.Groups;XmlNode node = xmldoc.CreateNode(XmlNodeType.Element, "Pair", null);XmlNode temp = xmldoc.CreateNode(XmlNodeType.Element, "Station", null);temp.InnerText = group["Name"].Value; node.AppendChild(temp);XmlNode temp2 = xmldoc.CreateNode(XmlNodeType.Element, "Code", null);temp2.InnerText = group["Code"].Value;node.AppendChild(temp2);root.AppendChild(node);}xmldoc.Save(nowpath+"/Data/"+"StationName.xml");}


依据中文车站名获取对应车站代码的方法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static string StationCode(string StationName)
{XmlDocument doc = new XmlDocument();doc.Load(Environment.CurrentDirectory + "/Data/StationName.xml");XmlNodeList data = doc.DocumentElement.ChildNodes;foreach (XmlNode node in data){XmlElement xe = (XmlElement)node;XmlNodeList xnl0 = xe.ChildNodes;string s = xnl0.Item(0).InnerText;if(s==StationName){return xnl0.Item(1).InnerText;}}return string.Empty;
}

数据模型

为了存储查询结果,我们需要构建一个车票的数据模型,具体如下;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
  public class Ticket{#region 数据模型private string trainid=string.Empty;//车次private string fromstation = "--";//出发站private string totation = "--";//到达站private string ticketdate = string.Empty;//车票日期private string starttime = "--";//发车时间private string arrivetime = "--";//到达时间private string lastedtime = "--";//历时private string tzNum = "--";//特等座private string ydNum = "--";//一等座private string edNum = "--";//二等座private string grwNum = "--";//高级软卧private string rwNum = "--";//软卧private string dwNum = "--";//动卧private string ywNum = "--";//硬卧private string rzNum = "--";//软座private string yzNum = "--";//硬座private string wzNum = "--";//无座private string qtNum = "--";//其他private string identity = string.Empty;//车票类型(学生/成人)public string TrainId { get { return trainid; } set { trainid = value; } }public string FromStation { get { return fromstation; } set { fromstation = value; } }public string ToStation { get { return totation; } set { totation = value; } }public string TicketDate { get { return ticketdate; } set { ticketdate = value; } }public string StartTime { get { return starttime; } set { starttime = value; } }public string ArriveTime { get { return arrivetime; }set { arrivetime = value; } }public string LastedTime { get { return lastedtime; } set { lastedtime = value; } }public string TzNum { get { return tzNum; } set { tzNum = value; } }public string YdNum { get { return ydNum; } set { ydNum = value; } }public string EdNum { get { return edNum; } set { edNum = value; } }public string GrwNum { get { return grwNum; } set { grwNum = value; } }public string RwNum { get { return rwNum; } set { rwNum = value; } }public string DwNum { get { return dwNum; } set { dwNum = value; } }public string YwNum {get{ return ywNum; }set{ ywNum = value; } }public string RzNum {get{ return rzNum; }set{ rzNum = value; } }public string YzNum {get{ return yzNum; }set{ yzNum = value; } }public string WzNum {get{ return wzNum; }set{ wzNum = value; } }public string QtNum {get{ return qtNum; }set{ qtNum = value; } }public string Identity { get { return identity; }set { identity = value; } }#endregionpublic Ticket() { }public Dictionary<string, string> getMap(){Dictionary<string, string> result = new Dictionary<string, string>();result.Add("车次", trainid);result.Add("出发站", FromStation);result.Add("到达站", ToStation);result.Add("发车时间", StartTime);result.Add("到达时间", ArriveTime);result.Add("历时", LastedTime);result.Add("特等座", TzNum);result.Add("一等座", YdNum);result.Add("二等座", EdNum);result.Add("高级软卧", GrwNum);result.Add("软卧", RwNum);result.Add("动卧", DwNum);result.Add("硬卧", YwNum);result.Add("软座", RzNum);result.Add("硬座", YzNum);result.Add("无座", WzNum);result.Add("其他", QtNum);return result;}
}

解析JSON


返回的报文为一个JSON格式的字符串,里面包含有各类车票信息、发车时间以及历时,为了对其进行解析,我们需要下载一个用于解析JSON的类库(下载地址:Newtonsoft.Json.dll)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
private static Ticket[] SpiderTicket(string date, string from, string to, string id){HttpWebRequest request;HttpWebResponse response;string url = "https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={0}&leftTicketDTO.from_station={1}&leftTicketDTO.to_station={2}&purpose_codes={3}";url = string.Format(url, date, from, to, id);request = (HttpWebRequest)WebRequest.Create(url);request.Method = "GET";request.Credentials = CredentialCache.DefaultCredentials;request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36";string str = string.Empty;try{response = (HttpWebResponse)request.GetResponse();if (response.StatusCode == HttpStatusCode.OK){StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("UTF-8"));str = sr.ReadToEnd();sr.Close();response.Close();//关闭response响应流}elsethrow new Exception();}catch (Exception e){Console.WriteLine(e.Message);}Dictionary<string, string> map=new Dictionary<string, string>();//保存返回的JSON串中的车站代码与车站名之间的映ArrayList AllTicket = new ArrayList();//所有的车票信息JObject jo = (JObject)JsonConvert.DeserializeObject(str);JToken record = jo["data"]["map"];foreach (JProperty item in record){map.Add(item.Name.ToString(), item.Value.ToString());}record = jo["data"]["result"];foreach (string s in record){string[] result = s.Split('|');Ticket t = new Ticket();t.TrainId = result[3];t.FromStation = map[result[6]];t.ToStation = map[result[7]];t.StartTime = result[8];t.ArriveTime = result[9];t.LastedTime = result[10];t.TicketDate = result[13];t.DwNum = result[33] != "" ? result[33] : t.DwNum;t.TzNum = result[32] != "" ? result[32] : t.TzNum;t.YdNum = result[31] != "" ? result[31] : t.YdNum;t.EdNum = result[30] != "" ? result[30] : t.EdNum;t.YzNum = result[29] != "" ? result[29] : t.YzNum;t.YwNum = result[28] != "" ? result[28] : t.YwNum;t.WzNum = result[26] != "" ? result[26] : t.WzNum;t.RzNum = result[24] != "" ? result[24] : t.RzNum;t.RwNum = result[23] != "" ? result[23] : t.RwNum;t.GrwNum = result[21] != "" ? result[21] : t.GrwNum;AllTicket.Add(t);}Ticket[] All = new Ticket[AllTicket.Count];int flag = 0;foreach (object t in AllTicket){All[flag++] = t as Ticket;}return All;}

效果图



项目地址:EasyLife

参考博客

C#调用12306API做余票查询(该参考博客中的方法已不可用)

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;namespace Ticket
{class Program{public static void StationNameXml(){string nowpath = Environment.CurrentDirectory;if (Directory.Exists(nowpath + "/Data") == false)Directory.CreateDirectory(nowpath + "/Data");//创建新路径if (File.Exists(nowpath + "/Data/StationName.xml") == true)return;HttpWebRequest request;HttpWebResponse response;string url = @"https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9055";request = (HttpWebRequest)WebRequest.Create(url);request.Method = "GET";request.Credentials = CredentialCache.DefaultCredentials;request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36";string str = string.Empty;try{response = (HttpWebResponse)request.GetResponse();if (response.StatusCode == HttpStatusCode.OK){StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("UTF-8"));str = sr.ReadToEnd();sr.Close();response.Close();//关闭response响应流}elsethrow new Exception();}catch (Exception e){Console.WriteLine(e.Message);}string pattern = @"@\S{2,5}?[|](?<Name>\S{1,8}?)[|](?<Code>\S{3,5}?)[|]";Regex r = new Regex(pattern, RegexOptions.ExplicitCapture);XmlDocument xmldoc = new XmlDocument();XmlDeclaration xmldecl;xmldecl = xmldoc.CreateXmlDeclaration("1.0", "UTF-8", null);//加入XML的声明段落xmldoc.AppendChild(xmldecl);XmlElement root = xmldoc.CreateElement("", "Dict", "");//加入一个根元素xmldoc.AppendChild(root);MatchCollection mc = r.Matches(str);foreach (Match m in mc){GroupCollection group = m.Groups;XmlNode node = xmldoc.CreateNode(XmlNodeType.Element, "Pair", null);XmlNode temp = xmldoc.CreateNode(XmlNodeType.Element, "Station", null);temp.InnerText = group["Name"].Value;node.AppendChild(temp);XmlNode temp2 = xmldoc.CreateNode(XmlNodeType.Element, "Code", null);temp2.InnerText = group["Code"].Value;node.AppendChild(temp2);root.AppendChild(node);}xmldoc.Save(nowpath + "/Data/" + "StationName.xml");}private static string StationCode(string StationName){XmlDocument doc = new XmlDocument();doc.Load(Environment.CurrentDirectory + "/Data/StationName.xml");XmlNodeList data = doc.DocumentElement.ChildNodes;foreach (XmlNode node in data){XmlElement xe = (XmlElement)node;XmlNodeList xnl0 = xe.ChildNodes;string s = xnl0.Item(0).InnerText;if (s == StationName){return xnl0.Item(1).InnerText;}}return string.Empty;}private static Ticket[] SpiderTicket(string date, string from, string to, string id){HttpWebRequest request;HttpWebResponse response;string url = "https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={0}&leftTicketDTO.from_station={1}&leftTicketDTO.to_station={2}&purpose_codes={3}";url = string.Format(url, date, from, to, id);request = (HttpWebRequest)WebRequest.Create(url);request.Method = "GET";request.Credentials = CredentialCache.DefaultCredentials;request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36";string str = string.Empty;try{response = (HttpWebResponse)request.GetResponse();if (response.StatusCode == HttpStatusCode.OK){StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("UTF-8"));str = sr.ReadToEnd();sr.Close();response.Close();//关闭response响应流}elsethrow new Exception();}catch (Exception e){Console.WriteLine(e.Message);}Dictionary<string, string> map=new Dictionary<string, string>();//保存返回的JSON串中的车站代码与车站名之间的映ArrayList AllTicket = new ArrayList();//所有的车票信息JObject jo = (JObject)JsonConvert.DeserializeObject(str);JToken record = jo["data"]["map"];foreach (JProperty item in record){map.Add(item.Name.ToString(), item.Value.ToString());}record = jo["data"]["result"];foreach (string s in record){string[] result = s.Split('|');Ticket t = new Ticket();t.TrainId = result[3];t.FromStation = map[result[6]];t.ToStation = map[result[7]];t.StartTime = result[8];t.ArriveTime = result[9];t.LastedTime = result[10];t.TicketDate = result[13];t.DwNum = result[33] != "" ? result[33] : t.DwNum;t.TzNum = result[32] != "" ? result[32] : t.TzNum;t.YdNum = result[31] != "" ? result[31] : t.YdNum;t.EdNum = result[30] != "" ? result[30] : t.EdNum;t.YzNum = result[29] != "" ? result[29] : t.YzNum;t.YwNum = result[28] != "" ? result[28] : t.YwNum;t.WzNum = result[26] != "" ? result[26] : t.WzNum;t.RzNum = result[24] != "" ? result[24] : t.RzNum;t.RwNum = result[23] != "" ? result[23] : t.RwNum;t.GrwNum = result[21] != "" ? result[21] : t.GrwNum;AllTicket.Add(t);}Ticket[] All = new Ticket[AllTicket.Count];int flag = 0;foreach (object t in AllTicket){All[flag++] = t as Ticket;}return All;}static void Main(string[] args){StationNameXml();string from = "上海";string to = "北京";Ticket[] All=new Ticket[50];All=SpiderTicket(DateTime.Now.ToString("yyyy-MM-dd"), StationCode(from), StationCode(to), "ADULT");for(int i=0;i<All.Length;i++){Dictionary<string, string> map = new Dictionary<string, string>();map = All[i].getMap();foreach (object j in map.Keys)Console.Write(j.ToString()+":"+map[j.ToString()].ToString()+" ");Console.WriteLine();}Console.Read();}}
}

使用c#中的HttpWebRequest实现12306余票查询相关推荐

  1. 返乡之路不容易之12306余票查询并给出备选方案v2

    在第一版的返乡之路不容易之12306余票查询并给出备选方案中,给出了余票查询和备选方案推荐,但当时有两个问题: 没有备选排名:虽然给出了备选,但哪个备选好没有给出排序 没有座位信息(商务/一等/二等/ ...

  2. 最新Python实现12306余票查询系统

    首先感谢这篇博客的引导,但是这篇文章已经比较旧了,12306进行了改版,源码编排方式有了变化.12306源码获取的方法可以从这篇博客中学习,我在这里主要贴出对于目前最新的12306的Python余票查 ...

  3. Python之12306余票查询

    简单的Python学习,用Python完成一个12306余票查询- Python之12306余票查询 参考资料来自Python 实现火车票查询工具 需要用到的第三方库 requests,使用 Pyth ...

  4. 火车票查询系统(一)——利用python实现监控12306余票查询

    最近春运高峰,本人从12月开始买过年回家的火车票,然鹅~现实总是那么残酷,放票时间记不住,要么就是不能实时盯着刷票,外面的软件也不知道靠不靠谱,反正是没买到票,所以一怒之下,自己做一个12306余票查 ...

  5. 12306 余票查询API浅探索

    12306 余票查询API浅探索 余票查询请求分析 响应数据解密代码查找调试 数据说明 余票查询请求分析 点击查询,F12进入控制台,进入NetWork面板,查看发送的请求: 点入请求查看详情,很明显 ...

  6. 铁路局12306余票查询的实现

    离上次写的类似于铁道部12306城市选择框的实现过了都快一个月了,最近一直在忙,在学习sharepoint2013,虽然早就实现了12306的余票查询,但是一直没抽出时间来写,刚好这几天也快放假了,抽 ...

  7. 12306余票查询(爬虫)

    经常因为忙而忘记给妹子订票. 每次结果不言而喻. 登个12306查下余票其实很简单,但不知道为什么就是不想登. 所以弄了个爬虫脚本直接查询余票.希望下个女票不会这样尴尬,哈哈哈哈哈哈 12306首页为 ...

  8. 12306余票查询(一)——分析页面

    这段时间Python写的很少,用这个小项目复习一下以前的知识 首先打开12306看一下页面结构吧 因为我们没有点击查询,所以现在页面中是没有余票信息的, 随便选个出发站目的地,日期,点击查询, 发现相 ...

  9. 12306余票查询(三)——获取余票票价

    上一节只能看到相关车次,车次的时间,但是没有很重要的一个信息,票价,这显然是很不合理的.通过分析页面请求情况,可知,车次的票价,都是分开请求的,但是一条请求里,包括了了一个车次的所有票价信息 每个车次 ...

最新文章

  1. maven多模块eclipse jetty热部署
  2. JavaScript判断变量数据类型
  3. AUC 评价指标详解,准确率(ACC),敏感性(sensitivity),特异性(specificity)计算 Python3【TensorFlow2入门手册】
  4. 音视频技术开发周刊(第123期)
  5. 使用手机游戏的新闻推送
  6. 前端学习(3147):react-hello-react之getBeforeUpdate
  7. python logging日志分割_python logging日志模块以及多进程日志
  8. Python中被双下划线包围的魔法方法
  9. Security+ 学习笔记37 脚本和命令行
  10. 分布式日志收集系统: Facebook Scribe之配置文件
  11. AtCoder Regular Contest 102 (ARC102) E - Stop. Otherwise... 排列组合
  12. 蓝桥杯web:2.【Bug 调试】修复注册验证问题
  13. 5GC 会话建立流程
  14. M1非接触式IC卡性能简介
  15. 一文讲清楚什么是类型化数组、ArrayBuffer、TypedArray、DataView等概念
  16. 光耦驱动单向可控硅_隔离开关电源继电器ssr输出限流电阻,逻辑性光耦驱动单向可控硅电路...
  17. 高通发布骁龙 695、778G+、480+、680 4G 四款芯片
  18. 视觉特效,图片转成漫画功能
  19. ABAP 供应商主数据批量导入
  20. Nico的刷题日记(一)

热门文章

  1. android 微信评论功能,Android仿微信朋友圈点赞和评论功能
  2. 玩转Blueteeth Chat蓝牙交友
  3. Map集合的遍历方式
  4. 谷歌android!Android基础面试常常死在这几个问题上,全套教学资料
  5. MySQL 创建数据库
  6. FlexCell demo
  7. 按键精灵,批量传包工具源码备份
  8. 5s获取的计算机找不到,苹果iPhone5s手机有计算器吗 iOS7计算器在哪
  9. linux网卡e1000下载,Linux E1000网卡驱动分析
  10. 互联网很快就将达到承载极限?