blexin

Consulenza
Sviluppo e
Formazione IT


Blog

Evolvi la tua azienda

Vediamo insieme come integrare le schede prodotti del nostro e-commerce con le informazioni provenienti da Amazon.

Quando Amazon ti fa il servizio...

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:

  1. Salvare le informazioni dei prodotti su un DB di Swap;
  2. 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.

Servizi

Evolvi la tua azienda