[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代码,如图:
图中选中部分则是单个车票信息,
<DD class=list_piao_time>11月18日 </DD>
<DD class=list_piao_mj><A href="http://www.ganji.com/lieche/cc_T110/" target=_blank>T110</A> </DD>
<DD class=list_piao_time>4小时前 </DD></DL>
下面祭出我们的正则神器:RegexTester,将HTML填入Source中,然后再一点一点的拼出正则,如图:
正则表达式即是:
接下来便是设计界面,我就不在详述了,见图吧:
抓取逻辑:
1.抓取http://sh.ganji.com/piao/cc_T110/页面的HTML信息;
{
string str = http.GetHTML(txtUrl.Text, "*/*", Encoding.UTF8, 20480);
this.Invoke(new invokeDelegate(Update), str);
}
2.用正则匹配HTML信息;
{
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.将新信息加入到网格中并声音提醒;
{
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.开启任务
{
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.执行抓取逻辑
{
while (IsRun)
{
GetHtml();
System.Threading.Thread.Sleep(5000);
}
}
最终执行效果:
附上源码:源码,不带DEV控件
由于此工具用了DEV10.1.4.0控件,因此没有此控件的童鞋需要下载DEV控件的DLL:本工具用到的DEV DLL