使用JS和HttpWatch监控携程网低价机票信息(非广告纯代码)
2011-01-27 18:30
357 查看
1、 春节回家,为了订到便宜机票,需要不停的刷新携程网来查看。时间一长,感觉又费时又费力,要不写个自动程序来定时检查提示该多好啊。为达到此目的,决定使用JS和HttpWatch实现自动监控低价机票的功能。以下是实现流程。帖不了附件很麻烦啊...
2、 总体流程
------------------------------------------------------------------------------------------
开始->分析页面->模拟访问URL->抓取请求返回消息->定位结果表->转化DOM对象验证
------------------------------------------------------------------------------------------
3、 分析请求页面,查看页面页文件(完整文件请看附件),有如下信息比较关键。可以看到在Form中有flightway(选择方式,单程,双程),homecity(出发点),destcity1(到达点)和DDatePeriod1(出发时间)几个对象。
------------------------------------------------------------------------------------------
<FORM id=flightForm onsubmit="return false;"
action=http://flights.ctrip.com/Domestic/ShowFareFirst.aspx method=post>
<DIV class=searchbox_fah> <DFN>自由·机+酒特惠</DFN><A id=airHotelBtn
href="javascript:void(0);">更实惠·更便捷</A></DIV>
<TABLE class=searchbox_content_fixed>
<TBODY>
<TR>
<TH>航程类型</TH>
<TD><LABEL class=index_label><INPUT type=radio CHECKED value=Single
------------------------------------------------------------------------------------------
4、对于flightway和DDatePeriod1比较容易确认内容,但对于homecity和destcity1内容是中文、汉语拼音,还是代码,只有测试后才知道。先后试了中文、汉语拼音后还是不行。再次查看源代码,发现了如下关键信息。这不就是航空城市代码吗,试了一下,可以了。然后筛出自己关心的两个代码:KMG(昆明)和NKG(南京)。附件是测试代码
-----------------------------------------------------------------
$.module.searchBox.airHotelList="BJS,SHA,CKG,DLC,TAO,NKG,HGH,XMN,CTU,SZX,CAN,KWL,KMG,LJG,CSX,SIA,WUH,TSN,HRB,KWE,URC,HAK,HET,TYN,FOC,HFE,NGB,KHN,NNG,SHE,WNZ,CGO";
$.module.pkgSearch.pkgStartCityHash={
-----------------------------------------------------------------
5、完成以上几步分析后,可以大致通过代码查询到机票信息。剩下的工作就需要使用到HttpWatch(版本6.0.14,低版本可能会导致抓取数据乱码无法解析)来完成请求返回数据的抓取。把查询请求信息先拼接为URL串(http://flights.ctrip.com/Domestic/ShowFareFirst.aspx?homecity=KMG&destcity1=NKG&DDatePeriod1=2011-02-13)。
6、通过HttpWatch查看返回的请求详细信息,分析后知道http://flights.ctrip.com/Domestic/ShowFareFirst.aspx这个请求里包含了所需的详细信息,主要的处理也针对这个URL项来处理。附件为该请求的返回消息内容。
7、开始编写测试驱动代码,使用了HttpWatch的IE控件(只需要安装HttpWatch即包含了IE控件)。编写RetryManage对象,主要包含了一些常量定义和一些用到的变量。CONST_URL是请求的URL,MATCH_URL是用于匹配HttpWatch返回请求结果列表中我们关心的URL(一个URL请求中,服务器会返回多个URL项,包括js、图片、页面信息等内容)。MATCH_BEG_MSG是在匹配到我们所关心的信息后,从信息中找到我们关注的HTML标记。MATCH_END_MSG是结束HTML标记(查找方式很多,本例仅使用indexOf方法来获取特定内容在整个字符串的开始、结束序号,用以确定子串内容,该方法并不好,随着网站的变化就会失效)。Control保存HttpWatch控制对象(只需获取一次即可),plugin保存HttpWatch的对应IE的ActiveXObject对象。isReady表示是否上下文环境是否已准备好
-----------------------------------------------------------------
function RetryManage()
{
// const url for air tickets query
this.CONST_URL = "http://flights.ctrip.com/Domestic/ShowFareFirst.aspx?homecity=KMG&destcity1=NKG&DDatePeriod1=2011-02-13";
// const url for match query
this.MATCH_URL = "http://flights.ctrip.com/Domestic/ShowFareFirst.aspx";
// const begin match tickets fee table
this.MATCH_BEG_MSG = "<table id=/"ctl00_MainContentPlaceHolder_LowestPriceWebControl_LowestPriceTable/"";
// const end match tickets fee table
this.MATCH_END_MSG = "</table>";
// inner object
this.control = null;
this.plugin = null;
this.isReady = false;
}
-----------------------------------------------------------------
8、 为RetryManage对象添加初始化方法,用于初始化上下文环境。初始化方法,获取IE控件等请参见HttpWatch接口说明(在安装HttpWatch时已自带,通过Help找到即可)。在初始化control和plugin对象成功后,就将isReady置为true,表示上下文环境可用。执行New方法后会打开一个IE窗口(测试时使用了360会不正常,请将默认程序设置为IE)。
-----------------------------------------------------------------
RetryManage.prototype.init = function()
{
try
{
// obtian httpwatch control
this.control = new ActiveXObject('HttpWatch.Controller');
// new IE window
this.plugin = this.control.IE.New();
// set ready status
this.isReady = true;
}
catch (e)
{
alert("init:" + e.message);
}
}
-----------------------------------------------------------------
9、 为RetryManage对象添加请求处理方法,除最后一步外,中间几步是HttpWatch请求的标准写法。Clear清除历史记录,Record开始记录请求历史记录,GotoURL访问指定URL(访问的请求、返回请求结果列表都会写会历史记录中),Wait等待请求完整返回后才处理(GotoURL是异步方法,如果不这样等待,则有可能会请求未完成程序已经开始处理了)。Stop是停止记录历史记录。然后就可以开始处理了,调用parserMessage方法解析请求(该方法是自定义的,下一步详述)
-----------------------------------------------------------------
RetryManage.prototype.procRequest = function()
{
// clear history log
this.plugin.clear();
// start record
this.plugin.Record();
// go to url
this.plugin.GotoURL(this.CONST_URL);
// wait server return
this.control.Wait(this.plugin, -1);
// stop record
this.plugin.Stop();
// parser message
this.parserMessage(this.plugin.Log.Entries);
}
-----------------------------------------------------------------
10、 为RetryManage对象添加请求记录解析方法。该方法处理比较简单,就是循环请求访问历史记录,找到我们关注的URL后就返回。注意不能使用==,而需要使用indexOf,应该entry.URL不仅仅是URL,还包含了URL中的查询参数等信息。用==是不能匹配上的。匹配完成后请调用parserContent方法处理返回消息内容(该方法是自定义的,下一步详述),完成后结束循环。
-----------------------------------------------------------------
RetryManage.prototype.parserMessage = function(entries)
{
// total entries(request message)
var enCount = entries.Count;
var entry = null;
for (var i = 0; i < enCount; i++)
{
entry = entries.Item(i);
if (entry.URL.indexOf(this.MATCH_URL) != -1)
{
this.parserContent(entry);
break;
}
}
}
-----------------------------------------------------------------
11、 为RetryManage对象添加请求内容解析方法。该方法处理也比较简单,就是使用indexOf方法从字符串中找到匹配,然后获取序号。var begIndex = feeMsg.indexOf(this.MATCH_BEG_MSG);获取到开始串的开始序号,然后使用var endIndex = feeMsg.indexOf(this.MATCH_END_MSG, begIndex);获取结束串的开始序号,注意多了一个begIndex参数是为了保证结束串是开始串之后第一个,否则会导致查询内容不正确。然后使用feeMsg = feeMsg.substring(begIndex, endIndex + this.MATCH_END_MSG.length);取出字串,注意endIndex只是结束串的起始位置,要获得完整子串,还需要在endIndex加上this.MATCH_END_MSG.length。完成后,就可以通过 var tab = document.getElementsByTagName("TABLE")(0); tab.outerHTML = feeMsg;把子串赋值给页面TABLE对象(从这里能看到获取到的子串是否正确)。最后调用foundLower(该方法是自定义的,下一步详述)查找低价信息,第一个参数是指定的日期,第二个参数是指定的最大金额。
-----------------------------------------------------------------
RetryManage.prototype.parserContent = function(entry)
{
var feeMsg = entry.content.data;
var begIndex = feeMsg.indexOf(this.MATCH_BEG_MSG);
if (begIndex == -1)
{
alert("Can not found fee info");
return;
}
var endIndex = feeMsg.indexOf(this.MATCH_END_MSG, begIndex);
if (endIndex == -1)
{
alert("Can not found fee info");
return;
}
feeMsg = feeMsg.substring(begIndex, endIndex + this.MATCH_END_MSG.length);
var tab = document.getElementsByTagName("TABLE")(0);
tab.outerHTML = feeMsg;
this.foundLower("02-13", 1300);
}
-----------------------------------------------------------------
12、 为RetryManage对象添加查找低价信息方法。主要处理就是将表格中数据解析到js对象列表中,然后循环和查询条件匹配。对于找到的信息(包括找到和没有找到)都写入页面对象textarea中,便于监控程序是否在正常运行。附件为解析后得到的表格HTML代码。
-----------------------------------------------------------------
RetryManage.prototype.foundLower = function(needDate, needMaxFee)
{
var tab = document.getElementsByTagName("TABLE")(0);
var fi = null;
var fd = null;
var ff = null;
var feeList = new Array();
for (var i = 1; i < tab.rows.length; i++)
{
for (var j = 0; j < tab.rows(i).cells.length; j++)
{
if (tab.rows(i).cells(j).hasChildNodes())
{
if (!tab.rows(i).cells(j).firstChild.hasChildNodes())
{
continue;
}
fd = trim(tab.rows(i).cells(j).firstChild.childNodes[0].toString());
ff = trim(tab.rows(i).cells(j).firstChild.childNodes[2].innerText);
ff = ff.substring(1);
fi = new FeeItem(fd, ff);
feeList.push(fi);
}
}
}
var hisMsg = document.getElementById("hisMsg");
var curMsg = null;
for (var i = 0; i < feeList.length; i++)
{
if (needDate == feeList[i].getDate())
{
if (new Number(needMaxFee) >= feeList[i].getFee())
{
hisMsg.innerText += "/n/rHas lower tickets fee at:" + feeList[i].getFee() + ". Date:" + new Date();
return;
}
else
{
curMsg = feeList[i].getFee();
}
}
}
hisMsg.innerText += "/n/rNo lower tickets fee below then " + needMaxFee + " Current is:" + curMsg + ". Date:" + new Date();
}
-----------------------------------------------------------------
13、 至此,所有处理方法都已编写完成,就可以编写doStart测试驱动方法。然后在处理完成后调用window.setTimeout(doStart, 30000);设置为每30秒处理一次(不包含处理时间)。将new RetryManage写在doStart方法,避免在每次执行时被重新创建,导致isReady状态丢失,造成每执行一次就打开一个IE窗口。
-----------------------------------------------------------------
RetryManage.prototype.isAllReady = function()
{
return this.isReady;
}
RetryManage.prototype.doTest = function()
{
try
{
if (!this.isAllReady())
{
this.init();
}
if (!this.isAllReady())
{
return;
}
this.procRequest();
window.setTimeout(doStart, 30000);
}
catch (e)
{
alert(e.message);
}
}
function doStart()
{
rm.doTest();
}
var rm = new RetryManage();
doStart();
-----------------------------------------------------------------
14、 到此就全部完成了,剩下的工作就是验证。以下就是源代码.
-----------------------------------------------------------------
<html>
<head>
<title>Retry</title>
<meta http-equiv="CONTENT-TYPE" content="TEXT/HTML; CHARSET=GBK"/>
<meta http-equiv="PRAGMA" content="no-cache">
<meta http-equiv="CACHE-CONTROL" content="no-cache">
<meta http-equiv="EXPIRES" content="0">
<meta NAME="AUTHOR" CONTENT="xuejun">
<meta NAME="KEYWORDS" CONTENT="JS HTTPWATCH">
<meta NAME="DESCRIPTION" CONTENT="Retry">
</head>
<body>
<form>
<table id="retMsg"></table>
<textArea id="hisMsg" rows="10" cols="100"></textArea>
</form>
</body>
</html>
<script>
<!--
function RetryManage()
{
// const url for air tickets query
this.CONST_URL = "http://flights.ctrip.com/Domestic/ShowFareFirst.aspx?homecity=KMG&destcity1=NKG&DDatePeriod1=2011-02-13";
// const url for match query
this.MATCH_URL = "http://flights.ctrip.com/Domestic/ShowFareFirst.aspx";
// const begin match tickets fee table
this.MATCH_BEG_MSG = "<table id=/"ctl00_MainContentPlaceHolder_LowestPriceWebControl_LowestPriceTable/"";
// const end match tickets fee table
this.MATCH_END_MSG = "</table>";
// inner object
this.control = null;
this.plugin = null;
this.isReady = false;
}
RetryManage.prototype.init = function()
{
try
{
// obtian httpwatch control
this.control = new ActiveXObject('HttpWatch.Controller');
// new IE window
this.plugin = this.control.IE.New();
// set ready status
this.isReady = true;
}
catch (e)
{
alert("init:" + e.message);
}
}
RetryManage.prototype.procRequest = function()
{
// clear history log
this.plugin.clear();
// start record
this.plugin.Record();
// go to url
this.plugin.GotoURL(this.CONST_URL);
// wait server return
this.control.Wait(this.plugin, -1);
// stop record
this.plugin.Stop();
// parser message
this.parserMessage(this.plugin.Log.Entries);
}
RetryManage.prototype.parserMessage = function(entries)
{
// total entries(request message)
var enCount = entries.Count;
var entry = null;
for (var i = 0; i < enCount; i++)
{
entry = entries.Item(i);
if (entry.URL.indexOf(this.MATCH_URL) != -1)
{
this.parserContent(entry);
break;
}
}
}
RetryManage.prototype.parserContent = function(entry)
{
var feeMsg = entry.content.data;
var begIndex = feeMsg.indexOf(this.MATCH_BEG_MSG);
if (begIndex == -1)
{
alert("Can not found fee info");
return;
}
var endIndex = feeMsg.indexOf(this.MATCH_END_MSG, begIndex);
if (endIndex == -1)
{
alert("Can not found fee info");
return;
}
feeMsg = feeMsg.substring(begIndex, endIndex + this.MATCH_END_MSG.length);
var tab = document.getElementsByTagName("TABLE")(0);
tab.outerHTML = feeMsg;
this.foundLower("02-13", 1300);
}
RetryManage.prototype.foundLower = function(needDate, needMaxFee)
{
var tab = document.getElementsByTagName("TABLE")(0);
var fi = null;
var fd = null;
var ff = null;
var feeList = new Array();
for (var i = 1; i < tab.rows.length; i++)
{
for (var j = 0; j < tab.rows(i).cells.length; j++)
{
if (tab.rows(i).cells(j).hasChildNodes())
{
if (!tab.rows(i).cells(j).firstChild.hasChildNodes())
{
continue;
}
fd = trim(tab.rows(i).cells(j).firstChild.childNodes[0].toString());
ff = trim(tab.rows(i).cells(j).firstChild.childNodes[2].innerText);
ff = ff.substring(1);
fi = new FeeItem(fd, ff);
feeList.push(fi);
}
}
}
var hisMsg = document.getElementById("hisMsg");
var curMsg = null;
for (var i = 0; i < feeList.length; i++)
{
if (needDate == feeList[i].getDate())
{
if (new Number(needMaxFee) >= feeList[i].getFee())
{
hisMsg.innerText += "/n/rHas lower tickets fee at:" + feeList[i].getFee() + ". Date:" + new Date();
return;
}
else
{
curMsg = feeList[i].getFee();
}
}
}
hisMsg.innerText += "/n/rNo lower tickets fee below then " + needMaxFee + " Current is:" + curMsg + ". Date:" + new Date();
}
RetryManage.prototype.isAllReady = function()
{
return this.isReady;
}
RetryManage.prototype.doTest = function()
{
try
{
if (!this.isAllReady())
{
this.init();
}
if (!this.isAllReady())
{
return;
}
this.procRequest();
window.setTimeout(doStart, 300000);
}
catch (e)
{
alert(e.message);
}
}
function FeeItem(fDate, fFee)
{
this.fDate = fDate;
this.fFee = fFee;
}
FeeItem.prototype.getDate = function()
{
return this.fDate;
}
FeeItem.prototype.getFee = function()
{
return new Number(this.fFee);
}
FeeItem.prototype.toString = function()
{
return this.fDate + ":" + this.fFee;
}
function trim(str)
{
if (str == null)
{
return "";
}
return str.replace(/(^/s*)|(/s*$)/g, "");
}
function doStart()
{
rm.doTest();
}
var rm = new RetryManage();
doStart();
//-->
</script>
-----------------------------------------------------------------
2、 总体流程
------------------------------------------------------------------------------------------
开始->分析页面->模拟访问URL->抓取请求返回消息->定位结果表->转化DOM对象验证
------------------------------------------------------------------------------------------
3、 分析请求页面,查看页面页文件(完整文件请看附件),有如下信息比较关键。可以看到在Form中有flightway(选择方式,单程,双程),homecity(出发点),destcity1(到达点)和DDatePeriod1(出发时间)几个对象。
------------------------------------------------------------------------------------------
<FORM id=flightForm onsubmit="return false;"
action=http://flights.ctrip.com/Domestic/ShowFareFirst.aspx method=post>
<DIV class=searchbox_fah> <DFN>自由·机+酒特惠</DFN><A id=airHotelBtn
href="javascript:void(0);">更实惠·更便捷</A></DIV>
<TABLE class=searchbox_content_fixed>
<TBODY>
<TR>
<TH>航程类型</TH>
<TD><LABEL class=index_label><INPUT type=radio CHECKED value=Single
------------------------------------------------------------------------------------------
4、对于flightway和DDatePeriod1比较容易确认内容,但对于homecity和destcity1内容是中文、汉语拼音,还是代码,只有测试后才知道。先后试了中文、汉语拼音后还是不行。再次查看源代码,发现了如下关键信息。这不就是航空城市代码吗,试了一下,可以了。然后筛出自己关心的两个代码:KMG(昆明)和NKG(南京)。附件是测试代码
-----------------------------------------------------------------
$.module.searchBox.airHotelList="BJS,SHA,CKG,DLC,TAO,NKG,HGH,XMN,CTU,SZX,CAN,KWL,KMG,LJG,CSX,SIA,WUH,TSN,HRB,KWE,URC,HAK,HET,TYN,FOC,HFE,NGB,KHN,NNG,SHE,WNZ,CGO";
$.module.pkgSearch.pkgStartCityHash={
-----------------------------------------------------------------
5、完成以上几步分析后,可以大致通过代码查询到机票信息。剩下的工作就需要使用到HttpWatch(版本6.0.14,低版本可能会导致抓取数据乱码无法解析)来完成请求返回数据的抓取。把查询请求信息先拼接为URL串(http://flights.ctrip.com/Domestic/ShowFareFirst.aspx?homecity=KMG&destcity1=NKG&DDatePeriod1=2011-02-13)。
6、通过HttpWatch查看返回的请求详细信息,分析后知道http://flights.ctrip.com/Domestic/ShowFareFirst.aspx这个请求里包含了所需的详细信息,主要的处理也针对这个URL项来处理。附件为该请求的返回消息内容。
7、开始编写测试驱动代码,使用了HttpWatch的IE控件(只需要安装HttpWatch即包含了IE控件)。编写RetryManage对象,主要包含了一些常量定义和一些用到的变量。CONST_URL是请求的URL,MATCH_URL是用于匹配HttpWatch返回请求结果列表中我们关心的URL(一个URL请求中,服务器会返回多个URL项,包括js、图片、页面信息等内容)。MATCH_BEG_MSG是在匹配到我们所关心的信息后,从信息中找到我们关注的HTML标记。MATCH_END_MSG是结束HTML标记(查找方式很多,本例仅使用indexOf方法来获取特定内容在整个字符串的开始、结束序号,用以确定子串内容,该方法并不好,随着网站的变化就会失效)。Control保存HttpWatch控制对象(只需获取一次即可),plugin保存HttpWatch的对应IE的ActiveXObject对象。isReady表示是否上下文环境是否已准备好
-----------------------------------------------------------------
function RetryManage()
{
// const url for air tickets query
this.CONST_URL = "http://flights.ctrip.com/Domestic/ShowFareFirst.aspx?homecity=KMG&destcity1=NKG&DDatePeriod1=2011-02-13";
// const url for match query
this.MATCH_URL = "http://flights.ctrip.com/Domestic/ShowFareFirst.aspx";
// const begin match tickets fee table
this.MATCH_BEG_MSG = "<table id=/"ctl00_MainContentPlaceHolder_LowestPriceWebControl_LowestPriceTable/"";
// const end match tickets fee table
this.MATCH_END_MSG = "</table>";
// inner object
this.control = null;
this.plugin = null;
this.isReady = false;
}
-----------------------------------------------------------------
8、 为RetryManage对象添加初始化方法,用于初始化上下文环境。初始化方法,获取IE控件等请参见HttpWatch接口说明(在安装HttpWatch时已自带,通过Help找到即可)。在初始化control和plugin对象成功后,就将isReady置为true,表示上下文环境可用。执行New方法后会打开一个IE窗口(测试时使用了360会不正常,请将默认程序设置为IE)。
-----------------------------------------------------------------
RetryManage.prototype.init = function()
{
try
{
// obtian httpwatch control
this.control = new ActiveXObject('HttpWatch.Controller');
// new IE window
this.plugin = this.control.IE.New();
// set ready status
this.isReady = true;
}
catch (e)
{
alert("init:" + e.message);
}
}
-----------------------------------------------------------------
9、 为RetryManage对象添加请求处理方法,除最后一步外,中间几步是HttpWatch请求的标准写法。Clear清除历史记录,Record开始记录请求历史记录,GotoURL访问指定URL(访问的请求、返回请求结果列表都会写会历史记录中),Wait等待请求完整返回后才处理(GotoURL是异步方法,如果不这样等待,则有可能会请求未完成程序已经开始处理了)。Stop是停止记录历史记录。然后就可以开始处理了,调用parserMessage方法解析请求(该方法是自定义的,下一步详述)
-----------------------------------------------------------------
RetryManage.prototype.procRequest = function()
{
// clear history log
this.plugin.clear();
// start record
this.plugin.Record();
// go to url
this.plugin.GotoURL(this.CONST_URL);
// wait server return
this.control.Wait(this.plugin, -1);
// stop record
this.plugin.Stop();
// parser message
this.parserMessage(this.plugin.Log.Entries);
}
-----------------------------------------------------------------
10、 为RetryManage对象添加请求记录解析方法。该方法处理比较简单,就是循环请求访问历史记录,找到我们关注的URL后就返回。注意不能使用==,而需要使用indexOf,应该entry.URL不仅仅是URL,还包含了URL中的查询参数等信息。用==是不能匹配上的。匹配完成后请调用parserContent方法处理返回消息内容(该方法是自定义的,下一步详述),完成后结束循环。
-----------------------------------------------------------------
RetryManage.prototype.parserMessage = function(entries)
{
// total entries(request message)
var enCount = entries.Count;
var entry = null;
for (var i = 0; i < enCount; i++)
{
entry = entries.Item(i);
if (entry.URL.indexOf(this.MATCH_URL) != -1)
{
this.parserContent(entry);
break;
}
}
}
-----------------------------------------------------------------
11、 为RetryManage对象添加请求内容解析方法。该方法处理也比较简单,就是使用indexOf方法从字符串中找到匹配,然后获取序号。var begIndex = feeMsg.indexOf(this.MATCH_BEG_MSG);获取到开始串的开始序号,然后使用var endIndex = feeMsg.indexOf(this.MATCH_END_MSG, begIndex);获取结束串的开始序号,注意多了一个begIndex参数是为了保证结束串是开始串之后第一个,否则会导致查询内容不正确。然后使用feeMsg = feeMsg.substring(begIndex, endIndex + this.MATCH_END_MSG.length);取出字串,注意endIndex只是结束串的起始位置,要获得完整子串,还需要在endIndex加上this.MATCH_END_MSG.length。完成后,就可以通过 var tab = document.getElementsByTagName("TABLE")(0); tab.outerHTML = feeMsg;把子串赋值给页面TABLE对象(从这里能看到获取到的子串是否正确)。最后调用foundLower(该方法是自定义的,下一步详述)查找低价信息,第一个参数是指定的日期,第二个参数是指定的最大金额。
-----------------------------------------------------------------
RetryManage.prototype.parserContent = function(entry)
{
var feeMsg = entry.content.data;
var begIndex = feeMsg.indexOf(this.MATCH_BEG_MSG);
if (begIndex == -1)
{
alert("Can not found fee info");
return;
}
var endIndex = feeMsg.indexOf(this.MATCH_END_MSG, begIndex);
if (endIndex == -1)
{
alert("Can not found fee info");
return;
}
feeMsg = feeMsg.substring(begIndex, endIndex + this.MATCH_END_MSG.length);
var tab = document.getElementsByTagName("TABLE")(0);
tab.outerHTML = feeMsg;
this.foundLower("02-13", 1300);
}
-----------------------------------------------------------------
12、 为RetryManage对象添加查找低价信息方法。主要处理就是将表格中数据解析到js对象列表中,然后循环和查询条件匹配。对于找到的信息(包括找到和没有找到)都写入页面对象textarea中,便于监控程序是否在正常运行。附件为解析后得到的表格HTML代码。
-----------------------------------------------------------------
RetryManage.prototype.foundLower = function(needDate, needMaxFee)
{
var tab = document.getElementsByTagName("TABLE")(0);
var fi = null;
var fd = null;
var ff = null;
var feeList = new Array();
for (var i = 1; i < tab.rows.length; i++)
{
for (var j = 0; j < tab.rows(i).cells.length; j++)
{
if (tab.rows(i).cells(j).hasChildNodes())
{
if (!tab.rows(i).cells(j).firstChild.hasChildNodes())
{
continue;
}
fd = trim(tab.rows(i).cells(j).firstChild.childNodes[0].toString());
ff = trim(tab.rows(i).cells(j).firstChild.childNodes[2].innerText);
ff = ff.substring(1);
fi = new FeeItem(fd, ff);
feeList.push(fi);
}
}
}
var hisMsg = document.getElementById("hisMsg");
var curMsg = null;
for (var i = 0; i < feeList.length; i++)
{
if (needDate == feeList[i].getDate())
{
if (new Number(needMaxFee) >= feeList[i].getFee())
{
hisMsg.innerText += "/n/rHas lower tickets fee at:" + feeList[i].getFee() + ". Date:" + new Date();
return;
}
else
{
curMsg = feeList[i].getFee();
}
}
}
hisMsg.innerText += "/n/rNo lower tickets fee below then " + needMaxFee + " Current is:" + curMsg + ". Date:" + new Date();
}
-----------------------------------------------------------------
13、 至此,所有处理方法都已编写完成,就可以编写doStart测试驱动方法。然后在处理完成后调用window.setTimeout(doStart, 30000);设置为每30秒处理一次(不包含处理时间)。将new RetryManage写在doStart方法,避免在每次执行时被重新创建,导致isReady状态丢失,造成每执行一次就打开一个IE窗口。
-----------------------------------------------------------------
RetryManage.prototype.isAllReady = function()
{
return this.isReady;
}
RetryManage.prototype.doTest = function()
{
try
{
if (!this.isAllReady())
{
this.init();
}
if (!this.isAllReady())
{
return;
}
this.procRequest();
window.setTimeout(doStart, 30000);
}
catch (e)
{
alert(e.message);
}
}
function doStart()
{
rm.doTest();
}
var rm = new RetryManage();
doStart();
-----------------------------------------------------------------
14、 到此就全部完成了,剩下的工作就是验证。以下就是源代码.
-----------------------------------------------------------------
<html>
<head>
<title>Retry</title>
<meta http-equiv="CONTENT-TYPE" content="TEXT/HTML; CHARSET=GBK"/>
<meta http-equiv="PRAGMA" content="no-cache">
<meta http-equiv="CACHE-CONTROL" content="no-cache">
<meta http-equiv="EXPIRES" content="0">
<meta NAME="AUTHOR" CONTENT="xuejun">
<meta NAME="KEYWORDS" CONTENT="JS HTTPWATCH">
<meta NAME="DESCRIPTION" CONTENT="Retry">
</head>
<body>
<form>
<table id="retMsg"></table>
<textArea id="hisMsg" rows="10" cols="100"></textArea>
</form>
</body>
</html>
<script>
<!--
function RetryManage()
{
// const url for air tickets query
this.CONST_URL = "http://flights.ctrip.com/Domestic/ShowFareFirst.aspx?homecity=KMG&destcity1=NKG&DDatePeriod1=2011-02-13";
// const url for match query
this.MATCH_URL = "http://flights.ctrip.com/Domestic/ShowFareFirst.aspx";
// const begin match tickets fee table
this.MATCH_BEG_MSG = "<table id=/"ctl00_MainContentPlaceHolder_LowestPriceWebControl_LowestPriceTable/"";
// const end match tickets fee table
this.MATCH_END_MSG = "</table>";
// inner object
this.control = null;
this.plugin = null;
this.isReady = false;
}
RetryManage.prototype.init = function()
{
try
{
// obtian httpwatch control
this.control = new ActiveXObject('HttpWatch.Controller');
// new IE window
this.plugin = this.control.IE.New();
// set ready status
this.isReady = true;
}
catch (e)
{
alert("init:" + e.message);
}
}
RetryManage.prototype.procRequest = function()
{
// clear history log
this.plugin.clear();
// start record
this.plugin.Record();
// go to url
this.plugin.GotoURL(this.CONST_URL);
// wait server return
this.control.Wait(this.plugin, -1);
// stop record
this.plugin.Stop();
// parser message
this.parserMessage(this.plugin.Log.Entries);
}
RetryManage.prototype.parserMessage = function(entries)
{
// total entries(request message)
var enCount = entries.Count;
var entry = null;
for (var i = 0; i < enCount; i++)
{
entry = entries.Item(i);
if (entry.URL.indexOf(this.MATCH_URL) != -1)
{
this.parserContent(entry);
break;
}
}
}
RetryManage.prototype.parserContent = function(entry)
{
var feeMsg = entry.content.data;
var begIndex = feeMsg.indexOf(this.MATCH_BEG_MSG);
if (begIndex == -1)
{
alert("Can not found fee info");
return;
}
var endIndex = feeMsg.indexOf(this.MATCH_END_MSG, begIndex);
if (endIndex == -1)
{
alert("Can not found fee info");
return;
}
feeMsg = feeMsg.substring(begIndex, endIndex + this.MATCH_END_MSG.length);
var tab = document.getElementsByTagName("TABLE")(0);
tab.outerHTML = feeMsg;
this.foundLower("02-13", 1300);
}
RetryManage.prototype.foundLower = function(needDate, needMaxFee)
{
var tab = document.getElementsByTagName("TABLE")(0);
var fi = null;
var fd = null;
var ff = null;
var feeList = new Array();
for (var i = 1; i < tab.rows.length; i++)
{
for (var j = 0; j < tab.rows(i).cells.length; j++)
{
if (tab.rows(i).cells(j).hasChildNodes())
{
if (!tab.rows(i).cells(j).firstChild.hasChildNodes())
{
continue;
}
fd = trim(tab.rows(i).cells(j).firstChild.childNodes[0].toString());
ff = trim(tab.rows(i).cells(j).firstChild.childNodes[2].innerText);
ff = ff.substring(1);
fi = new FeeItem(fd, ff);
feeList.push(fi);
}
}
}
var hisMsg = document.getElementById("hisMsg");
var curMsg = null;
for (var i = 0; i < feeList.length; i++)
{
if (needDate == feeList[i].getDate())
{
if (new Number(needMaxFee) >= feeList[i].getFee())
{
hisMsg.innerText += "/n/rHas lower tickets fee at:" + feeList[i].getFee() + ". Date:" + new Date();
return;
}
else
{
curMsg = feeList[i].getFee();
}
}
}
hisMsg.innerText += "/n/rNo lower tickets fee below then " + needMaxFee + " Current is:" + curMsg + ". Date:" + new Date();
}
RetryManage.prototype.isAllReady = function()
{
return this.isReady;
}
RetryManage.prototype.doTest = function()
{
try
{
if (!this.isAllReady())
{
this.init();
}
if (!this.isAllReady())
{
return;
}
this.procRequest();
window.setTimeout(doStart, 300000);
}
catch (e)
{
alert(e.message);
}
}
function FeeItem(fDate, fFee)
{
this.fDate = fDate;
this.fFee = fFee;
}
FeeItem.prototype.getDate = function()
{
return this.fDate;
}
FeeItem.prototype.getFee = function()
{
return new Number(this.fFee);
}
FeeItem.prototype.toString = function()
{
return this.fDate + ":" + this.fFee;
}
function trim(str)
{
if (str == null)
{
return "";
}
return str.replace(/(^/s*)|(/s*$)/g, "");
}
function doStart()
{
rm.doTest();
}
var rm = new RetryManage();
doStart();
//-->
</script>
-----------------------------------------------------------------
相关文章推荐
- 使用node.js 获取客户端信息代码分享
- 使用node.js 获取客户端信息代码分享
- 使用node.js建博客(六) - 添加代码高亮的支持 (Final)
- 使用node.js建博客(六) - 添加代码高亮的支持 (Final)
- JS使用cookie实现只出现一次的广告代码效果
- JS中定位 position 的使用实例代码
- 使用cvs的关键字来维护代码信息(zt)
- spring mvc 下使用ajaxfileupload.js 异步上传文件 并返回信息 各种问题解决
- 使用phpstorm调试node.js代码
- 使用DOM方法在文档中检索或添加信息的方法以及example代码
- 手风琴相册制作的代码(不使用任何js)
- js之checkbox的代码全选/全不选,使用id获取元素,而不是name
- 使用Beautify.js来美化你的jQuery代码
- 使用NDOUtils将Nagios监控信息存入mysql
- 使用flow捕获js错误提高代码质量
- 解决使用Vue.js显示数据的时,页面闪现原始代码
- 使用otl监控oracle和DB2代码封装
- JS代码优化一:谷歌浏览器的timeline工具的使用
- 多说使用ua-parser-js显示浏览器和系统信息
- BAPI--使用HR_INFOTYPE_OPERATION函数批量导入HR信息纪录代码样例(0759信息类型)