Skript na tvorbu Sklik produktových kampaní z feedu

Když nedávno kluci ze Skliku na PPC Campu oznámili, že produktové inzeráty (PI) se budou posouvat do top pozic, rozhodl jsem se na to připravit. Zkusil jsem tedy zrevidovat kampaně dle návodu v dokumentaci. Trápil jsem se s importy dva dny, ale pořád to nebylo ono.

Naštěstí se ozvala v diskusi Hanka Kobzová a poradila mi svůj skript na import produktových kampaní. Byl to krok vpřed, ale pořád mi přišlo, že je tam moc manuální práce. Tak jsem Hančin skript se Standovou pomocí, trošku upravil. Výsledek nyní předkládám.

Co tedy skript umí

  • na základě pravidel vytvoří z feedu importní soubor pro PI
  • dokáže kombinovat tagy z feedu custom_label_0, custom_label_1, categorytext, manufacturer, brand
  • dokáže přidat do produktových skupin jen produkty skladem
  • dokáže přidat do produktových skupin cenové rozsahy
  • výsledkem je importní soubor s kampaní, která obsahuje sestavy s produktovými skupinami, dle zadaných kritérií

Nastavení

V konfigurační části je třeba nastavit tyto věci

feed_url – url vašeho feedu pro Zbozi.cz

mail – e-mail na který vám přijde výsledný importní soubor

campaignName – jméno vaší kampaně, já si držím v názvu podmínky, dle kterých skládám produktové skupiny, pro případný update tedy například: PLA | Kategorie/Výrobce/Sklad/Cena.

campaignBudget – rozpočet vaší kampaně, v rozhraní lze později změnit

shopID – shopID nejlépe zjistíte tak, že dáte v sekci Export kampaní vyexportovat stávající PI kampaň a hodnotu shopID tam najdete, vypadá nějak takto: zbozi:12345780:JMENOESHOPU.cz.

defaultCPC – výchozí hodnota ceny za proklik, pokud budete provádět update kampaně, doporučuji nechat hodnotu prázdnou, systém pak sám doplní u nově přidaných sestav CPC 8 Kč a vy si filtrem CPC=8 Kč a Zobrazení=0 snadno dohledáte nově doimportované sestavy.

Dále je pak třeba nastavit filtr, dle kterého skript na základě vašich podmínek vytvoří názvy sestav a produktové skupiny. Stačí u položek od řádku 19-26 nastavit hodnotu TRUE, na tagy z feedu, které chcete použít. Podmínkou je, že musí být splněny všechny podmínky TRUE, pokud je jedna prázdná, tak se sestava nevytvoří.

Na řádku 26 pak můžete v poli priceRange nastavit cenové rozsahy, dle kterých se sestavy dále segmentují. (POZOR! Pokud to s rozsahy přeženete, script se zpomalí a může přesáhhnout limit 30 minut a spadnout. Stačí generování dle rozsahů rozdělit do více souborů s méně rozsahy a vše se zrychlí.)

Výsledný soubor pak stáhněte z e-mailu, ideálně do něj vůbec nezasahujte a dejte importovat do Skliku. Dobře si zkontrolujte zda se import podařil a vše sedí!

Pozor také na práci s tagem CATEGORYTEXT. Pokud vám správce zbožáků změní jméno CATEGORYTEXT kvůli párování na Zbozi.cz, rozhodí vám tím kompletně sestavy a produktové skupiny. Vyřešil jsem to tím, že si originální hodnotu CATEGORYTEXT z webu zasílám do štítku. Obdoba google_product_category a product_type se prý neplánuje.



//Url config spreadsheet----------------------------------------------------------------------------------
//********************************************************************************************************
var feed_url = "FEEDURL"; // URL feedu pro Zbozi.cz
/*********************************************************************************************************
Skript:                                          Sklik PI kampaně z feedu
Verze:                                           26.3. 2018
Poskládal:                                       Karel Rujzl [rujzl.cz]
Vydatně pomáhal:                                 Standa Jílek [standajilek.cz]
Prvotní myšlenka a základ scriptu:				 Hana Kobzová [hanakobzova.cz]
/********************************************************************************************************/
var mail			= "email@email.cz"; //email kam přijde CSV s importem
// konfigurace kampaní
var campaignName 	= "1 | PLA | KategorieLabel/Výrobce/Sklad/Cena"; // název kampaně
var campaignBudget 	= "300"; // rozpočet kampaně
var shopID 			= "zbozi:123456:eshop.cz"; //ID shopu ze Zbozi.cz
var defaultCPC 		= "1"; // pokud provádíte update, nechte prázdné, import přidá k novým sestavám CPC 8 Kč

