C#实现查询12306票价信息

注意:

文章后的下载代码,还有小bug,遇到1-9号的时间可能会查不到数据。因为日期的天必须为 “00” 格式,请大家自行下载代码后修改。

2013.12.03修改:

发送正文已变为:

 string parameter = @"condition=&queryMode=1&nyear2={0}&nyear2_new_value=true&nmonth2={1}&nmonth2_new_value=true&nday2={2}&nday2_new_value=false&startStation_ticketPrice1=
{3}&startStation_ticketPrice1_new_value=false&arriveStation_ticketPrice1={4}&arriveStation_ticketPrice1_new_value=false&trainCode=&trainCode_new_value=true&
rFlag=1&name_ckball=value_ckball&tFlagDC=DC&tFlagZ=Z&tFlagT=T&tFlagK=K&tFlagPK=PK&tFlagPKE=PKE&tFlagLK=LK&randCode={5}";

 parameter = string.Format(parameter, fromDate.Year, fromDate.Month, fromDate.Day.ToString("00"), startStation, arriveStation, randCode);

最近打算做一个抓取12306车票信息的功能,自动登录实现起来有些问题,老是登录不成功。于是就做了不需要登录的一些查询的功能。先看看票价查询的读取。

也就是这个页面的查询

这个页面是POST方式提交数据,有一个验证码图片的读取。
一般来说IE浏览器打开后(IE9),F12(开发人员工具)-网络,可以进行捕获提交的url,数据等。

可以查看每一步请求了哪些页面和传输了哪些数据,相当于火狐的firebug。这个可以用IE9研究一下,我试了下IE7、IE8都没有这个功能,IE9可以的。

如图,


具体过程就是点击开始捕获,然后完成当前页面的查询操作,再点击结束捕获,就会抓取所有访问的url和传输的参数。

大家可以自己尝试,直接上代码。

因此获取验证码图片方法可以这样写:
[csharp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. private static string setCookie = string.Empty;  
  2. /// <summary>  
  3. /// 票价查询验证码  
  4. /// </summary>  
  5. /// <returns></returns>  
  6. public static string DoGetTrainTicketPriceRandCodeImage()  
  7. {  
  8.     string resultImage = string.Empty;  
  9.   
  10.   
  11.     try  
  12.     {  
  13.         HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"http://dynamic.12306.cn/TrainQuery/passCodeActi0n.do?rand=rrand");  
  14.   
  15.   
  16.         request.Accept = @"image/png, image/svg+xml, image/*;q=0.8, */*;q=0.5";  
  17.   
  18.   
  19.         request.UserAgent = @"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)";  
  20.   
  21.   
  22.         request.Referer = @"http://dynamic.12306.cn/TrainQuery/ticketPriceByStation.jsp";  
  23.   
  24.   
  25.         request.Method = "GET";  
  26.   
  27.   
  28.         using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())  
  29.         {  
  30.             setCookie = response.GetResponseHeader("Set-cookie");  
  31.   
  32.   
  33.             setCookie = Regex.Replace(setCookie, "path(?:[^,]+),?""", RegexOptions.IgnoreCase);  
  34.   
  35.   
  36.             using (Stream reader = response.GetResponseStream())  
  37.             {  
  38.                 string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, new Random().Next(10000, 99999) + @"queryTicketPrice.JPEG");  
  39.   
  40.   
  41.                   
  42.                 using (FileStream file = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))  
  43.                 {  
  44.                     reader.CopyTo(file);  
  45.                 }  
  46.   
  47.   
  48.                 resultImage = path;  
  49.   
  50.   
  51.             }  
  52.         }  
  53.     }  
  54.     catch { }  
  55.   
  56.   
  57.     return resultImage;  
  58. }  
之所以写的这么麻烦,我原本打算用WebClient直接将http://dynamic.12306.cn/TrainQuery/passCodeActi0n.do?rand=rrand这个路径的文件下载下来,但是,测试时发现直接下载下来的输入验证码后返回验证码过期。于是用这种方式下载文件然后保存。并将图片文件显示在界面上。


setCookie字段存放的是当前页面的请求时的cookie。这个也是为了后面调用查询票价url时用的,用同一个cookie才能保证查询结果正确。


