【背景】
需要折腾amazon产品信息的抓取,之前是通过网页直接抓取,
但是由于网页变数太多,所以代码实现起来,很麻烦,效率很低。
后来得知了Amazon有个API,AWS,后来叫做
Amazon Product Advertising API
,可以供使用,可以实现很多相关的产品信息的查询功能:
所以,就打算去折腾折腾。
【折腾过程】
1.继续参考:
中提到的:
REST版本的:
Product Advertising API Signed Requests Sample Code – C# REST/QUERY
去下载看看。
2.参考了其中的介绍:
About this Sample
Prerequisites
Reference DocumentationQuick StartSample comes with SignedRequestHelper with the signature logic and an ItemLookupSample C# class that uses the SignedRequestHelper to perform a simple ItemLookup operation. To get started with the sample, follow these steps:
Comments, Questions or FeedbackIf you have any comments, questions or feedback on the library, please visit the Amazon Amazon Product Advertising API discussion forums. |
先去:
登陆,从:
https://portal.aws.amazon.com/gp/aws/securityCredentials
找到自己的ASW Access Key ID和Secret Key。
3.打开项目:
AmazonProductAdvtApiSample.sln
其中由于项目是旧版本的,所以还需要用当前新的VS2010,将项目转换一下。
找到ItemLookupSample.cs,将其中的
MY_AWS_ACCESS_KEY_ID
MY_AWS_SECRET_KEY
改为自己的值。
其他啥都不改:
/**********************************************************************************************
* Copyright 2009 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
* except in compliance with the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under the License.
*
* ********************************************************************************************
*
* Amazon Product Advertising API
* Signed Requests Sample Code
*
* API Version: 2009-03-31
*
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
using System.Xml.XPath;
namespace AmazonProductAdvtApi
{
class ItemLookupSample
{
private const string MY_AWS_ACCESS_KEY_ID = "xxx";
private const string MY_AWS_SECRET_KEY = "yyy";
private const string DESTINATION = "ecs.amazonaws.com";
private const string NAMESPACE = "http://webservices.amazon.com/AWSECommerceService/2009-03-31";
private const string ITEM_ID = "0545010225";
public static void Main()
{
SignedRequestHelper helper = new SignedRequestHelper(MY_AWS_ACCESS_KEY_ID, MY_AWS_SECRET_KEY, DESTINATION);
/*
* The helper supports two forms of requests - dictionary form and query string form.
*/
String requestUrl;
String title;
/*
* Here is an ItemLookup example where the request is stored as a dictionary.
*/
IDictionary<string, string> r1 = new Dictionary<string, String>();
r1["Service"] = "AWSECommerceService";
r1["Version"] = "2009-03-31";
r1["Operation"] = "ItemLookup";
r1["ItemId"] = ITEM_ID;
r1["ResponseGroup"] = "Small";
/* Random params for testing */
r1["AnUrl"] = "http://www.amazon.com/books";
r1["AnEmailAddress"] = "foobar@nowhere.com";
r1["AUnicodeString"] = "αβγδεٵٶٷٸٹٺチャーハン叉焼";
r1["Latin1Chars"] = "ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJij";
requestUrl = helper.Sign(r1);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Method 1: ItemLookup Dictionary form.");
System.Console.WriteLine("Title is \"" + title + "\"");
System.Console.WriteLine();
/*
* Here is a CartCreate example where the request is stored as a dictionary.
*/
IDictionary<string, string> r2 = new Dictionary<string, String>();
r2["Service"] = "AWSECommerceService";
r2["Version"] = "2009-03-31";
r2["Operation"] = "CartCreate";
r2["Item.1.OfferListingId"] = "Ho46Hryi78b4j6Qa4HdSDD0Jhan4MILFeRSa9mK+6ZTpeCBiw0mqMjOG7ZsrzvjqUdVqvwVp237ZWaoLqzY11w==";
r2["Item.1.Quantity"] = "1";
requestUrl = helper.Sign(r2);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Method 1: CartCreate Dictionary form.");
System.Console.WriteLine("Cart Item Title is \"" + title + "\"");
System.Console.WriteLine();
/*
* Here is an example where the request is stored as a query-string:
*/
/*
* string requestString = "Service=AWSECommerceService&Version=2009-03-31&Operation=ItemLookup&ResponseGroup=Small&ItemId=" + ITEM_ID;
*/
System.Console.WriteLine("Method 2: Query String form.");
String[] Keywords = new String[] {
"surprise!",
"café",
"black~berry",
"James (Jim) Collins",
"münchen",
"harry potter (paperback)",
"black*berry",
"finger lickin' good",
"!\"#$%'()*+,-./:;<=>?@[\\]^_`{|}~",
"αβγδε",
"ٵٶٷٸٹٺ",
"チャーハン",
"叉焼",
};
foreach (String keyword in Keywords)
{
String requestString = "Service=AWSECommerceService"
+ "&Version=2009-03-31"
+ "&Operation=ItemSearch"
+ "&SearchIndex=Books"
+ "&ResponseGroup=Small"
+ "&Keywords=" + keyword
;
requestUrl = helper.Sign(requestString);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Keyword=\"" + keyword + "\"; Title=\"" + title + "\"");
System.Console.WriteLine();
}
String cartCreateRequestString =
"Service=AWSECommerceService"
+ "&Version=2009-03-31"
+ "&Operation=CartCreate"
+ "&Item.1.OfferListingId=Ho46Hryi78b4j6Qa4HdSDD0Jhan4MILFeRSa9mK%2B6ZTpeCBiw0mqMjOG7ZsrzvjqUdVqvwVp237ZWaoLqzY11w%3D%3D"
+ "&Item.1.Quantity=1"
;
requestUrl = helper.Sign(cartCreateRequestString);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Cart Item Title=\"" + title + "\"");
System.Console.WriteLine();
System.Console.WriteLine("Hit Enter to end");
System.Console.ReadLine();
}
private static string FetchTitle(string url)
{
try
{
WebRequest request = HttpWebRequest.Create(url);
WebResponse response = request.GetResponse();
XmlDocument doc = new XmlDocument();
doc.Load(response.GetResponseStream());
XmlNodeList errorMessageNodes = doc.GetElementsByTagName("Message", NAMESPACE);
if (errorMessageNodes != null && errorMessageNodes.Count > 0)
{
String message = errorMessageNodes.Item(0).InnerText;
return "Error: " + message + " (but signature worked)";
}
XmlNode titleNode = doc.GetElementsByTagName("Title", NAMESPACE).Item(0);
string title = titleNode.InnerText;
return title;
}
catch (Exception e)
{
System.Console.WriteLine("Caught Exception: " + e.Message);
System.Console.WriteLine("Stack Trace: " + e.StackTrace);
}
return null;
}
}
}先测试一把试试,结果返回错误,xml是:
<?xml version="1.0"?>
<ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
<OperationRequest>
<RequestId>79b3b89b-6873-4d95-ba25-b5e782d1f41b</RequestId>
<Arguments>
<Argument Name="Operation" Value="ItemLookup"/>
<Argument Name="Service" Value="AWSECommerceService"/>
<Argument Name="AnUrl" Value="http://www.amazon.com/books"/>
<Argument Name="Signature" Value="oFsMRP3SJqgjejBtPOCJVgNPoi8GRmwvQwFiRUYEbEw="/>
<Argument Name="Latin1Chars" Value="ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJij"/>
<Argument Name="Version" Value="2009-03-31"/>
<Argument Name="ItemId" Value="0545010225"/>
<Argument Name="AWSAccessKeyId" Value="xxx"/>
<Argument Name="AUnicodeString" Value="αβγδεٵٶٷٸٹٺチャーハン叉焼"/>
<Argument Name="Timestamp" Value="2013-06-13T02:23:41Z"/>
<Argument Name="ResponseGroup" Value="Small"/>
<Argument Name="AnEmailAddress" Value="foobar@nowhere.com"/>
</Arguments>
<RequestProcessingTime>0.0029690000000000</RequestProcessingTime>
</OperationRequest>
<Items>
<Request>
<IsValid>False</IsValid>
<ItemLookupRequest>
<IdType>ASIN</IdType>
<ItemId>0545010225</ItemId>
<ResponseGroup>Small</ResponseGroup>
<VariationPage>All</VariationPage>
</ItemLookupRequest>
<Errors>
<Error>
<Code>AWS.MissingParameters</Code>
<Message>Your request is missing required parameters. Required parameters include AssociateTag.</Message>
</Error>
</Errors>
</Request>
</Items>
</ItemLookupResponse>
4.其中参考:
http://docs.aws.amazon.com/AWSECommerceService/latest/DG/CHAP_ResponseGroupsList.html
找到示例代码中的ResponseGroup的Small:
5.剩下的,只有先去自己参考api说明,一点点改改内容,试试能否得到正确的结果了。
结果还是没法完全弄懂。
6.后来发现,貌似此处是纯的C#代码,没有像别人:
amazon product advertising api – item lookup request working example
那样,用到Amazon的库,所以,想起来,Amazon本身对应.NET,是有个库的,可以去试试:
然后去下载:
http://sdk-for-net.amazonwebservices.com/latest/AWSToolsAndSDKForNet.msi
然后再安装:
然后就是,尝试利用AWS SDK For .NET去实现ItemLookup了:
【记录】尝试使用AWS SDK for .NET去实现C#版本的ItemLookup
7.结果还是没搞定,即,
通过AWS的SDK,搞不定;
通过那个AWSECommerceServicePortTypeClient,也搞不定;
8.后来只好再回来,试了试之前的AmazonProductAdvtApiSample,然后用如下代码:
/**********************************************************************************************
* Copyright 2009 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
* except in compliance with the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under the License.
*
* ********************************************************************************************
*
* Amazon Product Advertising API
* Signed Requests Sample Code
*
* API Version: 2009-03-31
*
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
using System.Xml.XPath;
namespace AmazonProductAdvtApi
{
class ItemLookupSample
{
private const string MY_AWS_ACCESS_KEY_ID = "xxx";
private const string MY_AWS_SECRET_KEY = "yyy";
private const string DESTINATION = "ecs.amazonaws.com";
//private const string NAMESPACE = "http://webservices.amazon.com/AWSECommerceService/2009-03-31";
private const string NAMESPACE = "http://webservices.amazon.com/AWSECommerceService/2011-08-01";
//private const string ITEM_ID = "0545010225";
private const string ITEM_ID = "B0083PWAPW";
public static void Main()
{
SignedRequestHelper helper = new SignedRequestHelper(MY_AWS_ACCESS_KEY_ID, MY_AWS_SECRET_KEY, DESTINATION);
/*
* The helper supports two forms of requests - dictionary form and query string form.
*/
String requestUrl;
String title;
/*
* Here is an ItemLookup example where the request is stored as a dictionary.
*/
IDictionary<string, string> r1 = new Dictionary<string, String>();
r1["Service"] = "AWSECommerceService";
//r1["Version"] = "2009-03-31";
r1["Operation"] = "ItemLookup";
r1["ItemId"] = ITEM_ID;
r1["ResponseGroup"] = "Small";
/* Random params for testing */
r1["AnUrl"] = "http://www.amazon.com/books";
r1["AnEmailAddress"] = "foobar@nowhere.com";
r1["AUnicodeString"] = "αβγδεٵٶٷٸٹٺチャーハン叉焼";
r1["Latin1Chars"] = "ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJij";
requestUrl = helper.Sign(r1);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Method 1: ItemLookup Dictionary form.");
System.Console.WriteLine("Title is \"" + title + "\"");
System.Console.WriteLine();
/*
* Here is a CartCreate example where the request is stored as a dictionary.
*/
IDictionary<string, string> r2 = new Dictionary<string, String>();
r2["Service"] = "AWSECommerceService";
//r2["Version"] = "2009-03-31";
r2["Operation"] = "CartCreate";
r2["Item.1.OfferListingId"] = "Ho46Hryi78b4j6Qa4HdSDD0Jhan4MILFeRSa9mK+6ZTpeCBiw0mqMjOG7ZsrzvjqUdVqvwVp237ZWaoLqzY11w==";
r2["Item.1.Quantity"] = "1";
requestUrl = helper.Sign(r2);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Method 1: CartCreate Dictionary form.");
System.Console.WriteLine("Cart Item Title is \"" + title + "\"");
System.Console.WriteLine();
/*
* Here is an example where the request is stored as a query-string:
*/
/*
* string requestString = "Service=AWSECommerceService&Version=2009-03-31&Operation=ItemLookup&ResponseGroup=Small&ItemId=" + ITEM_ID;
*/
System.Console.WriteLine("Method 2: Query String form.");
String[] Keywords = new String[] {
"surprise!",
"café",
"black~berry",
"James (Jim) Collins",
"münchen",
"harry potter (paperback)",
"black*berry",
"finger lickin' good",
"!\"#$%'()*+,-./:;<=>?@[\\]^_`{|}~",
"αβγδε",
"ٵٶٷٸٹٺ",
"チャーハン",
"叉焼",
};
foreach (String keyword in Keywords)
{
String requestString = "Service=AWSECommerceService"
+ "&Version=2009-03-31"
+ "&Operation=ItemSearch"
+ "&SearchIndex=Books"
+ "&ResponseGroup=Small"
+ "&Keywords=" + keyword
;
requestUrl = helper.Sign(requestString);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Keyword=\"" + keyword + "\"; Title=\"" + title + "\"");
System.Console.WriteLine();
}
String cartCreateRequestString =
"Service=AWSECommerceService"
+ "&Version=2009-03-31"
+ "&Operation=CartCreate"
+ "&Item.1.OfferListingId=Ho46Hryi78b4j6Qa4HdSDD0Jhan4MILFeRSa9mK%2B6ZTpeCBiw0mqMjOG7ZsrzvjqUdVqvwVp237ZWaoLqzY11w%3D%3D"
+ "&Item.1.Quantity=1"
;
requestUrl = helper.Sign(cartCreateRequestString);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Cart Item Title=\"" + title + "\"");
System.Console.WriteLine();
System.Console.WriteLine("Hit Enter to end");
System.Console.ReadLine();
}
private static string FetchTitle(string url)
{
try
{
WebRequest request = HttpWebRequest.Create(url);
WebResponse response = request.GetResponse();
XmlDocument doc = new XmlDocument();
doc.Load(response.GetResponseStream());
XmlNodeList errorMessageNodes = doc.GetElementsByTagName("Message", NAMESPACE);
if (errorMessageNodes != null && errorMessageNodes.Count > 0)
{
String message = errorMessageNodes.Item(0).InnerText;
return "Error: " + message + " (but signature worked)";
}
XmlNode titleNode = doc.GetElementsByTagName("Title", NAMESPACE).Item(0);
string title = titleNode.InnerText;
return title;
}
catch (Exception e)
{
System.Console.WriteLine("Caught Exception: " + e.Message);
System.Console.WriteLine("Stack Trace: " + e.StackTrace);
}
return null;
}
}
}返回结果xml是:
<?xml version="1.0"?>
<ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
<OperationRequest>
<RequestId>ce920fcd-a1df-49d6-9ded-770f7168027d</RequestId>
<Arguments>
<Argument Name="Operation" Value="ItemLookup"/>
<Argument Name="Service" Value="AWSECommerceService"/>
<Argument Name="AnUrl" Value="http://www.amazon.com/books"/>
<Argument Name="Signature" Value="PR9IL4SYJEE51RLKsppraFS3OFGLggaXaab8/zSF3sc="/>
<Argument Name="Latin1Chars" Value="ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJij"/>
<Argument Name="ItemId" Value="B0083PWAPW"/>
<Argument Name="AWSAccessKeyId" Value="xxx"/>
<Argument Name="AUnicodeString" Value="αβγδεٵٶٷٸٹٺチャーハン叉焼"/>
<Argument Name="Timestamp" Value="2013-06-13T06:53:07Z"/>
<Argument Name="ResponseGroup" Value="Small"/>
<Argument Name="AnEmailAddress" Value="foobar@nowhere.com"/>
</Arguments>
<RequestProcessingTime>0.0028030000000000</RequestProcessingTime>
</OperationRequest>
<Items>
<Request>
<IsValid>False</IsValid>
<ItemLookupRequest>
<IdType>ASIN</IdType>
<ItemId>B0083PWAPW</ItemId>
<ResponseGroup>Small</ResponseGroup>
<VariationPage>All</VariationPage>
</ItemLookupRequest>
<Errors>
<Error>
<Code>AWS.MissingParameters</Code>
<Message>Your request is missing required parameters. Required parameters include AssociateTag.</Message>
</Error>
</Errors>
</Request>
</Items>
</ItemLookupResponse>说明,有戏,继续折腾那个Associate Tag。
9.然后改为:
(1)ItemLookupSample.cs
/**********************************************************************************************
* Copyright 2009 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
* except in compliance with the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under the License.
*
* ********************************************************************************************
*
* Amazon Product Advertising API
* Signed Requests Sample Code
*
* API Version: 2009-03-31
*
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
using System.Xml.XPath;
namespace AmazonProductAdvtApi
{
class ItemLookupSample
{
private const string MY_AWS_ACCESS_KEY_ID = "xxx";
private const string MY_AWS_SECRET_KEY = "yyy";
private const string MY_AWS_ASSOCIATE_TAG = "zzz";
private const string DESTINATION = "ecs.amazonaws.com";
//private const string NAMESPACE = "http://webservices.amazon.com/AWSECommerceService/2009-03-31";
private const string NAMESPACE = "http://webservices.amazon.com/AWSECommerceService/2011-08-01";
//private const string ITEM_ID = "0545010225";
private const string ITEM_ID = "B0083PWAPW";
public static void Main()
{
SignedRequestHelper helper = new SignedRequestHelper(MY_AWS_ACCESS_KEY_ID, MY_AWS_SECRET_KEY, MY_AWS_ASSOCIATE_TAG, DESTINATION);
/*
* The helper supports two forms of requests - dictionary form and query string form.
*/
String requestUrl;
String title;
/*
* Here is an ItemLookup example where the request is stored as a dictionary.
*/
IDictionary<string, string> r1 = new Dictionary<string, String>();
r1["Service"] = "AWSECommerceService";
//r1["Version"] = "2009-03-31";
r1["Operation"] = "ItemLookup";
r1["ItemId"] = ITEM_ID;
r1["ResponseGroup"] = "Small";
/* Random params for testing */
r1["AnUrl"] = "http://www.amazon.com/books";
r1["AnEmailAddress"] = "foobar@nowhere.com";
r1["AUnicodeString"] = "αβγδεٵٶٷٸٹٺチャーハン叉焼";
r1["Latin1Chars"] = "ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJij";
requestUrl = helper.Sign(r1);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Method 1: ItemLookup Dictionary form.");
System.Console.WriteLine("Title is \"" + title + "\"");
System.Console.WriteLine();
/*
* Here is a CartCreate example where the request is stored as a dictionary.
*/
IDictionary<string, string> r2 = new Dictionary<string, String>();
r2["Service"] = "AWSECommerceService";
//r2["Version"] = "2009-03-31";
r2["Operation"] = "CartCreate";
r2["Item.1.OfferListingId"] = "Ho46Hryi78b4j6Qa4HdSDD0Jhan4MILFeRSa9mK+6ZTpeCBiw0mqMjOG7ZsrzvjqUdVqvwVp237ZWaoLqzY11w==";
r2["Item.1.Quantity"] = "1";
requestUrl = helper.Sign(r2);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Method 1: CartCreate Dictionary form.");
System.Console.WriteLine("Cart Item Title is \"" + title + "\"");
System.Console.WriteLine();
/*
* Here is an example where the request is stored as a query-string:
*/
/*
* string requestString = "Service=AWSECommerceService&Version=2009-03-31&Operation=ItemLookup&ResponseGroup=Small&ItemId=" + ITEM_ID;
*/
System.Console.WriteLine("Method 2: Query String form.");
String[] Keywords = new String[] {
"surprise!",
"café",
"black~berry",
"James (Jim) Collins",
"münchen",
"harry potter (paperback)",
"black*berry",
"finger lickin' good",
"!\"#$%'()*+,-./:;<=>?@[\\]^_`{|}~",
"αβγδε",
"ٵٶٷٸٹٺ",
"チャーハン",
"叉焼",
};
foreach (String keyword in Keywords)
{
String requestString = "Service=AWSECommerceService"
+ "&Version=2009-03-31"
+ "&Operation=ItemSearch"
+ "&SearchIndex=Books"
+ "&ResponseGroup=Small"
+ "&Keywords=" + keyword
;
requestUrl = helper.Sign(requestString);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Keyword=\"" + keyword + "\"; Title=\"" + title + "\"");
System.Console.WriteLine();
}
String cartCreateRequestString =
"Service=AWSECommerceService"
+ "&Version=2009-03-31"
+ "&Operation=CartCreate"
+ "&Item.1.OfferListingId=Ho46Hryi78b4j6Qa4HdSDD0Jhan4MILFeRSa9mK%2B6ZTpeCBiw0mqMjOG7ZsrzvjqUdVqvwVp237ZWaoLqzY11w%3D%3D"
+ "&Item.1.Quantity=1"
;
requestUrl = helper.Sign(cartCreateRequestString);
title = FetchTitle(requestUrl);
System.Console.WriteLine("Cart Item Title=\"" + title + "\"");
System.Console.WriteLine();
System.Console.WriteLine("Hit Enter to end");
System.Console.ReadLine();
}
private static string FetchTitle(string url)
{
try
{
WebRequest request = HttpWebRequest.Create(url);
WebResponse response = request.GetResponse();
XmlDocument doc = new XmlDocument();
doc.Load(response.GetResponseStream());
XmlNodeList errorMessageNodes = doc.GetElementsByTagName("Message", NAMESPACE);
if (errorMessageNodes != null && errorMessageNodes.Count > 0)
{
String message = errorMessageNodes.Item(0).InnerText;
return "Error: " + message + " (but signature worked)";
}
XmlNode titleNode = doc.GetElementsByTagName("Title", NAMESPACE).Item(0);
string title = titleNode.InnerText;
return title;
}
catch (Exception e)
{
System.Console.WriteLine("Caught Exception: " + e.Message);
System.Console.WriteLine("Stack Trace: " + e.StackTrace);
}
return null;
}
}
}(2)SignedRequestHelper.cs
/**********************************************************************************************
* Copyright 2009 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
* except in compliance with the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under the License.
*
* ********************************************************************************************
*
* Amazon Product Advertising API
* Signed Requests Sample Code
*
* API Version: 2009-03-31
*
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Security.Cryptography;
namespace AmazonProductAdvtApi
{
class SignedRequestHelper
{
private string endPoint;
private string akid;
//https://affiliate-program.amazon.co.uk/gp/advertising/api/detail/api-changes.html?ie=UTF8&pf_rd_t=501&ref_=amb_link_83388313_2&pf_rd_m=A3P5ROKL5A1OLE&pf_rd_p=&pf_rd_s=assoc-center-1&pf_rd_r=&pf_rd_i=assoc-api-detail-2-v2
//must add this Associate Tag
private string associateTag;
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 SignedRequestHelper(string awsAccessKeyId, string awsSecretKey, string awsAssociateTag, string destination)
{
this.endPoint = destination.ToLower();
this.akid = awsAccessKeyId;
this.associateTag = awsAssociateTag;
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 naturual 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["AssociateTag"] = this.associateTag;
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(sigBytes);
// now construct the complete URL and return to caller.
StringBuilder qsBuilder = 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;
}
/*
* Consttuct the canonical query string from the sorted parameter 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;
}
}
/*
* To help the SortedDictionary order the name-value pairs in the correct way.
*/
class ParamComparer : IComparer<string>
{
public int Compare(string p1, string p2)
{
return string.CompareOrdinal(p1, p2);
}
}
}就可以获得返回结果了:
<?xml version="1.0"?>
<ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
<OperationRequest>
<RequestId>5a3d38fc-d6bf-4a91-92c1-4e87df69a612</RequestId>
<Arguments>
<Argument Name="Operation" Value="ItemLookup"/>
<Argument Name="Service" Value="AWSECommerceService"/>
<Argument Name="Latin1Chars" Value="ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJij"/>
<Argument Name="AssociateTag" Value="xxx"/>
<Argument Name="AUnicodeString" Value="αβγδεٵٶٷٸٹٺチャーハン叉焼"/>
<Argument Name="AnUrl" Value="http://www.amazon.com/books"/>
<Argument Name="Signature" Value="nqx7diOZknsj+gedZY7CuGkDnFtFPV+Sq3J1wkQy9pE="/>
<Argument Name="ItemId" Value="B0083PWAPW"/>
<Argument Name="AWSAccessKeyId" Value="xxx"/>
<Argument Name="Timestamp" Value="2013-06-13T07:02:50Z"/>
<Argument Name="ResponseGroup" Value="Small"/>
<Argument Name="AnEmailAddress" Value="foobar@nowhere.com"/>
</Arguments>
<RequestProcessingTime>0.0085650000000000</RequestProcessingTime>
</OperationRequest>
<Items>
<Request>
<IsValid>True</IsValid>
<ItemLookupRequest>
<IdType>ASIN</IdType>
<ItemId>B0083PWAPW</ItemId>
<ResponseGroup>Small</ResponseGroup>
<VariationPage>All</VariationPage>
</ItemLookupRequest>
</Request>
<Item>
<ASIN>B0083PWAPW</ASIN>
<ParentASIN>B008GGCAVM</ParentASIN>
<DetailPageURL>http://www.amazon.com/Kindle-Fire-HD/dp/B0083PWAPW%3FSubscriptionId%3Dxxx%26tag%3Dxxx%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB0083PWAPW</DetailPageURL>
<ItemLinks>
<ItemLink>
<Description>Technical Details</Description>
<URL>http://www.amazon.com/Kindle-Fire-HD/dp/tech-data/B0083PWAPW%3FSubscriptionId%3Dxxx%26tag%3Dxxx%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0083PWAPW</URL>
</ItemLink>
<ItemLink>
<Description>Add To Baby Registry</Description>
<URL>http://www.amazon.com/gp/registry/baby/add-item.html%3Fasin.0%3DB0083PWAPW%26SubscriptionId%3Dxxx%26tag%3Dxxx%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0083PWAPW</URL>
</ItemLink>
<ItemLink>
<Description>Add To Wedding Registry</Description>
<URL>http://www.amazon.com/gp/registry/wedding/add-item.html%3Fasin.0%3DB0083PWAPW%26SubscriptionId%3Dxxx%26tag%3Dxxx%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0083PWAPW</URL>
</ItemLink>
<ItemLink>
<Description>Add To Wishlist</Description>
<URL>http://www.amazon.com/gp/registry/wishlist/add-item.html%3Fasin.0%3DB0083PWAPW%26SubscriptionId%3Dxxx%26tag%3Dxxx%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0083PWAPW</URL>
</ItemLink>
<ItemLink>
<Description>Tell A Friend</Description>
<URL>http://www.amazon.com/gp/pdp/taf/B0083PWAPW%3FSubscriptionId%3Dxxx%26tag%3Dxxx%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0083PWAPW</URL>
</ItemLink>
<ItemLink>
<Description>All Customer Reviews</Description>
<URL>http://www.amazon.com/review/product/B0083PWAPW%3FSubscriptionId%3Dxxx%26tag%3Dxxx%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0083PWAPW</URL>
</ItemLink>
<ItemLink>
<Description>All Offers</Description>
<URL>http://www.amazon.com/gp/offer-listing/B0083PWAPW%3FSubscriptionId%3Dxxx%26tag%3Dxxx%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB0083PWAPW</URL>
</ItemLink>
</ItemLinks>
<ItemAttributes>
<Manufacturer>Amazon Digital Services, Inc</Manufacturer>
<ProductGroup>Amazon Devices</ProductGroup>
<Title>Kindle Fire HD 7", Dolby Audio, Dual-Band Wi-Fi, 16 GB - Includes Special Offers</Title>
</ItemAttributes>
</Item>
</Items>
</ItemLookupResponse>其中,cmd中打印出来的,第一个title就是对应的:
Kindle Fire HD 7", Dolby Audio, Dual-Band Wi-Fi, 16 GB – Includes Special Offers
【总结】
至此,算是至少让整个流程跑通了。接下来,就是如何分析返回的xml数据。
以及去增强整个AWS的函数和功能了。
后续折腾,参见:
转载请注明:在路上 » 【基本解决】用C#实现AWS的API中的ItemLookup