// konfigurace produktových skupin (sčítá se)
var readLabel0 = false; // filtruj dle labelu 0 
var readLabel1 = true; // filtruj dle labelu 0 
var readCategorytext = false; // filtruj dle kategorie 
var readManufacturer = true; // filtruj dle výrobce 
var readBrand = false; // filtruj dle brandu 
var inStock = true; // filtruje jen zboží skladem
var enPrRange = true; // segmentace sestav dle ceny produktů, v následujícím řádku nadefinuj rozsahy
var priceRange = ["0-249","250-499","500-999","1000-1999","2000-2999","3000-999999"];

/********************************************************************************************************/
//KONEC RUČNÍHO CONFIGU DÁL NEŠAHAT
/********************************************************************************************************/


//vars
var feed_split 		= "";
var csv="";
var newData = [];

function main() {
    try {
      
        // nácuc a extrakce dat z feedu
        var xml = (UrlFetchApp.fetch(feed_url).getContentText()).split(feed_split);
        var data = [];
		var sestava =[];
        for (var i = 0; i < (xml.length - 1); i++)
        {
          
          
          //slozit produtk skupiny a adgroups dle podmínek
          
         
          // název sestavy
          var entities = [];
          var rCategorytext="";
          var rManufacturer="";
          var rBrand="";
          var makeName="ok";
          
          // sjednotit načtení promných z feedu a ošetřit prázdné hotnoty manufacturer a další
          if (readCategorytext==true) {
           rCategorytext = parse(xml[i], "", "");
           if (rCategorytext=="") {
             		makeName="no";} 
            	else {
                	rCategorytext = removeCdata (rCategorytext);
                }
          }
          
          if (readManufacturer==true) {
           rManufacturer = parse(xml[i], "", "");
           if (rManufacturer=="") {
             		makeName="no";} 
            	else {
                	rManufacturer = removeCdata (rManufacturer);
                }
          }
          
          if (readBrand==true) {
           rBrand = parse(xml[i], "", "");
           if (rBrand=="") {
             		makeName="no";} 
            	else {
                	rBrand = removeCdata (rBrand);
                }
          }
          
          if (readLabel0==true) {
           rLabel0 = parse(xml[i], "", "");
           if (rLabel0=="") {
             		makeName="no";} 
            	else {
                	rLabel0 = removeCdata (rLabel0);
                }
          }
          
          if (readLabel1==true) {
           rLabel1 = parse(xml[i], "", "");
            
          
            if (rLabel1=="") {
             		makeName="no";} 
            	else {
                	rLabel1 = removeCdata (rLabel1);
                }
          }
                 
          
          if(makeName=="ok"){
          
          //pokud jsou dostupné všechny názvy udělej sestavu a produktovou skupinu
          
          if (readLabel0==true) entities.push(rLabel0);
          if (readLabel1==true) entities.push(rLabel1);  
          if (readCategorytext==true) entities.push(rCategorytext);
          if (readManufacturer==true) entities.push(rManufacturer);
          if (readBrand==true) entities.push(rBrand);
          
            
          
          // název sestavy rozseká oddělovačem
          var adgroupName = entities.join(" | ");
          
          //Logger.log(entities);
          
          // definice produktové skupiny
          var prodGroup="*";
          
          if (readLabel0==true) prodGroup += " / Custom label 0='" + rLabel0 + "'";
          if (readLabel1==true) prodGroup += " / Custom label 1='" + rLabel1 + "'";    
          if (readCategorytext==true) prodGroup += " / Product type='" + rCategorytext + "'";
          if (readManufacturer==true) prodGroup += " / Manufacturer='" + rManufacturer + "'";
          if (readBrand==true) prodGroup += " / Brand='" + rBrand + "'";
          if (inStock==true) prodGroup += " /Availability='in_stock'";
          
           
          // přidá do pole řádek adgroupy a produktové skupiny
          sestava.push({adgroup: adgroupName, productgroup: prodGroup}); 
          }	
        }

 } catch(err) {
   Logger.log(err);
 }
   Logger.log("Neunikatnich sestav: " + sestava.length);
 
  //slozit sestavu a produktovou skupinu 
  
  
var flags = [], output = [], l = sestava.length, i;
for( iii=0; iii

//Url config spreadsheet----------------------------------------------------------------------------------
//********************************************************************************************************
var feed_url = "FEEDURL"; // URL feedu pro Zbozi.cz
/*********************************************************************************************************
Skript:                                          Sklik PI kampaně z feedu
Verze:                                           26.3. 2018
Poskládal:                                       Karel Rujzl [rujzl.cz]
Vydatně pomáhal:                                 Standa Jílek [standajilek.cz]
Prvotní myšlenka a základ scriptu:				 Hana Kobzová [hanakobzova.cz]
/********************************************************************************************************/
var mail			= "email@email.cz"; //email kam přijde CSV s importem
// konfigurace kampaní
var campaignName 	= "1 | PLA | KategorieLabel/Výrobce/Sklad/Cena"; // název kampaně
var campaignBudget 	= "300"; // rozpočet kampaně
var shopID 			= "zbozi:123456:eshop.cz"; //ID shopu ze Zbozi.cz
var defaultCPC 		= "1"; // pokud provádíte update, nechte prázdné, import přidá k novým sestavám CPC 8 Kč

// konfigurace produktových skupin (sčítá se)
var readLabel0 = false; // filtruj dle labelu 0 <CUSTOM_LABEL_0>
var readLabel1 = true; // filtruj dle labelu 0 <CUSTOM_LABEL_1>
var readCategorytext = false; // filtruj dle kategorie <CATEGORYTEXT>
var readManufacturer = true; // filtruj dle výrobce <MANUFACTURER>
var readBrand = false; // filtruj dle brandu <BRAND>
var inStock = true; // filtruje jen zboží skladem
var enPrRange = true; // segmentace sestav dle ceny produktů, v následujícím řádku nadefinuj rozsahy
var priceRange = ["0-249","250-499","500-999","1000-1999","2000-2999","3000-999999"];

/********************************************************************************************************/
//KONEC RUČNÍHO CONFIGU DÁL NEŠAHAT
/********************************************************************************************************/


//vars
var feed_split 		= "</SHOPITEM>";
var csv="";
var newData = [];

function main() {
    try {
      
        // nácuc a extrakce dat z feedu
        var xml = (UrlFetchApp.fetch(feed_url).getContentText()).split(feed_split);
        var data = [];
		var sestava =[];
        for (var i = 0; i < (xml.length - 1); i++)
        {
          
          
          //slozit produtk skupiny a adgroups dle podmínek
          
         
          // název sestavy
          var entities = [];
          var rCategorytext="";
          var rManufacturer="";
          var rBrand="";
          var makeName="ok";
          
          // sjednotit načtení promných z feedu a ošetřit prázdné hotnoty manufacturer a další
          if (readCategorytext==true) {
           rCategorytext = parse(xml[i], "<CATEGORYTEXT>", "</CATEGORYTEXT>");
           if (rCategorytext=="") {
             		makeName="no";} 
            	else {
                	rCategorytext = removeCdata (rCategorytext);
                }
          }
          
          if (readManufacturer==true) {
           rManufacturer = parse(xml[i], "<MANUFACTURER>", "</MANUFACTURER>");
           if (rManufacturer=="") {
             		makeName="no";} 
            	else {
                	rManufacturer = removeCdata (rManufacturer);
                }
          }
          
          if (readBrand==true) {
           rBrand = parse(xml[i], "<BRAND>", "</BRAND>");
           if (rBrand=="") {
             		makeName="no";} 
            	else {
                	rBrand = removeCdata (rBrand);
                }
          }
          
          if (readLabel0==true) {
           rLabel0 = parse(xml[i], "<CUSTOM_LABEL_0>", "</CUSTOM_LABEL_0>");
           if (rLabel0=="") {
             		makeName="no";} 
            	else {
                	rLabel0 = removeCdata (rLabel0);
                }
          }
          
          if (readLabel1==true) {
           rLabel1 = parse(xml[i], "<CUSTOM_LABEL_1>", "</CUSTOM_LABEL_1>");
            
          
            if (rLabel1=="") {
             		makeName="no";} 
            	else {
                	rLabel1 = removeCdata (rLabel1);
                }
          }
                 
          
          if(makeName=="ok"){
          
          //pokud jsou dostupné všechny názvy udělej sestavu a produktovou skupinu
          
          if (readLabel0==true) entities.push(rLabel0);
          if (readLabel1==true) entities.push(rLabel1);  
          if (readCategorytext==true) entities.push(rCategorytext);
          if (readManufacturer==true) entities.push(rManufacturer);
          if (readBrand==true) entities.push(rBrand);
          
            
          
          // název sestavy rozseká oddělovačem
          var adgroupName = entities.join(" | ");
          
          //Logger.log(entities);
          
          // definice produktové skupiny
          var prodGroup="*";
          
          if (readLabel0==true) prodGroup += " / Custom label 0='" + rLabel0 + "'";
          if (readLabel1==true) prodGroup += " / Custom label 1='" + rLabel1 + "'";    
          if (readCategorytext==true) prodGroup += " / Product type='" + rCategorytext + "'";
          if (readManufacturer==true) prodGroup += " / Manufacturer='" + rManufacturer + "'";
          if (readBrand==true) prodGroup += " / Brand='" + rBrand + "'";
          if (inStock==true) prodGroup += " /Availability='in_stock'";
          
           
          // přidá do pole řádek adgroupy a produktové skupiny
          sestava.push({adgroup: adgroupName, productgroup: prodGroup}); 
          }	
        }

 } catch(err) {
   Logger.log(err);
 }
   Logger.log("Neunikatnich sestav: " + sestava.length);
 
  //slozit sestavu a produktovou skupinu 
  
  
var flags = [], output = [], l = sestava.length, i;
for( iii=0; iii<l; iii++) {
    if( flags[sestava[iii].adgroup]) continue;
    flags[sestava[iii].adgroup] = true;
    
  	      // pokud je zapnuto přidání cenových rozsahů
  		  if (enPrRange == true) {
            
            for (ii = 0; ii < priceRange.length; ii++) {
            
            output.push({adgroup: sestava[iii].adgroup + " | " + priceRange[ii], productgroup: sestava[iii].productgroup + " / Price=["+priceRange[ii] + "]"});
            
            }  
            
          } else {
          // bez cenových rozsahů 
            output.push({adgroup: sestava[iii].adgroup, productgroup:sestava[iii].productgroup});
          }
            
	  	}
  
  Logger.log("Unikatnich sestav: " + output.length);
  
  
 // slozit hlavicku import souboru
  
  var tableHeader = ["Campaign", "Campaign type", "Campaign Daily Budget", "Networks", "Campaign Status", "Ad Group", "Max CPC", "Ad Group Status", "Status", "Product Group", "Product Set Label", "PLA shop"];
  var campaignLine = [campaignName, "Shopping", campaignBudget, "Search;Google Search;Search Partners","active"];	
  
  csv += '"' + tableHeader.join('"\t"') + '"\r\n';
  csv += '"' + campaignLine.join('"\t"') + '"\r\n';
  

 //	 zalozit sestavy a produktové skupiny
  

for(key in output) {
    if(output.hasOwnProperty(key)) {
        var value = output[key].productgroup;
        //vytvori produktovou skupinu
      
      
      	//Logger.log (output[key].adgroup.length);
      	
       //zkratit název reklamní skupiny
		var trimmedString = output[key].adgroup.substring(0, 50);

      	var groupLine = [campaignName, "", "", "", "", output[key].adgroup, "", "", "active", output[key].productgroup, trimmedString, shopID];
      	//vytvori sestavu
      	var adgroupLine = [campaignName, "", "", "", "", output[key].adgroup, defaultCPC, "active", "", "", "", ""];
    
    	csv += '"' + groupLine.join('"\t"') + '"\r\n';
      	csv += '"' + adgroupLine.join('"\t"') + '"\r\n';
    }
}
  
// poslat csv mailem	
  
  var blob = Utilities.newBlob(csv, "application/octet-stream", "SklikImportPI.csv").setDataFromString(csv, "UTF-16");

  MailApp.sendEmail({to: mail, subject: "Import PI Sklik CSV", attachments: blob});
  
}

//-------------------------------------------------------------------------------------------
function parse(text, from, to)
{
    var step_one = text.substr(text.indexOf(from) + from.length);
    return (step_one.substr(0, step_one.indexOf(to))).trim();
}
function removeCdata(text){
 	var text = text.replace("<![CDATA[", ""); 
 	var text = text.replace("]]>", ""); 
  	return text;
 }

  1. 1. 10. 2019 - Odpovědět

    Ahoj, parádní script a díky, jen bych se chtěl zeptat, s kolika produkty eshopu s timto scriptem pracuješ a jaké to má výhody oproti struktuře jedna sestava = jeden produkt?

    • 7. 10. 2019 - Odpovědět

      @Kuba

      Mám feedy kde jsou desetitisíce produktů.

      Dělit to co sestava to produkt mi přijde už hodně silná segmentace, zvláště pokud se ti produkty překrývají pak se ti budou chytat obecná slova na více sestav což je horší na správu. Dávám přednost rozumné segmentaci na větší skupinky kategorie/značka/cena

  2. 3. 10. 2019 - Odpovědět

    Ahoj, mohl bych se zeptat, jak naložit s takovýmito chybovými hláškami? Díky!

    Exception: Request failed for https://www.xyz.cz returned code 403. Truncated server response: 403 Forbidden (use muteHttpExceptions option to examine full response)

    TypeError: Cannot read property „length“ from undefined. (file Code.gs, line 137)

    • 7. 10. 2019 - Odpovědět

      @Martin

      Neodkážu říct přesně ale dle té chyby bych si tipl že bude problém s feedem. Buď je špatná URL nebo neexistuje.

  3. 14. 9. 2020 - Odpovědět

    Ahoj,
    super skript. Díky za něj. Jak třeba řešíš to, když máš větší počet podkategorií, že tě třeba zajímá úroveň 2 rozdělená dle cenových hladin.

    Např. máš kategorii Kancelářské potřeby | Obaly a balení | Bublinkové a chtěl bys jen Kancelářské potřeby | Obaly a balení rozdělenou dle cenových hladin.

    Jde to nějak v nastavení takhle s tím pracovat?

    Moc díky.

    Honza

    • 14. 9. 2020 - Odpovědět

      @Jan Eder

      Ahoj, teoreticky si to můžeš rozhodit jak potřebuješ, stačí si poslat do šítku hodnoty dle toho jak potřebuješ, něco asi půjde v Mergadu a něco možná bude spíš na programátora, ale udělat to jde.

      • 14. 10. 2020 - Odpovědět

        @Karel Rujzl

        Jasne, super. Rozumím. Diky moc. Jdu to nastavit v Mergadu.

        H.

  4. 9. 11. 2020 - Odpovědět

    Skvelá práca, ďakujem! Viem, že skript je už staršieho data, ale stále super. V kombinácii so „smartibiddingom“ skrz automatické pravidlá mňam!

  5. 15. 1. 2021 - Odpovědět

    Zdravím Karle, prosím vás, chcem sa len rýchlo spýtať, pri aktualizácii importu s nejakou periodicitou potom pri importe volíte aktualizačný režim, alebo přepsat? Predpokladám, že aktualizačný, aby sa zachoval bidding, ale chcel som si to pre istotu potvrdiť. Ďakujem.

    • 6. 5. 2021 - Odpovědět

      @Lubo Kružliak

      Přesně tak aktualizační režim a ve scriptu nechávám proměnnou defaultCPC=““

  6. 24. 2. 2021 - Odpovědět

    Ahoj,
    díky za script. Chci se zeptat – řešili jste u něj nějakou automatizaci nahrávání do Skliku? Třeba tak, aby nebylo třeba při každé větší změně v sortimentu spouštět skript a soubor ručně nahrávat do systému, ale vše se dělo plně automaticky, třeba na týdenní, nebo měsíční bázi?
    Děkuji za odpověď!

    Jakub

    • 6. 5. 2021 - Odpovědět

      @Jakub

      Bohužel neřešili, cca jednou za měsíc vygentuji nový import soubor a nahraji update do Skliku. Z toho důvodu mám pro každý projekt tento script sólo a je to pak na jeden klik.

Comment
Name
Email