Use the Product Advertising API from Amazon

Good day, harrowdene!

image

In this post I want to tell you about my first experience with Amazon Product Advertising API. This API allows You to perform a parameterized search of the goods and all information which is linked on the websites of Amazon. Also have an affiliate program with which you can later monetize (1. Get Affiliate ID, 2. Add it to the query 3. Profit? ...). To register the program you can here.
If you're interested, read on.

the Backstory


The post will start with the background, why do I go there? Was 2012, the month of March, the very beginning. And that's on the horizon began to appear "international women's day", in which it is customary to give flowers, various gifts to their mothers, wives, daughters, girls. After a few minutes of deliberation, the decision was made to purchase the wife of the second iPad and colors respectively. As the flowers in contrast to the device can be purchased without problems, their purchase was postponed on the same day "X". In the city where I live, poorly developed market of various gadgets, there is healthy competition (and unhealthy, in principle, too), so prices are just outrageous. By happy coincidence, my colleague was on a business trip overseas and I decided to buy the device on Amazon.com. Proposals were weight, prices are also ranged from 300$-700$. And somehow, just went to the site in search of more lucrative offers, and saw that some a reseller threw in the sale of 8 new devices at a ridiculous price until I tried to checkout and scored in the form of data, they already grind. A little googling on the subject, decided to write something for monitoring prices for the device.

Inception


To get started, go to your AWS account, go to Security Credentials and then click Create a new Access Key. As a result we get the following picture:

Cherished key pair is obtained, let it test. Enter our keys on the form to Unsigned URL of the writing for our particular situation URL:
the
http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService
&Version=2011-08-01
&AssociateTag=520
&Operation=ItemLookup
&ResponseGroup=Large
&ItemId=B0047DVWLW


A little explain, I did not register the affiliate program so AssociateTag out of my head. Operation=ItemLookup (all operations supported by the service can I see them tut) return all or some properties (depending on the ResponseGroup) at the desired ItemId (which can be determined by URL).

Click Display Signed URL... and voila, we have signed our keys link! Clicking on it will get:

XML Response type (had a little okorotit, Habr refused to eat the whole thing):
<ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
<OperationRequest>
<HTTPHeaders>
<Header Name="UserAgent" Value="Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1"/>
</HTTPHeaders>
<RequestId>9645cf90-c1ae-4505-be21-08198e5e8274</RequestId>
<Arguments>
<Argument Name="ItemId" Value="B0047DVWLW"/>
<Argument Name="Operation" Value="ItemLookup"/>
<Argument Name="Service" Value="AWSECommerceService"/>
<Argument Name="AWSAccessKeyId" Value="AKIAILZWKKRUXK7QRNRA"/>
<Argument Name="Timestamp" Value="2012-09-21T10:34:09.000 Z"/>
<Argument Name="Signature" Value="w8kK7v5WuPO2lxaUwtnw1fax10SMcqN8wg8qtqudehq="/>
<Argument Name="ResponseGroup" Value="Large"/>
<Argument Name="AssociateTag" Value="520"/>
<Argument Name="Version" Value="2011-08-01"/>
</Arguments>
<RequestProcessingTime>0.2015160000000000</RequestProcessingTime>
</OperationRequest>
<Items>
<Request>
<IsValid>True</IsValid>
<ItemLookupRequest>
<IdType>ASIN</IdType>
<ItemId>B0047DVWLW</ItemId>
<ResponseGroup > Large < /ResponseGroup>
<VariationPage>All</VariationPage>