那么下面看传参数读取票价信息的方法:
[csharp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1.        /// <summary>  
  2.         /// 火车票票价信息字符串  
  3.         /// </summary>  
  4.         public static string DoQueryTrainTicketPriceInfo(DateTime fromDate, string startStation,string arriveStation,string randCode)  
  5.         {  
  6.             string result = string.Empty;  
  7.   
  8.   
  9.             try  
  10.             {  
  11.                 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"http://dynamic.12306.cn/TrainQuery/iframeTicketPriceByStation.jsp");  
  12.   
  13.   
  14.                 request.Accept = @"text/html, application/xhtml+xml, */*";  
  15.   
  16.   
  17.                 request.UserAgent = @"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)";  
  18.   
  19.   
  20.                 request.Referer = @"http://dynamic.12306.cn/TrainQuery/ticketPriceByStation.jsp";  
  21.   
  22.   
  23.                 request.ContentType = @"application/x-www-form-urlencoded";  
  24.   
  25.   
  26.                 request.Headers[HttpRequestHeader.Cookie] = setCookie;  
  27.   
  28.   
  29.                 request.Method = "POST";  
  30.   
  31.   
  32.                 byte[] buffer = new byte[0];  
  33.   
  34.   
  35.                 DoGetTrainTicketPriceRandCodeImage();  
  36.   
  37.   
  38.                 string parameter = @"condition=&queryMode=1&nmonth2={0}&nmonth2_new_value=true&nday2={1}&nday2_new_value=false&startStation_ticketPrice1={2}  
  39. &startStation_ticketPrice1_new_value=false&arriveStation_ticketPrice1={3}&arriveStation_ticketPrice1_new_value=false&trainCode=&trainCode_new_value=true&  
  40. rFlag=1&name_ckball=value_ckball&tFlagDC=DC&tFlagZ=Z&tFlagT=T&tFlagK=K&tFlagPK=PK&tFlagPKE=PKE&tFlagLK=LK&randCode={4}";  
  41.   
  42.   
  43.                 parameter = string.Format(parameter, fromDate.Month, fromDate.Day.ToString("00"), startStation, arriveStation, randCode);  
  44.   
  45.   
  46.                 buffer = Encoding.UTF8.GetBytes(parameter);  
  47.   
  48.   
  49.                 request.ContentLength = buffer.Length;  
  50.   
  51.   
  52.                 using (Stream writer = request.GetRequestStream())  
  53.                 {  
  54.                     writer.Write(buffer, 0, buffer.Length);  
  55.                     writer.Flush();  
  56.                 }  
  57.   
  58.   
  59.                 using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())  
  60.                 {  
  61.                     using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))  
  62.                     {  
  63.                         result = reader.ReadToEnd();  
  64.                     }  
  65.                 }  
  66.             }  
  67.             catch { }  
  68.   
  69.   
  70.             return result;  
  71.         }  
这里比较关键的就是传输正文,也就是parameter字符串的内容,关键参数也就是代码中的那几个,时间,出发站,到达站和验证码。这里的request.Headers[HttpRequestHeader.Cookie]就要用到读取验证码文件那里的同一个cookie。
至于其他参数也是通过捕获查询过程获取的。
所有代码如下,这里是用Visual studio 2010,wpf实现的界面显示。

主要代码:

