[ Team LiB ] |
16.4 Creating the Web Services ClientBy the time you read this book, the code shown in Example 16-2 might not work. If Amazon has changed the layout of the book page, the parsing code could easily break. Screen-scraping is notoriously fragile. Fortunately, however, Amazon has provided a web service with methods that allow you to acquire the information you need based on ISBN or other criteria. In the first revision of the desktop application, you'll strip out all the screen scraping code and replace it with a web services client. To make this work, however, you'll need to download the Amazon Web Services developer kit, available at http://www.amazon.com/webservices, and as shown in Figure 16-5. Figure 16-5. Amazon.com Web ServicesThe key is to create a proxy class to use with your client. You do so by obtaining the WSDL document that Amazon supplies, and then compiling that with the command-line instruction: wsdl /o:Amazon.cs AmazonWebServices.wsdl Your next step is to add the resulting file (Amazon.cs) to your new project, which will, among other things, declare the ProductInfo class (an instance of which you'll create in your own class). To get started, create a duplicate of the project shown in Example 16-2, and call it SalesRankDBWebServices, as shown in Example 16-5. Example 16-5. SalesRankDBWebServicesusing System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Web.Services; using System.Text; using System.Text.RegularExpressions; using System.Net; // use Amazon web service rather than // Screen Scraping namespace SalesRankDBWebService { public class Form1 : System.Windows.Forms.Form { private System.Windows.Forms.Button btnStart; private string connectionString; private System.Data.SqlClient.SqlConnection connection; private System.Data.SqlClient.SqlCommand command; private System.Windows.Forms.Timer timer1; private System.ComponentModel.IContainer components; private int timeRemaining; private System.Windows.Forms.Button btnNow; private System.Windows.Forms.TextBox txtClock; private System.Windows.Forms.ListBox lbOutput; const int WaitTime = 900; // 15 min. public Form1( ) { InitializeComponent( ); } /// <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 #endregion /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main( ) { Application.Run(new Form1( )); } private void Form1_Load(object sender, System.EventArgs e) { // connection string to connect to the Bugs Database connectionString = "server=localhost;Trusted_Connection=true;database=AmazonSalesRanks"; // Create connection object, initialize with // connection string. Open it. connection = new System.Data.SqlClient.SqlConnection(connectionString); // Create a SqlCommand object and assign the connection command = new System.Data.SqlClient.SqlCommand( ); command.Connection = connection; timer1.Interval = 1000; // one second timer1.Enabled = false; timeRemaining = 2; // how many seconds until update // UpdateTextFields( ); lbOutput.Items.Add("Version 1.10"); UpdateButton( ); } private void UpdateButton( ) { btnStart.Text = timer1.Enabled ? "Stop" : "Start"; } private void btnStart_Click(object sender, System.EventArgs e) { timer1.Enabled = timer1.Enabled ? false : true; UpdateButton( ); } // this method changes considerably private void GetInfoFromISBN(string isbn, string technology) { if (isbn.Length != 10) return ; ProductInfo pi = null; // declare the ProductInfo object // initialize the local variables string title = ""; string author = ""; string publisher = ""; string pubDate = ""; int rank = 9999999; string strURL=""; // create an instance of the web service AmazonSearchService ws = new AmazonSearchService( ); if ( ws == null ) return; // instantiate AsinRequest object -- willhold info // about the ISBN you want to find AsinRequest req = new AsinRequest( ); req.asin = isbn; req.type = "heavy"; req.tag = "webservices-20"; // replace with yours req.devtag = "XXXXXXXXXXXXX"; // replace with yours try { pi = ws.AsinSearchRequest(req); } catch { MessageBox.Show( "Error accessing Amazon.com's web service."); } if ( pi.Details == null ) return; // set the local variables based on the data you get back title = FixQuotes(pi.Details[0].ProductName); // if you have any authors, get the first if ( pi.Details[0].Authors != null ) author = FixQuotes(pi.Details[0].Authors[0]); publisher = FixQuotes(pi.Details[0].Manufacturer); pubDate = pi.Details[0].ReleaseDate; rank = GetRank(pi.Details[0].SalesRank); strURL = pi.Details[0].Url; // update the list box string results = title + " by " + author + ": " + publisher + ", " + pubDate + ". Rank: " + rank; lbOutput.Items.Add(results); lbOutput.SelectedIndex = lbOutput.Items.Count -1; // update the database string commandString = @"Update BookInfo set isbn = '" + isbn + "', title = '" + title + "', publisher = '" + publisher + "', pubDate = '" + pubDate + "', rank = " + rank + ", link = '" + strURL + "', lastUpdate = '" + System.DateTime.Now + "', technology = '" + technology + "', author = '" + author + "' where isbn = '" + isbn + "'"; command.CommandText = commandString; try { // if no rows were affected, this is a new record connection.Open( ); int numRowsAffected = command.ExecuteNonQuery( ); if (numRowsAffected == 0) { commandString = @"Insert into BookInfo values ('" + isbn + "', '" + title + "', '" + publisher + "', '" + pubDate + "', " + rank + ", '" + strURL + "', '" + System.DateTime.Now + "', '" + technology + "', '" + author + "')"; command.CommandText = commandString; command.ExecuteNonQuery( ); } } catch (Exception e) { lbOutput.Items.Add("Unable to update database!"); lbOutput.SelectedIndex = lbOutput.Items.Count -1; } finally { connection.Close( ); // clean up } Application.DoEvents( ); // update the UI } // close for GetInfoFromISBN private int GetRank(string strRank) { if ( strRank == null ) return 99999; string fixedString = strRank.Replace(",",""); return Convert.ToInt32(fixedString); } private string FixQuotes(string s) { if (s == null) return null; string newString = s.Replace("'",""); return newString; } private void timer1_Tick(object sender, System.EventArgs e) { if (timer1.Enabled) txtClock.Text = (--timeRemaining).ToString( ) + " seconds"; else txtClock.Text = "Stopped"; if ( timeRemaining < 1 ) { timeRemaining = WaitTime; DataSet BookData = new DataSet( ); BookData.ReadXml("aspnetIsbn.xml"); foreach(DataRow Book in BookData.Tables[0].Rows) { string isbn = Book[0].ToString( ); GetInfoFromISBN(isbn,"ASPNET"); } BookData = new DataSet( ); BookData.ReadXml("csharpIsbn.xml"); foreach(DataRow Book in BookData.Tables[0].Rows) { string isbn = Book[0].ToString( ); GetInfoFromISBN(isbn,"CSHARP"); } BookData = new DataSet( ); BookData.ReadXml("VBnetIsbn.xml"); foreach(DataRow Book in BookData.Tables[0].Rows) { string isbn = Book[0].ToString( ); GetInfoFromISBN(isbn,"VBNET"); } } } private void btnNow_Click(object sender, System.EventArgs e) { timeRemaining = 2; } } } The key change is in GetInfoFromISBN. Once again you check that the ISBN is 10 characters: if (isbn.Length != 10) return ; But after that, the entire method is changed. The first thing to do is to declare an instance of ProductInfo. This object will represent all the information about the book that you'll retrieve from Amazon: ProductInfo pi = null; Next, you'll initialize local variables that will be used to hold the values returned within the ProductInfo object: string title = ""; string author = ""; string publisher = ""; string pubDate = ""; int rank = 9999999; string strURL=""; The first step is to create an instance of the AmazonSearchService. AmazonSearchService ws = new AmazonSearchService( );
The kind of search you'll be conducting for this version of the program is an Amazon Standard Item Number (which turns out, for books, to be the ISBN). To do this, you'll create an instance of an AsinRequest object, and set its asin property to the ISBN you're searching for: AsinRequest req = new AsinRequest( ); req.asin = isbn; You need to set the tag property to either the default (webservices-20) or to the name of your Amazon Associates account (if you run an Amazon Associates web site): req.tag = "webservices-20"; You'll set the type to either "heavy" or "lite." Typically, a lite document contains only essential catalog information (product price, name, etc.), while a "heavy" document contains more complete product information. Finally, you'll add the devtag that Amazon supplied you with when you registered the Web Services developer's kit: req.type = "heavy"; req.devtag = "XXXXXXXXXXXXX"; You are now ready to call the AsinSearchRequest method on the web service, passing in the AsinRequest object you've created: try { pi = ws.AsinSearchRequest(req); } catch { MessageBox.Show( "Error accessing Amazon.com's web service."); } Assuming you have not thrown an exception, what you get back is a populated ProductInfo object, defined in Amazon.cs. Inside that object, among other things, is a Details collection, which has all the information you need.
You use the members of the Details collection to set the values of the local variables. title = FixQuotes(pi.Details[0].ProductName); if ( pi.Details[0].Authors != null ) ...author = FixQuotes(pi.Details[0].Authors[0]); publisher = FixQuotes(pi.Details[0].Manufacturer); pubDate = pi.Details[0].ReleaseDate; rank = GetRank(pi.Details[0].SalesRank); strURL = pi.Details[0].Url; The FixQuotes method is a private helper method that simply removes single quotes so as not to confuse SQL Server. You must check to make sure the Authors collection is not null, so that you do not throw an exception when you try to access the first member. private string FixQuotes(string s) { if (s == null) return null; string newString = s.Replace("'",""); return newString; }
Now that you've populated the local variables, you can create the string for the listbox, and display that as you did previously, string results = title + " by " + author + ": " + publisher + ", " + pubDate + ". Rank: " + rank; lbOutput.Items.Add(results); lbOutput.SelectedIndex = lbOutput.Items.Count -1; The rest of the updating code is identical to the previous version. |
[ Team LiB ] |