</Request>
<Item>
<ASIN>B0047DVWLW</ASIN>
<ParentASIN>B004QGY7M6</ParentASIN > 
<DetailPageURL>
http://www.amazon.com/Apple-MC979LL-Tablet-White-Generation/dp/B0047DVWLW%3FSubscriptionId%3DAKIAILZWKKRUXK7QRNRA%26tag%3D520%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB0047DVWLW
</DetailPageURL>
<ItemLinks>
<ItemLink>
<Description>Technical Details</Description>
<URL>
http://www.amazon.com/Apple-MC979LL-Tablet-White-Generation/dp/tech-data/B0047DVWLW%3FSubscriptionId%3DAKIAILZWKKRUXK7QRNRA%26tag%3D520%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0047DVWLW
</URL>
</ItemLink>
<ItemLink>
<Description>Add To Baby Registry</Description>
<URL>
http://www.amazon.com/gp/registry/baby/add-item.html%3Fasin.0%3DB0047DVWLW%26SubscriptionId%3DAKIAILZWKKRUXK7QRNRA%26tag%3D520%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0047DVWLW
</URL>
</ItemLink>
<ItemLink>
<Description>Add To Wedding Registry</Description>
<URL>
http://www.amazon.com/gp/registry/wedding/add-item.html%3Fasin.0%3DB0047DVWLW%26SubscriptionId%3DAKIAILZWKKRUXK7QRNRA%26tag%3D520%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0047DVWLW
</URL>
</ItemLink>
<ItemLink>
<Description>Add To Wishlist</Description>
<URL>
http://www.amazon.com/gp/registry/wishlist/add-item.html%3Fasin.0%3DB0047DVWLW%26SubscriptionId%3DAKIAILZWKKRUXK7QRNRA%26tag%3D520%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0047DVWLW
</URL>
</ItemLink>
<ItemLink>
<Description>Tell A Friend</Description>
<URL>
http://www.amazon.com/gp/pdp/taf/B0047DVWLW%3FSubscriptionId%3DAKIAILZWKKRUXK7QRNRA%26tag%3D520%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0047DVWLW
</URL>
</ItemLink>
<ItemLink>
<Description>All Customer Reviews</Description>
<URL>
http://www.amazon.com/review/product/B0047DVWLW%3FSubscriptionId%3DAKIAILZWKKRUXK7QRNRA%26tag%3D520%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0047DVWLW
</URL>
</ItemLink>
<ItemLink>
<Description>All Offers</Description>
<URL>
http://www.amazon.com/gp/offer-listing/B0047DVWLW%3FSubscriptionId%3DAKIAILZWKKRUXK7QRNRA%26tag%3D520%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0047DVWLW
</URL>
</ItemLink>
</ItemLinks>
<SalesRank>8</SalesRank>
<SmallImage>
<URL>
http://ecx.images-amazon.com/images/I/41Yisrlx%2BFL._SL75_.jpg
</URL>
<Height Units="pixels">63</Height>
<Width Units="pixels">75</Width>
</SmallImage>
<MediumImage>
<URL>
http://ecx.images-amazon.com/images/I/41Yisrlx%2BFL._SL160_.jpg
</URL>
<Height Units="pixels">135</Height>
<Width Units="pixels">160</Width>
</MediumImage>
<LargeImage>
<URL>
http://ecx.images-amazon.com/images/I/41Yisrlx%2BFL.jpg
</URL>
<Height Units="pixels">365</Height>
<Width Units="pixels">434</Width>
</LargeImage>
<ImageSets>
<ImageSet Category="primary">
<SwatchImage>
<URL>
http://ecx.images-amazon.com/images/I/41Yisrlx%2BFL._SL30_.jpg
</URL>
<Height Units="pixels">25</Height>
<Width Units="pixels">30</Width>
</SwatchImage>
<SmallImage>
<URL>
http://ecx.images-amazon.com/images/I/41Yisrlx%2BFL._SL75_.jpg
</URL>
<Height Units="pixels">63</Height>
<Width Units="pixels">75</Width>
</SmallImage>
<ThumbnailImage>
<URL>
http://ecx.images-amazon.com/images/I/41Yisrlx%2BFL._SL75_.jpg
</URL>
<Height Units="pixels">63</Height>
<Width Units="pixels">75</Width>
</ThumbnailImage>
<TinyImage>
<URL>
http://ecx.images-amazon.com/images/I/41Yisrlx%2BFL._SL110_.jpg
</URL>
<Height Units="pixels">93</Height>
<Width Units="pixels">110</Width>
</TinyImage>
<MediumImage>
<URL>
http://ecx.images-amazon.com/images/I/41Yisrlx%2BFL._SL160_.jpg
</URL>
<Height Units="pixels">135</Height>
<Width Units="pixels">160</Width>
</MediumImage>
<LargeImage>
<URL>
http://ecx.images-amazon.com/images/I/41Yisrlx%2BFL.jpg
</URL>
<Height Units="pixels">365</Height>
<Width Units="pixels">434</Width>
</LargeImage>
</ImageSet>
<ImageSet Category="variant">
<SwatchImage>
<URL>
http://ecx.images-amazon.com/images/I/31lmfWbe6-L._SL30_.jpg
</URL>
<Height Units="pixels">15</Height>
<Width Units="pixels">30</Width>
</SwatchImage>
<SmallImage>
<URL>
http://ecx.images-amazon.com/images/I/31lmfWbe6-L._SL75_.jpg
</URL>
<Height Units="pixels">38</Height>
<Width Units="pixels">75</Width>
</SmallImage>
<ThumbnailImage>
<URL>
http://ecx.images-amazon.com/images/I/31lmfWbe6-L._SL75_.jpg
</URL>
<Height Units="pixels">38</Height>
<Width Units="pixels">75</Width>
</ThumbnailImage>
<TinyImage>
<URL>
http://ecx.images-amazon.com/images/I/31lmfWbe6-L._SL110_.jpg
</URL>
<Height Units="pixels">56</Height>
<Width Units="pixels">110</Width>
</TinyImage>
<MediumImage>
<URL>
http://ecx.images-amazon.com/images/I/31lmfWbe6-L._SL160_.jpg
</URL>
<Height Units="pixels">81</Height>
<Width Units="pixels">160</Width>
</MediumImage>
<LargeImage>
<URL>
http://ecx.images-amazon.com/images/I/31lmfWbe6-L.jpg
</URL>
<Height Units="pixels">253</Height>
<Width Units="pixels">500</Width>
</LargeImage>
</ImageSet>
<ImageSet Category="variant">
<SwatchImage>
<URL>
http://ecx.images-amazon.com/images/I/41omkn%2BPH6L._SL30_.jpg
</URL>
<Height Units="pixels">30</Height>
<Width Units="pixels">25</Width>
</SwatchImage>
<SmallImage>
<URL>
http://ecx.images-amazon.com/images/I/41omkn%2BPH6L._SL75_.jpg
</URL>
<Height Units="pixels">75</Height>
<Width Units="pixels">61</Width>
</SmallImage>
<ThumbnailImage>
<URL>
http://ecx.images-amazon.com/images/I/41omkn%2BPH6L._SL75_.jpg
</URL>
<Height Units="pixels">75</Height>
<Width Units="pixels">61</Width>
</ThumbnailImage>
<TinyImage>
<URL>
http://ecx.images-amazon.com/images/I/41omkn%2BPH6L._SL110_.jpg
</URL>
<Height Units="pixels">110</Height>
<Width Units="pixels">90</Width>
</TinyImage>
<MediumImage>
<URL>