[csharp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1.  public  class TrainTicketPriceQuery  
  2.     {  
  3.         private static string setCookie = string.Empty;  
  4.   
  5.         /// <summary>  
  6.         /// 火车票票价信息字符串  
  7.         /// </summary>  
  8.         public static string DoQueryTrainTicketPriceInfo(DateTime fromDate, string startStation, string arriveStation, string randCode)  
  9.         {  
  10.             string result = string.Empty;  
  11.   
  12.             try  
  13.             {  
  14.                 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"http://dynamic.12306.cn/TrainQuery/iframeTicketPriceByStation.jsp");  
  15.   
  16.                 request.Accept = @"text/html, application/xhtml+xml, */*";  
  17.   
  18.                 request.UserAgent = @"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)";  
  19.   
  20.                 request.Referer = @"http://dynamic.12306.cn/TrainQuery/ticketPriceByStation.jsp";  
  21.   
  22.                 request.ContentType = @"application/x-www-form-urlencoded";  
  23.   
  24.                 request.Headers[HttpRequestHeader.Cookie] = setCookie;  
  25.   
  26.                 request.Method = "POST";  
  27.   
  28.                 byte[] buffer = new byte[0];  
  29.   
  30.                 DoGetTrainTicketPriceRandCodeImage();  
  31.   
  32.                 string parameter = @"condition=&queryMode=1&nmonth2={0}&nmonth2_new_value=true&nday2={1}&nday2_new_value=false&startStation_ticketPrice1={2}  
  33. &startStation_ticketPrice1_new_value=false&arriveStation_ticketPrice1={3}&arriveStation_ticketPrice1_new_value=false&trainCode=&trainCode_new_value=true&  
  34. rFlag=1&name_ckball=value_ckball&tFlagDC=DC&tFlagZ=Z&tFlagT=T&tFlagK=K&tFlagPK=PK&tFlagPKE=PKE&tFlagLK=LK&randCode={4}";  
  35.   
  36.                 parameter = string.Format(parameter, fromDate.Month, fromDate.Day.ToString("00"), startStation, arriveStation, randCode);  
  37.   
  38.                 buffer = Encoding.UTF8.GetBytes(parameter);  
  39.   
  40.                 request.ContentLength = buffer.Length;  
  41.   
  42.                 using (Stream writer = request.GetRequestStream())  
  43.                 {  
  44.                     writer.Write(buffer, 0, buffer.Length);  
  45.                     writer.Flush();  
  46.                 }  
  47.   
  48.                 using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())  
  49.                 {  
  50.                     using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))  
  51.                     {  
  52.                         result = reader.ReadToEnd();  
  53.                     }  
  54.                 }  
  55.             }  
  56.             catch { }  
  57.   
  58.             return result;  
  59.         }  
  60.         /// <summary>  
  61.         /// 票价查询验证码  
  62.         /// </summary>  
  63.         /// <returns></returns>  
  64.         public static string DoGetTrainTicketPriceRandCodeImage()  
  65.         {  
  66.             string resultImage = string.Empty;  
  67.   
  68.             try  
  69.             {  
  70.                 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"http://dynamic.12306.cn/TrainQuery/passCodeActi0n.do?rand=rrand");  
  71.   
  72.                 request.Accept = @"image/png, image/svg+xml, image/*;q=0.8, */*;q=0.5";  
  73.   
  74.                 request.UserAgent = @"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)";  
  75.   
  76.                 request.Referer = @"http://dynamic.12306.cn/TrainQuery/ticketPriceByStation.jsp";  
  77.   
  78.                 request.Method = "GET";  
  79.   
  80.                 using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())  
  81.                 {  
  82.                     setCookie = response.GetResponseHeader("Set-cookie");  
  83.   
  84.                     setCookie = Regex.Replace(setCookie, "path(?:[^,]+),?""", RegexOptions.IgnoreCase);  
  85.   
  86.                     using (Stream reader = response.GetResponseStream())  
  87.                     {  
  88.                         string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, new Random().Next(10000, 99999) + @"queryTicketPrice.JPEG");  
  89.   
  90.   
  91.                         using (FileStream file = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))  
  92.                         {  
  93.                             reader.CopyTo(file);  
  94.                         }  
  95.   
  96.                         resultImage = path;  
  97.   
  98.                     }  
  99.                 }  
  100.             }  
  101.             catch { }  
  102.   
  103.             return resultImage;  
  104.         }  
  105.     }  
界面代码:

