
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.
Scritto da

Enrico Bencivenga