</URL>
<Height Units="pixels">160</Height>
<Width Units="pixels">131</Width>
</MediumImage>
<LargeImage>
<URL>
http://ecx.images-amazon.com/images/I/41omkn%2BPH6L.jpg
</URL>
<Height Units="pixels">500</Height>
<Width Units="pixels">409</Width>
</LargeImage>
</ImageSet>
<ImageSet Category="variant">
<SwatchImage>
<URL>
http://ecx.images-amazon.com/images/I/31gqTQEEqAL._SL30_.jpg
</URL>
<Height Units="pixels">30</Height>
<Width Units="pixels">25</Width>
</SwatchImage>
<SmallImage>
<URL>
http://ecx.images-amazon.com/images/I/31gqTQEEqAL._SL75_.jpg
</URL>
<Height Units="pixels">75</Height>
<Width Units="pixels">63</Width>
</SmallImage>
<ThumbnailImage>
<URL>
http://ecx.images-amazon.com/images/I/31gqTQEEqAL._SL75_.jpg
</URL>
<Height Units="pixels">75</Height>
<Width Units="pixels">63</Width>
</ThumbnailImage>
<TinyImage>
<URL>
http://ecx.images-amazon.com/images/I/31gqTQEEqAL._SL110_.jpg
</URL>
<Height Units="pixels">110</Height>
<Width Units="pixels">93</Width>
</TinyImage>
<MediumImage>
<URL>
http://ecx.images-amazon.com/images/I/31gqTQEEqAL._SL160_.jpg
</URL>
<Height Units="pixels">160</Height>
<Width Units="pixels">135</Width>
</MediumImage>
<LargeImage>
<URL>
http://ecx.images-amazon.com/images/I/31gqTQEEqAL.jpg
</URL>
<Height Units="pixels">365</Height>
<Width Units="pixels">309</Width>
</LargeImage>
</ImageSet>
</ImageSets>
<ItemAttributes>
<Binding>Personal Computers</Binding>
<Brand>Apple</Brand>
<CatalogNumberList>
<CatalogNumberListElement>B0047DVWLW</CatalogNumberListElement > 
</CatalogNumberList>
<Color>White</Color>
<EAN>0885909471812</EAN>
<EANList>
<EANListElement>0885909471812</EANListElement>
<EANListElement>0811331000009</EANListElement>
</EANList>
<Feature>Designed for Apple's 2nd  generation  of iPads</Feature>
<Feature>
9.7-inch (diagonal) LED-backlit glossy widescreen Multi-Touch display with IPS technology
</Feature>
<Feature>1 GHz dual-core Apple A5 custom-designed processor</Feature>
<Feature>Forward facing and rear facing cameras</Feature>
<Feature>Apple's iOS 4 and access to Apple's app store</Feature>
<Format>CD-ROM</Format>
<HardwarePlatform>Mac</HardwarePlatform > 
<IsAutographed>0</IsAutographed>
<IsEligibleForTradeIn>1</IsEligibleForTradeIn>
<IsMemorabilia>0</IsMemorabilia>
<ItemDimensions>
<Height Units="hundredths-inches">949</Height>
<Length Units="hundredths-inches">35</Length>
<Weight Units="hundredths-pounds">133</Weight>
<Width Units="hundredths-inches">732</Width>
</ItemDimensions>
<Label>Apple Computer</Label>
<LegalDisclaimer>
Item Will Not Be Shipped Until Payment Clears And Funds Are Tranferred To My Bank Account!
</LegalDisclaimer>
<ListPrice>
<Amount>39900</Amount>
<CurrencyCode>USD</CurrencyCode>
< FormattedPrice > $399.00 < /FormattedPrice > 
</ListPrice>
<Manufacturer>Apple Computer</Manufacturer>
<Model>MC979LL/A</Model>
< MPN > MC979LL/A < /MPN>
<NumberOfItems>1</NumberOfItems>
<OperatingSystem>Apple iOS 5.0</OperatingSystem>
<PackageDimensions>
<Height Units="hundredths-inches">350</Height>
<Length Units="hundredths-inches">1140</Length>
<Weight Units="hundredths-pounds">133</Weight>
<Width Units="hundredths-inches">960</Width>
</PackageDimensions>
<PackageQuantity>1</PackageQuantity>
<PartNumber>MC979LL/A</PartNumber>
<ProductGroup>Personal Computer</ProductGroup>
<ProductTypeName>TABLET_COMPUTER</ProductTypeName>
<Publisher>Apple Computer</Publisher>
<Size>16GB</Size>
<SKU>118087@634532900684301250</SKU>
<Studio>Apple Computer</Studio>
<Title>
Apple iPad 2 MC979LL/A Tablet (16GB, Wifi, White) 2nd Generation
</Title>
<TradeInValue>
<Amount>33000</Amount>
<CurrencyCode>USD</CurrencyCode>
<FormattedPrice>$330.00</FormattedPrice > 
</TradeInValue>
<UPC>811331000009</UPC>
<UPCList>
<UPCListElement>811331000009</UPCListElement > 
<UPCListElement>885909471812</UPCListElement > 
</UPCList>
</ItemAttributes>
<OfferSummary>
<LowestNewPrice>
<Amount>39900</Amount>
<CurrencyCode>USD</CurrencyCode>
< FormattedPrice > $399.00 < /FormattedPrice > 
</LowestNewPrice>
<LowestUsedPrice>
<Amount>34000</Amount>
<CurrencyCode>USD</CurrencyCode>
<FormattedPrice>$340.00</FormattedPrice > 
</LowestUsedPrice>
<LowestRefurbishedPrice>
<Amount>34999</Amount>
<CurrencyCode>USD</CurrencyCode>
< FormattedPrice > $349.99 < /FormattedPrice > 
</LowestRefurbishedPrice>
<TotalNew>85</TotalNew>
<TotalUsed>100</TotalUsed>
<TotalCollectible > 0 < /TotalCollectible>
<TotalRefurbished>17</TotalRefurbished>
</OfferSummary>
<Offers>
<TotalOffers > 1 < /TotalOffers>
<TotalOfferPages > 1 < /TotalOfferPages > 
<MoreOffersUrl>
http://www.amazon.com/gp/offer-listing/B0047DVWLW%3FSubscriptionId%3DAKIAILZWKKRUXK7QRNRA%26tag%3D520%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0047DVWLW
</MoreOffersUrl>
<Offer>
<OfferAttributes>
<Condition>New</Condition>
</OfferAttributes>
<OfferListing>
<OfferListingId>
5tIFOSgtOfWUjj1N2%2FBqWUyXOtsYzDcDWOygGn8T3wdoo5gs1flvqgbaotanvlgmtxh1rwsyi57d%2FfNHr%2BWexLUNQrcrhi1RM1OxR%2B65I%2Fs2Ofz0nfJ83bhbwZNUqm75udmgNjgk2t%2F3%2FJhFd5Cc87KIbmpEK7SH
</OfferListingId > 
<Price>
<Amount>41400</Amount>
<CurrencyCode>USD</CurrencyCode>
<FormattedPrice>$414.00</FormattedPrice > 
</Price>
<AvailabilityAttributes>
<AvailabilityType>futureDate</AvailabilityType>
<MinimumHours>0</MinimumHours>
<MaximumHours>0</MaximumHours > 
</AvailabilityAttributes>
<IsEligibleForSuperSaverShipping>; 1 < /IsEligibleForSuperSaverShipping>;
</OfferListing>
</Offer>
</Offers>



