这次的题目很简单,中国银行有一个查当天汇率的网页(http://www.bank-of-china.com/info/qpindex.shtml),不过是传统的Html格式,而其又没有提供Xml格式或者WebService查询。现在如果希望其他的信息系统能够随时读取其中的数据,那么方便的莫过于中行提供一个WebService接口供大家调用,这也是典型的安全的WebService应用。可惜中行没有做,那么我们能不能自己来做呢?当然可以,只要用程序分析其html网页,那么就可以很容易的读取其中的数据。文本分析,当然要看我们的"Regular Expression"(呵呵,其实这才是写这个程序的真实目的 -- 应用正则表达式。)
中行的页面类似于:
日期:2004/09/30 有效期至2004/10/07
货币名称 | 现汇买入价 | 现钞买入价 | 卖出价 | 基准价 |
---|---|---|---|---|
英镑 |
1488.1700 |
1453.1500 |
1492.6400 |
|
港币 |
105.9700 |
105.3300 |
106.2900 |
106.1100 |
美元 |
826.4200 |
821.4500 |
828.9000 |
827.6600 |
瑞士法郎 |
655.9300 |
641.1400 |
659.2200 |
|
新加坡元 |
488.7600 |
477.2600 |
490.2300 |
|
瑞典克朗 |
112.4900 |
109.8400 |
112.8300 |
|
丹麦克朗 |
136.5900 |
133.3700 |
137.0000 |
|
挪威克朗 |
121.9500 |
119.0800 |
122.3100 |
|
日元 |
7.4344 |
7.3785 |
7.4717 |
7.4519 |
加拿大元 |
650.8000 |
635.4800 |
652.7600 |
|
澳大利亚元 |
591.9900 |
578.6400 |
594.9600 |
|
欧元 |
1019.6400 |
1010.9600 |
1022.7000 |
1019.7000 |
澳门元 |
103.2200 |
102.6000 |
103.5300 |
|
菲律宾比索 |
14.6700 |
14.3300 |
14.7200 |
|
泰国铢 |
19.9000 |
19.4300 |
19.9600 |
|
新西兰元 |
553.7000 |
|
555.3600 |
|
对其代码分析后,给出了一个正则表达式,当然这个表达式还不完善,但是针对目前比较固定的中行的汇率页面来说,暂时还没有问题。
@"<tr bgcolor='#\w+' ><td height='20'>(?<currency>.*)</td>\s*" +
@"<td height='20'><p align='right'>(?<bankbuytt>\d*.?\d*)( )+.?</td>\s*" +
@"<td height='20'><p align='right'>(?<buynotes>\d*.?\d*)( )+.?</td>\s*" +
@"<td height='20'><p align='right'>(?<sell>\d*.?\d*)( )+.?</td>\s*" +
@"<td height='20'><p align='right'>(?<base>\d*.?\d*)( )+.?</td>\s*"
然后过滤就非常简单了。我一直以为代码是最好的说明,特别是对于优雅的语言来说,因为我就不多说了,代码伺候。
这是所建WebService页面ForeignExchange.asmx的代码:
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Net;
using System.Web.Services;
using System.Xml;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
namespace ChinaBank
{
/// <summary>
/// Summary description for ForeignExchange.
/// </summary>
[WebService(Namespace="http://dancefires.com/ChinaBank/")]
public class ForeignExchange : System.Web.Services.WebService
{
public ForeignExchange()
{
//CODEGEN: This call is required by the ASP.NET Web Services Designer
InitializeComponent();
}
#region Component Designer generated code
//Required by the Web Services Designer
private IContainer components = null;
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if(disposing && components != null)
{
components.Dispose();
}
base.Dispose(disposing);
}
#endregion
[WebMethod]
public XmlDataDocument GetForeignExchangeRates()
{
return getXmlDoc();
}
[WebMethod]
public DataSet GetForeignExchangeRatesDataSet()
{
return getXmlDoc().DataSet;
}
[WebMethod]
public string GetBankPage()
{
return getWebContent( "http://www.bank-of-china.com/info/whjrpj.html" );
}
// private methods
private string getWebContent( string url )
{
using( WebClient client = new WebClient() )
{
byte[] buffer = client.DownloadData( url );
string str = Encoding.GetEncoding("GB2312").GetString( buffer, 0, buffer.Length );
return str;
}
}
private XmlDataDocument getXmlDoc()
{
string webcontent = getWebContent("http://www.bank-of-china.com/info/whjrpj.html");
// Prepair for DataSet
DataSet ds = new DataSet("Exchange");
DataTable dt = new DataTable("ForeignExchange");
ds.Tables.Add( dt );
dt.Columns.Add( "Currency", typeof(string) );
dt.Columns.Add( "BankBuyTT", typeof(double) );
dt.Columns.Add( "BankBuyNotes", typeof(double) );
dt.Columns.Add( "BankSell", typeof(double) );
dt.Columns.Add( "Baseline", typeof(double) );
XmlDataDocument xmldoc = new XmlDataDocument( ds );
Regex expr = new Regex(
@"<tr bgcolor='#\w+' ><td height='20'>(?<currency>.*)</td>\s*" +
@"<td height='20'><p align='right'>(?<bankbuytt>\d*.?\d*)( )+.?</td>\s*" +
@"<td height='20'><p align='right'>(?<buynotes>\d*.?\d*)( )+.?</td>\s*" +
@"<td height='20'><p align='right'>(?<sell>\d*.?\d*)( )+.?</td>\s*" +
@"<td height='20'><p align='right'>(?<base>\d*.?\d*)( )+.?</td>\s*"
, RegexOptions.Compiled);
for( Match m = expr.Match(webcontent) ; m.Success ; m=m.NextMatch() )
{
string key;
DataRow row = dt.NewRow();
row["Currency"] = m.Groups["currency"];
key = m.Groups["bankbuytt"].ToString();
row["BankBuyTT"] = key.Length > 0 ? Convert.ToDouble( key )/100 : 0;
key = m.Groups["buynotes"].ToString();
row["BankBuyNotes"] = key.Length > 0 ? Convert.ToDouble( key )/100 : 0;
key = m.Groups["sell"].ToString();
row["BankSell"] = key.Length > 0 ? Convert.ToDouble( key )/100 : 0;
key = m.Groups["base"].ToString();
row["Baseline"] = key.Length > 0 ? Convert.ToDouble( key )/100 : 0;
dt.Rows.Add( row );
}
return xmldoc;
}
}
}
客户端也很容易,只要用wsdl生成了相应的WebService Proxy后,直接调用就行了,由于我让Server端返回了DataSet,因此客户端直接用DataGrid来显示DataSet即可,非常Easy,在这个问题上客户端没有什么技术关键点。
using System.Threading;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace BankDataClient
{
/// <summary>
/// Summary description for frmMainBankRates.
/// </summary>
public class frmMainBankRates : System.Windows.Forms.Form
{
private System.Windows.Forms.DataGrid dataGrid1;
private System.Windows.Forms.Button btnConnect;
private System.Data.DataSet ds;
private BankDataClient.com.dancefires.[url]www.ForeignExchange[/url] proxy = new BankDataClient.com.dancefires.www.ForeignExchange();
private System.Windows.Forms.TextBox txtUrl;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public frmMainBankRates()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
try
{
txtUrl.Text = System.Configuration.ConfigurationSettings.AppSettings["url"];
proxy.Url = txtUrl.Text;
}
catch(Exception)
{
proxy.Url = "http://www.dancefires.com/ChinaBank/ForeignExchange.asmx";
txtUrl.Text = proxy.Url;
}
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.dataGrid1 = new System.Windows.Forms.DataGrid();
this.ds = new System.Data.DataSet();
this.btnConnect = new System.Windows.Forms.Button();
this.txtUrl = new System.Windows.Forms.TextBox();
((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.ds)).BeginInit();
this.SuspendLayout();
//
// dataGrid1
//
this.dataGrid1.DataMember = "";
this.dataGrid1.DataSource = this.ds;
this.dataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText;
this.dataGrid1.Location = new System.Drawing.Point(32, 48);
this.dataGrid1.Name = "dataGrid1";
this.dataGrid1.Size = new System.Drawing.Size(480, 256);
this.dataGrid1.TabIndex = 0;
//
// ds
//
this.ds.DataSetName = "Exchange";
this.ds.Locale = new System.Globalization.CultureInfo("zh-CN");
//
// btnConnect
//
this.btnConnect.Location = new System.Drawing.Point(432, 16);
this.btnConnect.Name = "btnConnect";
this.btnConnect.TabIndex = 1;
this.btnConnect.Text = "连接";
this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
//
// txtUrl
//
this.txtUrl.Location = new System.Drawing.Point(32, 16);
this.txtUrl.Name = "txtUrl";
this.txtUrl.Size = new System.Drawing.Size(384, 20);
this.txtUrl.TabIndex = 2;
this.txtUrl.Text = "";
//
// frmMainBankRates
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(544, 318);
this.Controls.Add(this.txtUrl);
this.Controls.Add(this.btnConnect);
this.Controls.Add(this.dataGrid1);
this.Name = "frmMainBankRates";
this.Text = "Foreign Exchange Rates of Bank of China";
((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.ds)).EndInit();
this.ResumeLayout(false);
}
#endregion
private void btnConnect_Click(object sender, System.EventArgs e)
{
UpdateDataGrid();
}
private void UpdateDataGrid()
{
try
{
btnConnect.Enabled = false;
txtUrl.ReadOnly = true;
proxy.Url = txtUrl.Text;
ds = proxy.GetForeignExchangeRatesDataSet();
dataGrid1.SetDataBinding( ds, "ForeignExchange" );
dataGrid1.Update();
}
catch( Exception err )
{
MessageBox.Show( err.Message );
}
finally
{
txtUrl.ReadOnly = false;
btnConnect.Enabled = true;
}
}
[STAThread]
static void Main( string[] args )
{
Application.Run( new frmMainBankRates() );
}
}
}
有了这个例子,应该可以从中了解最基本的XML, WebService, Regular Expression, DataSet, DataGrid的知识。
软件所有代码,及相关截屏可以从下面的连接中获得:
http://www.dancefires.com/ChinaBank/