
Durante un’attività di consulenza per un cliente, che ha commissionato lo sviluppo di un e-commerce in ambito tecnologico, ci siamo trovati ad affrontare la scarsità di informazioni tecniche e commerciali messe a disposizione dal fornitore dei prodotti, per cui, in fase di presentazione, avremmo avuto un sito spoglio e schede prodotto piuttosto limitate.
Una delle soluzioni migliori trovate per aggiungere o completare le informazioni sui prodotti è stato l’utilizzo delle Amazon Product Advertising API (ex Amazon Web Service AWS), una serie di servizi che Amazon mette a disposizione per la vendita dei propri prodotti su altri store.
Dopo una veloce e gratuita registrazione come Amazon Associate, possiamo iscriverci al programma di Amazon Product Advertising API, ottenendo le credenziali per l’utilizzo dei servizi.
Consultando la documentazione, è possibile vedere che sono disponibili i seguenti servizi:
- ItemSearch: ricerca prodotti su Amazon;
- BrowseNodeLookup: restituisce la gerarchia delle categorie a partire da un prodotto/categoria;
- ItemLookup: restituisce una lista di attributi per l’oggetto specificato;
- SimilarityLookup: restituisce gli oggetti simili all’oggetto specificato.
Inoltre, nell’invocazione di questi, è possibile aggiungere ulteriori parametri che si vogliono ottenere nella risposta. Ad esempio, tra i parametri disponibili ci sono: Accessories (oggetti acquistati insieme); EditoralReview (descrizione del prodotto); Images (set delle immagini in varie dimensioni); ItemAttributes (caratteristiche peculiari); OfferListing (elenco delle offerte dei vari venditori); RelatedItems (oggetti collegati); Reviews (recensioni).
Tra tutti i servizi, ItemSearch risulta quello più completo a soddisfare le nostre esigenze e, probabilmente, anche quello che ci consente di fare meno invocazioni, dal momento che riceve in input l’EAN (codice a barre di 13 cifre) e restituisce le informazioni di cui abbiamo necessità, come nella response di esempio di seguito:
<Item>
<ASIN>B00TZL96BY</ASIN>
<ParentASIN>B012E5CKMY</ParentASIN>
<DetailPageURL>https://www.amazon.com/Lacoste-Womens-Sleeve-Stretch-Varsity/dp/B00TZL96BY%3Fpsc%3D1%26SubscriptionId%3D[AWS Access Key ID]%26tag%3D[Associate ID]%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB00TZL96BY</DetailPageURL>
<ItemLinks>
<ItemLink>
<Description>Technical Details</Description>
<URL>https://www.amazon.com/Lacoste-Womens-Sleeve-Stretch-Varsity/dp/tech-data/B00TZL96BY%3FSubscriptionId%3D[AWS Access Key ID]%26tag%3D[Associate ID]%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB00TZL96BY</URL>
</ItemLink>
<ItemLink>
<Description>Add To Baby Registry</Description>
<URL>https://www.amazon.com/gp/registry/baby/add-item.html%3Fasin.0%3DB00TZL96BY%26SubscriptionId%3D[AWS Access Key ID]%26tag%3D[Associate ID]%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB00TZL96BY</URL>
</ItemLink>
<ItemLink>
<Description>Add To Wedding Registry</Description>
<URL>https://www.amazon.com/gp/registry/wedding/add-item.html%3Fasin.0%3DB00TZL96BY%26SubscriptionId%3D[AWS Access Key ID]%26tag%3D[Associate ID]%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB00TZL96BY</URL>
</ItemLink>
...
</ItemLinks>
<ItemAttributes>
<Manufacturer>Lacoste Womens Apparel</Manufacturer>
<ProductGroup>Apparel</ProductGroup>
<Title>Lacoste Women's Long Sleeve Stretch Pique Slim Fit Polo Shirt, Varsity Blue, 40</Title>
</ItemAttributes>
</Item>
Il limite di utilizzo gratuito del servizio di è di 25.000 chiamate per ora, per cui risulta particolarmente utile anche nel caso di grossi e-commerce.
Prima di utilizzare i servizi, naturalmente, è necessario autenticarsi, utilizzando il client id e il secret ricevuto in fase di registrazione.
Per l’implementazione in ambiente .NET abbiamo utilizzato una classe Java trovata tra gli esempi forniti da Amazon, SignedRequestsHelper, e l’abbiamo riscritta in C#. Questa classe ci fornisce l’accesso e l’utilizzo dei servizi di Amazon.
La nostra scelta, anche per ovviare ai limiti di utilizzo del servizio e gestire al meglio gli errori, è stata di procedere secondo due step differenti:
- Salvare le informazioni dei prodotti su un DB di Swap;
- Allineare i prodotti Amazon con quelli dell’e-commerce.
Così facendo, nella seconda fase potremmo decidere cosa importare, definire le similarità tra i prodotti, ed eventualmente ripristinare informazioni modificate per errore.
Implementiamo quindi un servizio di importazione, AmazonSwapImportManager, in cui, nel costruttore, istanziamo un oggetto SignedRequestHelper passandogli i parametri ottenuti in fase di registrazione:
SignedRequestHelper helper;
public AmazonSwapImportManager() {
try {
helper = new SignedRequestHelper(ACCESS_KEY_ID, SECRET_KEY, ENDPOINT);
} catch (Exception e) {
//logga l’eccezione;
}
}
Per ottenere le informazioni relative ai prodotti, invece, creiamo un metodo GetInfoFromAmazonServices:
private String GetInfoFromAmazonServices(String ean) {
try {
//ottieni la url del prodotto richiesto tramite il suo EAN
String requestUrl = GetProductUrlRequestByEan(ean);
//ottieni l'oggetto XML dalla url del prodotto
String xmlObject = GetXmlObjectFromUrl(requestUrl).Result;
//trasforma in JSON l'oggetto XML ricevuto precedentemente.
return GetJsonFromXml(xmlObject);
} catch (Exception e) {
//logga l’eccezione;
}
}
che si basa su altri metodi di utility:
private static String GetJsonFromXml(string xmlObject) {
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlObject);
var node = doc.DocumentElement.GetElementsByTagName("Item");
if (node.Item(0) == null) return "none";
string json = JsonConvert.SerializeXmlNode(node.Item(0));
return json;
}
private String GetProductUrlRequestByEan(String ean) {
IDictionary <string, string> requestParams = new Dictionary <string, String> ();
requestParams.Add("Service", "AWSECommerceService");
requestParams.Add("Operation", "ItemSearch");
requestParams.Add("AWSAccessKeyId", ACCESS_KEY);
requestParams.Add("AssociateTag", TAG);
requestParams.Add("Keywords", EAN);
requestParams.Add("IdType", "EAN");
requestParams.Add("ResponseGroup", "Accessories,AlternateVersions,BrowseNodes,EditorialReview,Images,ItemAttributes,ItemIds,OfferFull,OfferListings,Offers,OfferSummary,PromotionSummary,RelatedItems,Reviews,SalesRank,Similarities");
requestParams.Add("SearchIndex", "All");
requestParams.Add("RelationshipType", "AuthorityTitle");
return helper.Sign(requestParams);
}
private async Task <String> GetXmlObjectFromUrl(String requestUrl) {
return await requestUrl.GetStringAsync();
}
Il metodo GetInfoFromAmazonServices viene utilizzato all’interno di un metodo ImportProductInfo, che cicla su tutti i prodotti dello store e tenta di ottenere le informazioni di cui necessitiamo:
private void ImportProductsInfo(IList <Product> products) {
/* altre operazioni di utilità */
//cicla sui prodotti
foreach(var product in products) {
String productInfo = GetInfoFromAmazonServices(product.Ean);
//salva su un DB di swap le informazioni trovate sul prodotto
StoreItemToAmazonSwapDB(product.Ean, productInfo);
}
/* altre operazioni di riepilogo */
}
L’oggetto salvato sul DB di Swap è di tipo SwapItem, così definito:
public class SwapItem
{
public int Id { get; set; }
public string Ean { get; set; }
public string Content { get; set; }
}
Una volta salvati i dati relativi ai prodotti su un DB di swap, procediamo all’allineamento col nostro DB di produzione. Creiamo una classe AmazonSwapSyncer, che contiene il metodo Sync, così definito:
public void Sync()
{
SynchronizeProductsInfo(GetItemsFromAmazonSwapDB());
}
che, a sua volta, si basa su SynchronizeProductsInfo:
private void SynchronizeProductsInfo(IList <SwapItem> swapItems) {
/* altre operazioni di utilità */
foreach(var swapItem in swapItems) {
//ricerca sul nostro DB se ci sono occorrenze del prodotto con l'EAN specificato
var currentProduct = dbContext.Products.FirstOrDefault(p => p.Ean == swapItem.Ean);
if (currentProduct != null) {
try {
//deserializza la stringa in un oggetto AmazonItem
var amazonItem = JsonConvert.DeserializeObject <AmazonItem> (swapItem.Content);
//ottiene la descrizione
currentProduct.Description = amazonItem.Item.EditorialReviews.EditorialReview.Content;
//ottiene le caratteristiche techiche del prodotto
currentProduct.Features = String.Join(" * ", amazonItem.Item.ItemAttributes.Feature);
//ottiene la url dell'immagine di grandi dimensioni
currentProduct.ImageUrl = amazonItem.Item.LargeImage.Url;
/* ottieni altre informazioni */
} catch (Exception e) {
//logga l’eccezione;
}
}
}
//salva le informazioni ottenute
dbContext.SubmitChanges();
}
La classe AmazonItem, con le varie classi aggregate, è così definita a seconda delle nostre necessità:
public class AmazonItem
{
public Item Item { get; set; }
}
public class Item
{
public String ASIN { get; set; }
public long SalesRank { get; set; }
public ItemAttributes ItemAttributes { get; set; }
public EditorialReviews EditorialReviews { get; set; }
public AmazonImage LargeImage { get; set; }
}
public class ItemAttributes
{
public String Binding { get; set; }
public String Color { get; set; }
public String[] Feature { get; set; }
}
public class EditorialReviews
{
public EditorialReview EditorialReview { get; set; }
}
public class EditorialReview
{
public String Content { get; set; }
}
public class AmazonImage
{
public String Url { get; set; }
public int Height { get; set; }
public int Width { get; set; }
}
Per cui, alla fine del ciclo di sincronizzazione, abbiamo informazioni dei prodotti aggiornate grazie ai contenuti disponibili su Amazon.
Il codice citato in questo articolo è disponibile su GitHub al seguente link.