准备工作

  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. Jupyter Notebook 快捷键(基本)
  2. Unity3D 游戏引擎之脚本实现模型的平移与旋转(六)
  3. yum安装nginx的负载均衡详解
  4. Verilog inout 双向口使用和仿真
  5. leetcode 1239. 串联字符串的最大长度
  6. 正则表达式(思维导图速查版)
  7. SHELL脚本--多命令逻辑执行顺序
  8. [转载] IoT设备WiFi配网及现状
  9. 陕西2020行政区划调整_2020西安会成立直辖市
  10. 如何收割流量红利?UB Store的直播电商“三宝”
  11. Unity3d+C#斗地主学习探究1
  12. Tables[0].Rows.count是什么意思
  13. 如何用html写抖动效果,利用transition实现文字上下抖动的效果
  14. 路飞学城python全栈开发_[Python] 老男孩路飞学城Python全栈开发重点班 骑士计划最新100G...
  15. Https环境将key秘钥和crt格式证书转成cer证书
  16. 利用矩阵相乘公式Cij = ∑ a ik × b kj,编程计算m×n阶矩阵A和n×m阶矩阵B之积
  17. qrcode二维码生成工具
  18. passive-interface 总结整理
  19. 被谷歌出卖定位信息成“嫌疑犯”,花了大价钱才避免窦娥冤 | 一个自行车爱好者自述...
  20. lisp语言画地物符号_地图符号分为地物符号和特殊符号。

热门文章

  1. 电脑长按电源键强行关机,对SSD有伤害吗?SSD 掉盘之殇
  2. pr(premiere)导出h.264显示“导出错误,创建编码器失败” 解决方法
  3. 如何查看wi-fi的密码_如何在Windows 10上查看所有已保存的Wi-Fi密码
  4. html文本缩进自动换行,CSS 段落/换行/缩进
  5. HEU Tunnels(常规最小生成树)
  6. 这一次我不再低调,老板法拉利的车牌有我的汗水
  7. 智能mp5 android系统,新Android智能MP5 蓝晨BM790改版上市
  8. 解决ireport开发报表时detail和last page footer之间存在的空白
  9. 阿尔巴尼的计算机科学,纽约州立大学阿尔巴尼分校计算机科学研究生语言及申请要求-费用-课程设置...
  10. QQ好友上限提升至5000人