[C#]基于HttpHelper的赶集抓票工具

每到年关,火车票总是一票难求,去年差点没买到票回家,看到赶集、58等上有转让票,大喜,遂掏出手机呼叫之,可结果都是已转让。想想原因皆是看到的不及时,被别人捷足先登了。哥们是干什么的,居然从我嘴里抢食,越想越气,便做一工具,实时在赶集上监控着,有新出售票就提醒,几天下来,一张D字头的到手。。。

闲扯了几句,下面进入正题。

 

开发这一工具大概思路就是从网页上获取HTML,然后用正则匹配之,匹配出有新数据数据时加入到网格中显示,另外不可能实时盯着这个工具看,必须在有新信息时有提醒,于是我便加了一个声音提醒,一小时内的新信息都加声音提醒。这里遇到两个问题,一是如何从网页上抓取HTML,二是正则匹配问题。关于抓取HTML,本人前一篇文章中已有介绍,见http://www.cnblogs.com/vanjoge/archive/2011/11/18/2253937.html,剩下的问题就是正则了,我们以赶集的T110次车为例来找到对应正则,首先打开赶集的T110次车的连接http://sh.ganji.com/piao/cc_T110/,图中红框部分就是我们需要抓取的数据

 

 

 

 

 

查看源文件,找到对应的HTML代码,如图:

 

 

 

图中选中部分则是单个车票信息,

<DL class=list_piao><DT><href="/piao/11111809_2887746.htm" target=_blank>[转让] T110 上海-北京 硬卧 2张 发车日期:11-18</A> </DT>
<DD class=list_piao_time>11月18日 </DD>
<DD class=list_piao_mj><href="http://www.ganji.com/lieche/cc_T110/" target=_blank>T110</A> </DD>
<DD class=list_piao_time>4小时前 </DD></DL>

 

 

下面祭出我们的正则神器:RegexTester,将HTML填入Source中,然后再一点一点的拼出正则,如图:

 

 

正则表达式即是:

 

<dl class="list_piao">\s*<dt><a href="([^"]+)" target="_blank">([^<]+)</a></dt>\s*<dd class="list_piao_time">([^<]+)</dd>\s*<dd class="list_piao_mj"><a href="([^"]+)" target="_blank">([^<]+)</a></dd>\s*<dd class="list_piao_time">([^<]+)</dd>\s*</dl>

 

 

 

接下来便是设计界面,我就不在详述了,见图吧:

抓取逻辑:

1.抓取http://sh.ganji.com/piao/cc_T110/页面的HTML信息;

 

private void GetHtml()
{
            string str = http.GetHTML(txtUrl.Text, "*/*", Encoding.UTF8, 20480);
            this.Invoke(new invokeDelegate(Update), str);
}

 

2.用正则匹配HTML信息;

 

        void Update(string str)
        {
            lblUpdateTime.Text = "更新时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            string pn = "<dl class=\"list_piao\">\\s*<dt><a href=\"([^\"]+)\" target=\"_blank\">([^<]+)</a></dt>\\s*<dd class=\"list_piao_time\">([^<]+)</dd>\\s*<dd class=\"list_piao_mj\"><a href=\"([^\"]+)\" target=\"_blank\">([^<]+)</a></dd>\\s*<dd class=\"list_piao_time\">([^<]+)</dd>\\s*</dl>";
            Regex reg = new Regex(pn);
            MatchCollection mths = reg.Matches(str); 
            foreach (Match item in mths)
            { 
                Add(item.Groups[1].Value,
                    item.Groups[2].Value,
                    item.Groups[3].Value,
                    item.Groups[4].Value,
                    item.Groups[5].Value,
                    item.Groups[6].Value);
            }
            gridView1.BestFitColumns();
        }

 

3.将新信息加入到网格中并声音提醒;

 

        private void Add(string Url, string Text, string StartDate, string LUrl, string Vcc, string Time)
        {
            DataSet1.DTRow drow = GetDrow(dataSet11.DT, Url);//因为URL是唯一,因此根据URL获取是否已存在此火车票信息
            int itemp = 0;
            DateTime dt;
            if (Int32.TryParse(Time.Replace("分前"""), out itemp))
            {
            }
            else if (Int32.TryParse(Time.Replace("小时前"""), out itemp))
            {
                itemp = itemp * 60;
            }
            else if (DateTime.TryParse(strYear + Time, out dt))
            {
                itemp = Convert.ToInt32((DateTime.Now - dt).TotalMinutes);
            }
            if (drow == null)//不存在时追加
            {
                if (itemp < 60)
                {
                    if (chkAuto.Checked)
                    {
                        OpenNewUrl(Text, strHost + Url, true);
                    }
                    PlaySoundSync();
                }
                dataSet11.DT.AddDTRow(Url, Text, StartDate, LUrl, Vcc, Time, itemp);
            }
            else//存在时更新间隔时间
            {
                drow.Time = Time;
                drow.Ticks = itemp;
            }
        }

        DataSet1.DTRow GetDrow(DataSet1.DTDataTable dt,string Url)
        {
            foreach (DataSet1.DTRow drow in dt)
            {
                if (drow.Url==Url)
                {
                    return drow;
                }
            }

            return null;
        }

 

 

由于我们这个任务是需要定时的执行,因此要用到timer或Thread,这里我选择了用Thread,是为了防止在读取数据的过程中出现假死的情况,因此完整逻辑应该是:

 

1.开启任务

 

        private void btnStart_Click(object sender, EventArgs e)
        {
            strYear = DateTime.Now.Year.ToString() + "-";
            if (chkUseProxy.Checked)
            {
                this.http.Proxy = new WebProxy();
                try
                {
                    this.http.Proxy.Address = new Uri(string.Format("http://{0}:{1}"this.txtProxyServer.Text, this.spinProxyPort.Text));
                    if ((this.txtProxyUsername.Text.Length > 0) && (this.txtProxyPassword.Text.Length > 0))
                    {
                        this.http.Proxy.Credentials = new NetworkCredential(this.txtProxyUsername.Text, this.txtProxyPassword.Text);
                    }
                }
                catch
                {
                    MessageBox.Show("代理设置出错,请检查代理设置!""错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    this.http.Proxy = null;
                    return;
                }
            }
            else
            {
                this.http.Proxy = null;
            }



            IsRun = !IsRun;
            if (IsRun)
            {
                Uri uri = new Uri(txtUrl.Text);
                strHost = uri.Scheme + "://" + uri.Host;
                btnStart.Text = "停止监控";
                txtUrl.Enabled = false;
                System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(Run));
                th.IsBackground = true;
                th.Start();
            }
            else
            {
                btnStart.Text = "开始监控";
                txtUrl.Enabled = true;
            }
        }

 

2.执行抓取逻辑

 

  void Run()
        {
            while (IsRun)
            {
                GetHtml();
                System.Threading.Thread.Sleep(5000);
            }
        }

 

 

最终执行效果:

 

 

附上源码:源码,不带DEV控件

由于此工具用了DEV10.1.4.0控件,因此没有此控件的童鞋需要下载DEV控件的DLL:本工具用到的DEV DLL

posted @ 2011-11-18 15:45  二杠  阅读(5594)  评论(26编辑  收藏  举报