[csharp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <Window x:Class="TestQueryTrainTicketPrice.MainWindow"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="MainWindow" Width="900" MinHeight="600">  
  5.     <StackPanel>  
  6.         <Grid>  
  7.             <Grid.Resources>  
  8.                 <Style TargetType="TextBlock">  
  9.                     <Setter Property="FontFamily" Value="Microsoft YaHei"/>  
  10.                     <Setter Property="FontSize" Value="20"/>  
  11.                     <Setter Property="VerticalAlignment" Value="Center"/>  
  12.                 </Style>  
  13.   
  14.                 <Style TargetType="TextBox">  
  15.                     <Setter Property="FontSize" Value="20"/>  
  16.                     <Setter Property="MinWidth" Value="300"/>  
  17.                     <Setter Property="Height" Value="50"/>  
  18.                     <Setter Property="VerticalAlignment" Value="Center"/>  
  19.                 </Style>  
  20.             </Grid.Resources>  
  21.             <Grid.RowDefinitions>  
  22.                 <RowDefinition Height="auto"/>  
  23.                 <RowDefinition Height="auto"/>  
  24.                 <RowDefinition Height="auto"/>  
  25.                 <RowDefinition Height="auto"/>  
  26.             </Grid.RowDefinitions>  
  27.   
  28.             <Grid.ColumnDefinitions>  
  29.                 <ColumnDefinition Width="auto"/>  
  30.                 <ColumnDefinition Width="auto"/>  
  31.             </Grid.ColumnDefinitions>  
  32.   
  33.             <TextBlock Grid.Row="0" Grid.Column="0" Text="日   期"/>  
  34.             <TextBox Grid.Row="0" Grid.Column="1" x:Name="txtDate" />  
  35.   
  36.             <TextBlock Grid.Row="1" Grid.Column="0" Text="出发站"/>  
  37.             <TextBox Grid.Row="1" Grid.Column="1" x:Name="txtStartStation" />  
  38.   
  39.             <TextBlock Grid.Row="2" Grid.Column="0" Text="到达站"/>  
  40.             <TextBox Grid.Row="2" Grid.Column="1" x:Name="txtArriveStation" />  
  41.   
  42.             <TextBlock Grid.Row="3" Grid.Column="0" Text="验证码"/>  
  43.   
  44.             <StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal"  
  45.                         VerticalAlignment="Center">  
  46.                 <TextBox  x:Name="txtRandCode" />  
  47.                 <Image x:Name="imageRandCode" Width="auto"/>  
  48.                 <Button Content="刷新验证码" Height="30" Width="100" Click="Button_Click_1" />  
  49.             </StackPanel>  
  50.   
  51.         </Grid>  
  52.   
  53.         <Button Content="开始查询" Width="150" Height="50" Click="Button_Click" />  
  54.   
  55.         <RichTextBox x:Name="rtxResultInfo" VerticalScrollBarVisibility="Visible" Height="315"/>  
  56.   
  57.     </StackPanel>  
  58. </Window>  
控制显示代码:

[csharp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public partial class MainWindow : Window  
  2.    {  
  3.        public MainWindow()  
  4.        {  
  5.            InitializeComponent();  
  6.   
  7.            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);  
  8.        }  
  9.   
  10.        void MainWindow_Loaded(object sender, RoutedEventArgs e)  
  11.        {  
  12.            string date = DateTime.Now.AddDays(3).ToString(@"yyyy-MM-dd");  
  13.   
  14.            this.txtDate.Text = date;  
  15.   
  16.            this.txtStartStation.Text = @"广州";  
  17.   
  18.            this.txtArriveStation.Text = @"西安";  
  19.   
  20.            InitialRandCode();  
  21.        }  
  22.   
  23.        private void InitialRandCode()  
  24.        {  
  25.            //初始化验证码  
  26.            string randCodeImageUrl = TrainTicketPriceQuery.DoGetTrainTicketPriceRandCodeImage();  
  27.   
  28.            if (!string.IsNullOrEmpty(randCodeImageUrl))  
  29.            {  
  30.                ImageSource src = (ImageSource)(new ImageSourceConverter().ConvertFromString(randCodeImageUrl));  
  31.   
  32.                this.imageRandCode.Source = src;  
  33.            }  
  34.        }  
  35.   
  36.        /// <summary>  
  37.        /// 开始查询  
  38.        /// </summary>  
  39.        /// <param name="sender"></param>  
  40.        /// <param name="e"></param>  
  41.        private void Button_Click(object sender, RoutedEventArgs e)  
  42.        {  
  43.            string startStation = this.txtStartStation.Text.Trim();  
  44.   
  45.            string arriveStation = this.txtArriveStation.Text.Trim();  
  46.   
  47.            string randCode = this.txtRandCode.Text.Trim();  
  48.   
  49.            string startDate = this.txtDate.Text;  
  50.   
  51.            if (string.IsNullOrEmpty(startStation) || string.IsNullOrEmpty(arriveStation) || string.IsNullOrEmpty(randCode) || string.IsNullOrEmpty(startDate))  
  52.            {  
  53.                MessageBox.Show("参数不完整,请重新填写!");  
  54.   
  55.                return;  
  56.            }  
  57.   
  58.            DateTime fromDate = DateTime.Parse(startDate);  
  59.   
  60.            string queryResult = TrainTicketPriceQuery.DoQueryTrainTicketPriceInfo(DateTime.Now.AddDays(4), startStation, arriveStation, randCode);  
  61.   
  62.            System.Windows.Documents.FlowDocument doc = rtxResultInfo.Document;  
  63.   
  64.            doc.Blocks.Clear();  
  65.   
  66.            rtxResultInfo.AppendText(queryResult);  
  67.   
  68.        }  
  69.   
  70.        private void Button_Click_1(object sender, RoutedEventArgs e)  
  71.        {  
  72.            InitialRandCode();  
  73.        }  
  74.    }  
效果,



其中比如:
parent.mygrid.addRow(0,"1,G832^skbcx.jsp?cxlx=cc&date=20131125&trainCode=G832_6c0000G83201,广州南^skbcx.jsp?cxlx=czjgcc&zm=&date=20131125&stationName_passTrain=%E5%B9%BF%E5%B7%9E%E5%8D%97^self,西安北^skbcx.jsp?cxlx=czjgcc&zm=&date=20131125&stationName_passTrain=%E8%A5%BF%E5%AE%89%E5%8C%97^self,--,--,1301.5,813.5,--,--,--,--,--,07:18,16:23,09:05,广州南^skbcx.jsp?cxlx=czjgcc&zm=&date=20131125&stationName_passTrain=%E5%B9%BF%E5%B7%9E%E5%8D%97^self,西安北^skbcx.jsp?cxlx=czjgcc&zm=&date=20131125&stationName_passTrain=%E8%A5%BF%E5%AE%89%E5%8C%97^self,高速,有",0);


这些才是票价信息,当然了还需要对字符串进行一些简单的处理。
用  
[csharp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. Regex regex = new Regex(@"parent.mygrid.addRow");  
  2.   
  3.   
  4. string[] arrows = regex.Split(priceContent);  

将整个字符串中的每一个车次的信息分离出来,然后再对每一条数据截取、替换等操作最终就能得到你想要的列车票价信息了。

代码下载

代码下载(修改bug后)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值