From the Response you can see what information is provided by a value (for example, was taken ResponseGroup = Large, therefore, for a particular problem, we got a lot of extra data, the best fit ResponseGroup = Offers).
All information on working with the service can be found in Developers Guide.

Search bikes


The service provides WSDL. Ie if we add it as a Web Service Reference to our decision, in the end, get the proxy to work with the service. Okay, here is a Request which we will send to the service you have to sign the resulting SAK (Secret Access Key).
The authentication process with our looks as follows:

From AWS:

Everything is clear, but the service is lacking the functionality for the subscription request Your SAK. The simplest solution at the time seemed to view samples and to find the needed functionality, but not so simple as it seems. Of all me samples failed to find a working code for signing request. Zaguglit quickly found a solution:
C# solution for the signature Request
class SignRequestHelper
{
private string endPoint;
private string akid;
private byte[] secret;
private HMAC signer;

private const string REQUEST_URI = "/onca/xml";
private const string REQUEST_METHOD = "GET";

/*
* Use this constructor to create the object. The AWS credentials are available on
* http://aws.amazon.com
* 
* The destination is the service end-point for your application:
* US: ecs.amazonaws.com
* JP: ecs.amazonaws.jp
* UK: ecs.amazonaws.co.uk
* DE: ecs.amazonaws.de
* FR: ecs.amazonaws.fr
* CA: ecs.amazonaws.ca
*/
public SignRequestHelper(string awsAccessKeyId, string awsSecretKey, string destination)
{
this.endPoint = destination.ToLower();
this.akid = awsAccessKeyId;
this.secret = Encoding.UTF8.GetBytes(awsSecretKey);
this.signer = new HMACSHA256(this.secret);
}

/*
* Sign a request in the form of a Dictionary of name-value pairs.
* 
* This method returns a complete URL to use. Modifying the returned URL
* in any way invalidates the signature and Amazon will reject the requests.
*/
public string Sign(IDictionary<string, string> request)
{
// Use a SortedDictionary to get the parameters in natural byte order, as
// required by AWS.
ParamComparer pc = new ParamComparer();
SortedDictionary<string, string> sortedMap = new SortedDictionary<string, string>(request, pc);

// Add the AWSAccessKeyId and Timestamp to the requests.
sortedMap["AWSAccessKeyId"] = this.akid;
sortedMap["Timestamp"] = this.GetTimestamp();

// Get the canonical query string
string canonicalQS = this.ConstructCanonicalQueryString(sortedMap);

// Derive the bytes needs to be signed.
StringBuilder builder = new StringBuilder();
builder.Append(REQUEST_METHOD)
.Append("\n")
.Append(this.endPoint)
.Append("\n")
.Append(REQUEST_URI)
.Append("\n")
.Append(canonicalQS);

string stringToSign = builder.ToString();
byte[] toSign = Encoding.UTF8.GetBytes(stringToSign);

// Compute the signature and convert to Base64.
byte[] sigBytes = signer.ComputeHash(toSign);
string signature = Convert.Tobase64string method(sigBytes);

// now construct the complete URL and return to caller.
QsBuilder StringBuilder = new StringBuilder();
qsBuilder.Append("http://")
.Append(this.endPoint)
.Append(REQUEST_URI)
.Append("?")
.Append(canonicalQS)
.Append("&Signature=")
.Append(this.PercentEncodeRfc3986(signature));

return qsBuilder.ToString();
}

/*
* Sign a request in the form of a query string.
* 
* This method returns a complete URL to use. Modifying the returned URL
* in any way invalidates the signature and Amazon will reject the requests.
*/
public string Sign(string queryString)
{
IDictionary<string, string> request = this.CreateDictionary(queryString);
return this.Sign(request);
}

/*
* Current time in IS0 8601 format as required by Amazon
*/
private string GetTimestamp()
{
DateTime currentTime = DateTime.UtcNow;
string timestamp = currentTime.ToString("yyyy-MM-ddTHH:mm:ssZ");
return timestamp;
}

/*
* Percent-encode (URL Encode) according to RFC 3986 as required by Amazon.
* 
* This is necessary because .NET's HttpUtility.UrlEncode does not encode
* according to the above standard. Also, .NET returns lower-case encoding
* by default and Amazon requires upper-case encoding.
*/
private string PercentEncodeRfc3986(string str)
{
str = HttpUtility.UrlEncode(str, System.Text.Encoding.UTF8);
str = str.Replace("'", "%27").Replace("(", "%28").Replace(")", "%29").Replace("*", "%2A").Replace("!", "%21").Replace("%7e", "~").Replace("+", "%20");

StringBuilder sbuilder = new StringBuilder(str);
for (int i = 0; i < sbuilder.Length; i++)
{
if (sbuilder[i] == '%')
{
if (Char.IsLetter(sbuilder[i + 1]) || Char.IsLetter(sbuilder[i + 2]))
{
sbuilder[i + 1] = Char.ToUpper(sbuilder[i + 1]);
sbuilder[i + 2] = Char.ToUpper(sbuilder[i + 2]);
}
}
}
return sbuilder.ToString();
}

/*
* Convert a query string to corresponding dictionary of name-value pairs.
*/
private IDictionary<string, string > CreateDictionary(string queryString)
{
Dictionary<string, string> map = new Dictionary<string, string>();

string[] requestParams = queryString.Split('&');

for (int i = 0; i < requestParams.Length; i++)
{
if (requestParams[i].Length < 1)
{
continue;
}

char[] sep = { '=' };
string[] param = requestParams[i].Split(sep, 2);
for (int j = 0; j < param.Length; j++)
{
param[j] = HttpUtility.UrlDecode(param[j], System.Text.Encoding.UTF8);
}
switch (param.Length)
{
case 1:
{
if (requestParams[i].Length >= 1)
{
if (requestParams[i].ToCharArray()[0] == '=')
{
map[""] = param[0];

else
{
map[param[0]] = "";
}
}
break;
}
case 2:
{
if (!string.IsNullOrEmpty(param[0]))
{
map[param[0]] = param[1];
}
}
break;
}
}

return map;
}

private string ConstructCanonicalQueryString(SortedDictionary<string, string> sortedParamMap)
{
StringBuilder builder = new StringBuilder();

if (sortedParamMap.Count == 0)
{
builder.Append("");
return builder.ToString();
}

foreach (KeyValuePair<string, string> kvp in sortedParamMap)
{
builder.Append(this.PercentEncodeRfc3986(kvp.Key));
builder.Append("=");
builder.Append(this.PercentEncodeRfc3986(kvp.Value));
builder.Append("&");
}
string canonicalString = builder.ToString();
canonicalString = canonicalString.Substring(0, canonicalString.Length - 1);
return canonicalString;
}
}

class ParamComparer : IComparer<string>
{
public int Compare(string p1, string p2)
{
return string.CompareOrdinal(p1, p2);
}
}


Use will be as follows:
the
/*...*/
SignRequestHelper signRequestHelper = new SignRequestHelper(Settings.Default.MyAWSKeyID, Settings.Default.MyAWSSecretKey, Settings.Default.Destination);
/*....*/
XmlDocument document = RetrieveXmlResponse(this.signRequestHelper.Sign(requestString));
/*...*/
private static XmlDocument RetrieveXmlResponse(string url)
{
try
{
WebRequest request = HttpWebRequest.Create(url);
WebResponse response = request.GetResponse();
XmlDocument doc = new XmlDocument();

doc.Load(response.GetResponseStream());

return doc;
}

catch (Exception e)
{
Console.WriteLine("Caught Exception:" + e.Message);
Console.WriteLine("Stack Trace:" + e.StackTrace);
}

return null;
}

At first I tried to parse the XmlDocument that came from response (which at the root was not right and took a lot of time). Then I remembered the proxy which had a class ItemLookupResponse (the root element of the XML response) and deserialize. And it was a lot easier:
the
public ItemLookupResponse RetrieveItemDetails(string itemID)
{
Result ItemLookupResponse = new ItemLookupResponse();

string requestString = String.Format(Settings.Default.RequestFormatString,
Settings.Default.ServiceParameter,
Settings.Default.VersionParameter,
Settings.Default.OperationParameter,
ResponseGroup.Large.ToString(),
itemID,
Settings.Default.AssociateTagParameter);

XmlDocument document = RetrieveXmlResponse(this.signRequestHelper.Sign(requestString));

var ser = new XmlSerializer(typeof(ItemLookupResponse), Settings.Default.NamespaceURI);

try
{
var wrapper = (ItemLookupResponse)ser.Deserialize(new XmlNodeReader(document.DocumentElement));

result = wrapper;
}

catch (Exception ex)
{
Console.WriteLine("Caught Exception:" + ex.Message);
Console.WriteLine("Stack Trace:" + ex.StackTrace);
}

return result;
}

In the end we get object ItemLookupResponse with all the desired properties. We can pull from the object Item
the
Item item = GetItem(result);
/* item.OfferSummary.LowestCollectiblePrice;
item.OfferSummary.LowestNewPrice;
item.OfferSummary.LowestRefurbishedPrice;
item.OfferSummary.LowestUsedPrice;
*/

All he wanted, we got. Once a reservation request for ItemLookup is not limited to one &ItemId=B0047DVWLW they may be more at times. ItemID — the so-called ASIN (Amazon Standard Identification Number). Some more posledoval ItemLookupResponse caught my eye session:
the
<Tool>
<SimilarProduct>
<ASIN>B0013FRNKG</ASIN>
<Title>
Apple iPad 2 MC769LL/A Tablet (16GB, WiFi, Black) 2nd Generation
</Title>
</SimilarProduct > 
<SimilarProduct>
<ASIN>B003D8GAA0</ASIN>
<Title>
3 Pack of Premium Crystal Clear Screen Protectors for Apple iPad
</Title>
</SimilarProduct > 
/*..........*/

Ie in the XML response still arrives a set of similar devices. Therefore, the idea to remove from the database all the necessary devices and similar products through RetrieveItemDetails(string itemID) (write a recursive method). As You may have noticed ASIN = B003D8GAA0 hides completely we do not need a screen protector (and therefore filtered these elements in response).
Search for similar devices
private void GetAllSimilarItemDetails(string asin)
{
try
{
ItemLookupResponse lookupResponse = RetrieveItemDetails(asin);

if (!devicesCollection.ContainsKey(asin))
{
devicesCollection.Add(asin, lookupResponse);
}

else
{
return;
}

foreach (var item in lookupResponse.Items)
{
foreach (var item in internalItem.Item)
{
isContainNeededNode bool = false;

if (internalItem.ItemAttributes.Title.ToLower().Contains("device_title"))
{
foreach (var node in internalItem.BrowseNodes.BrowseNode)
{
if (node.Name == "Tablets" || node.Name == "Electronics")
{
isContainNeededNode = true;
}
}

if (isContainNeededNode)
{
foreach (var similarItem in internalItem.SimilarProducts)
{
GetAllSimilarItemDetails(similarItem.ASIN);
}
}

else
{
break;
}
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}



Notification


As an alert was selected mail and Twitter. Established the timer and after a certain interval have checked the price of the device, and if the price differed in the smaller side since the last check or the minimum specified on the soap left a letter and a certain Twitter account, postal very same message (via Twitterizer).

run


If you let the desire and time, there is an idea to rewrite this whole thing in a Windows Service which will monitor the specified parameters. And to write a responsive twitbot which will be mention with a specific tag and parameters to give the user some info.

Result


And now about the main thing, the device was purchased and donated by the wife. Everyone is happy!

Links


the
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

The release of the new version of the module modLivestreet 0.3.0-rc

mSearch: search + filter for MODX Revolution

Emulator data from GNSS receiver NMEA