Inserire il motore di ricerca Google CSE nel proprio sito

Guida tecnica all'installazione e configurazione del motore di ricerca Google in un sito web

Da quando Google ha interrotto la vendita di Google Site Search, il 1° aprile 2017, molti utenti si sono trovati a dover fronteggiare una situazione poco chiara: la documentazione fornita dall'azienda di Mountain View, infatti, è stata - ed è tutt'ora - ben poca. Il primo Aprile 2018 GSS è stato completamente dismesso.

Indice dei contenuti

Allo stesso tempo stanno spingendo la diffusione del loro Custom Search Engine, che è molto simile a GSS, solo che è gratuito ed inserisce annunci pubblicitari tra i risultati di ricerca.

Ho dovuto anche io avere a che fare con questo caso più volte, ho scambiato varie email con il supporto di Google senza mai avere una risposta definitiva, ho cercato soluzioni alternative e già pronte all'uso, ma non ho trovato nulla. È chiaro che se un utente è abituato ad utilizzare il motore di Google nel suo sito, non è facile proporgli qualcosa di completamente diverso. Contemporaneamente, se non hai già qualcosa di pronto creato da te, devi investire molte ore (quindi molti soldi) per realizzare un tuo motore di ricerca, che in ogni caso non potrà mai competere con la potenza di Google, purtroppo, a meno che tu abbia una realtà solida alle spalle che è intenzionata a spendere migliaia di euro.

Insomma, se abbiamo decine di clienti il cui sito web utilizza/utilizzava Google Site Search... Come risolvere la cosa?

Andiamo per punti, in modo ordinato:

Cosa succede da quando Google ha dismesso il suo Site Search?

Risposta semplice: nulla. In realtà l'azienda americana informa via mail tutti gli utilizzatori del prodotto che il servizio termina, convertendosi però in CSE gratuito. La differenza principale per chi deve implementare il motore all'interno di un sito internet, sta nel codice da utilizzare. Infatti se utilizziamo un codice recente come questo, che utilizza il Custom Search Element 2.0, non dovremmo avere grossi problemi. Se invece stiamo sfruttando dei codici completamente diversi, o troppo vecchi? Allora:

Come implementare il nuovo motore di ricerca CSE gratuito?

Il Custom Search Engine di Google è la versione gratuita (e ora l'unica versione) del motore di ricerca che si può utilizzare nel proprio sito. All'interno dei risultati di ricerca possono apparire degli annunci pubblicitari. Supponiamo che un utente cerchi nel sito "libri" o "lampade", tra i risultati potrebbe vedere dei "libri" o delle "lampade" di una determinata azienda (magari concorrente), che sta pagando affinchè i suoi annunci appaiano nella rete Display di Google AdWords.

Per implementarlo, basta creare un nuovo motore (se non ne abbiamo già uno) nell'apposito sito. Dopodichè inseriamo l'indirizzo del sito in cui cercare, tipo "www.example.com" e scegliamo una lingua e un nome da assegnare. Ora possiamo già generare il codice per l'implementazione, copiarlo ed inserirlo come indicato da Google: quindi all'interno del tag HEAD della pagina.

Chiaramente, se stavamo già utilizzando CSE, possiamo limitarci ad aggiornare il codice generato, se vediamo che è diverso. Per chi invece utilizzava versioni particolari di GSS con codice completamente diverso, dovrà solo capire se vuole la versione gratuita ad annunci o la versione a pagamento senza pubblicità. Nel secondo caso, continua a leggere di seguito.

Complimenti, il tuo motore di ricerca è pronto! Ora devi verificare se il motore precedente aveva delle impostazioni particolari, che dovrai riportare anche qui.

Come implementare la versione a pagamento e senza annunci pubblicitari di CSE?

Qui entriamo più nel tecnico. Vi spiegherò la scelta che ho fatto io: non ho voluto utilizzare i sistemi indicati da Google, come la Custom Search Element Control API 2.0 o le librerie già pronte. Non mi sembrava che la documentazione fosse sufficientemente chiara. Ho optato invece per l'alternativa delle JSON/Atom API partendo però da zero. Nelle mail che ho scambiato con il supporto Google, quel poco che ho potuto capire è che l'unico modo per non avere gli annunci pubblicitari era di utilizzare la versione a pagamento, solo che nessuno ti dava una semplice implementazione. Invece di passare ore a capire come utilizzare le librerie, che poi magari non facevano comunque quello che volevo, ho voluto creare io una soluzione ad-hoc. Mi sembrava impossibile che la stessa Google, dismettendo GSS, non ti fornisse qualcosa di già pronto se volevi utilizzare CSE pagando le query... Eppure ancora adesso è cosi! Credo infatti che l'intento dell'azienda sia ben preciso, e alla fine di questo punto spiegherò brevemente la mia opinione.

Passaggi da seguire:

  1. Creare o modificare il proprio motore di ricerca. Prendere il codice identificativo del motore. (leggi anche il punto 2 qui sopra)
  2. Registrarsi a Google Cloud e impostare un metodo di pagamento (leggi il punto 4 per saperne di più sui costi)
  3. Creare un progetto nel Cloud Google
  4. Aggiungere al progetto creato l'API Custom Search dalla libreria disponibile
  5. Creare una chiave API (api key) per Custom Search. Eventualmente imporre delle restrizioni. Questa chiave ci servirà per implementare il nostro motore.
  6. Utilizzare il codice identificativo (ID cx del motore) e la chiave API nel proprio script.

Nel dettaglio:

  1. Avremo bisogno del codice identificativo del motore di ricerca da implementare, che si chiama tecnicamente "cx"...
  2. ... e della chiave API creata nel Cloud, che si definisce come "key".
  3. Possiamo fare dei test con l'API Explorer o nella pagina delle reference dei parametri
  4. La singola query è definita come paramentro "q".

Codice di esempio:

Di seguito riporto un esempio che ho realizzato in PHP.

Visualizza il codice su PasteBin

In pratica, sto solamente inviando una richiesta AJAX all'URL indicato nella documentazione Google, quindi ricevo ed elaboro il risultato in PHP e costruisco il mio motore di ricerca, decidendo separatamente se voglio mostrare anche i risultati "promossi" o solo quelli normali. I risultati promossi possono essere impostati dal pannello di CSE.

<?php
# @author: Christian Marongiu
# @version: v1.1.3

class SearchEngine {
 public $cx=''; # CSE Engine ID
 public $apikey=''; # CSE API Key
 public $start=0; # CSE parameter to define which page to start from
 public $q=''; # Search query
 public $num=10; # Results per page
 public $url='';
 public $json='';
 public $results=[]; # Normal results
 public $promo=[]; # Promoted results
 public $multilang=FALSE;
 public $data='';
 public $selpage=1; # Selected results page
 public $message=''; # String of message to present the user about the success of his query
 public $strings=[]; # Array of translated strings
 public $totalResults=0; # Total of normal search results
 public $submit_name='send'; # Input submit name
 public $reqInfo=''; # Object containing general data about the request: data->queries
 public $searchInfo=''; # Object with more info about the research, like search time and total results;
 public $lang='it'; # Engine default language

 function __construct($cx,$apikey,$lang='it',$multilang=FALSE,$q='',$submit='send',$start=-1,$num=10) {
  if($cx) $this->cx=$cx;
  if($apikey) $this->apikey=$apikey;
  if((int)$start>=0) $this->start=$start; else $this->start=(isSet($_GET['start'])?(int)$_GET['start']:0);
  if($this->start<0) $this->start=0; elseif($this->start>90) $this->start=90; # Start maximum at 90 (10th page)
  if($q!='') $this->q=trim($q); elseif($_GET['q']) $this->q=trim($_GET['q']); # Avoid XSS Injection
  if((int)$num>=0) $this->num=$num; else $this->num=10;
  if($this->num>50) $this->num=50; # Max 50 results per page
  if($multilang) $this->multilang=TRUE;
  if($lang!='') $this->lang=$lang;
  if($this->q && $this->q!='') { # User sent a query
   $this->url='https://www.googleapis.com/customsearch/v1?q='.urlencode($this->q).'&num='.$this->num.'&start='.($this->start?$this->start:1).'&cx='.$this->cx.'&key='.$this->apikey;
   $this->selpage=(int)floor($this->start/10)+1;
   $this->json=file_get_contents($this->url);
   if($this->json) $this->data=json_decode($this->json); # JSON search results provided by Google Cloud Search API
   if($this->data->queries) $this->reqInfo=$this->data->queries;
   if($this->data->searchInformation) $this->searchInfo=$this->data->searchInformation;
   if($this->data->items) $this->results=$this->data->items;
   if($this->data->promotions) $this->promo=$this->data->promotions;
   if((int)$this->searchInfo->totalResults>0) $this->totalResults=(int)$this->searchInfo->totalResults;
   if(isSet($this->promo) && count($this->promo)>0) $this->totalResults+=count($this->promo); # Adding number of promo results to number of normal results
   if($submit) $this->submit_name=$submit; else $this->submit_name='send';
  }
 }

 public function translate($id=0,$lang='') {
  if($lang=='' && $this->lang!='') $lang=$this->lang;  # Set language if custom language is set by Class
  $strings=[
   1=>['it'=>'Nessun termine di ricerca inserito','en'=>'No search term entered','es'=>'No se ha introducido ningún término de búsqueda','de'=>'Keinen Suchbegriff eingegeben','fr'=>'Aucun terme de recherche saisi','ru'=>'Ничего не найдено по Вашему запросу'],
   2=>['it'=>'Altri risultati','en'=>'Other results','es'=>'Otros resultados','de'=>'Weitere Ergebnisse','fr'=>'Autres résultats','ru'=>'Другие результаты поиска'],
   3=>['it'=>'Risultati trovati per','en'=>'Results found for'],
   4=>['it'=>'risultato','en'=>'result'],
   5=>['it'=>'risultati','en'=>'results'],
   6=>['it'=>'Nessun risultato','en'=>'No results','es'=>'Ningún resultado','de'=>'Kein Ergebnis','fr'=>'Aucun résultat','ru'=>'Ничего не найдено'],
   7=>['it'=>'Trova nel sito','en'=>'Search website','es'=>'Encontrar en la web','de'=>'Auf der Website suchen','fr'=>'Trouver dans le site','ru'=>'Найти на сайте'],
   8=>['it'=>'Cerca','en'=>'Search','es'=>'Buscar','de'=>'Suchen','fr'=>'Rechercher','ru'=>'Поиск']
  ];
  if($id>0 && isSet($strings[$id][$lang])) return $strings[$id][$lang];
  else return '';
 }

 public function render_promoResults($class='result result-promo') {
  $pprint=''; $ptot=0;
  if(isSet($this->promo) && count($this->promo)>0) { # If there are promo results I build html
  $ptot=count($this->promo); # Total number of promo results
   foreach($this->promo as $pNum=>$pData) {
    if(($pfoto=$pData->pagemap->cse_image[0]->src)!='') $pimg='<div class="rimg"><img src="'.$pfoto.'" /></div>'."\n";
    else $pimg='';
    $ptitle='<h3 class="rtitle">'.$pData->title."</h3>\n";
    $plink='<a class="link" href="'.$pData->link.'">'.$pData->title."</a>\n";
    $pwww='<a class="www" href="'.$pData->link.'">'.$pData->link."</a>\n";
    if($pData->bodyLines[0]->title) $psnippet='<p class="rsnippet">'.$pData->bodyLines[0]->title."</p>\n"; # Promoted result description is not mandatory
    else $psnippet='';
    $pprint.='<div class="'.$class.'">'.$pimg.'<div class="rtext">'.$plink.$psnippet.$pwww."</div>\n</div>\n";
   }
  }
  return $pprint;
 }

 public function render_normalResults($class='result') {
  $rprint='';
  if(isSet($this->results)&&$this->totalResults>0) { # If there are normal results (not promoted)
   foreach($this->results as $resNum=>$resData) {
    if(($rphoto=$resData->pagemap->cse_image[0]->src)!='') $rimg='<div class="rimg"><img src="'.$rphoto.'" /></div>'."\n";
    else $rimg='';
    $rtitle='<h3 class="rtitle">'.$resData->title."</h3>\n";
    $rlink='<a class="link" href="'.$resData->link.'">'.$resData->title."</a>\n";
    $rwww='<a class="www" href="'.$resData->link.'">'.$resData->link."</a>\n";
    $rsnippet='<p class="rsnippet">'.$resData->htmlSnippet."</p>\n";
    $rprint.='<div class="'.$class.'">'.$rimg.'<div class="rtext">'.$rlink.$rsnippet.$rwww."</div>\n</div>\n";
   }
  }
  return $rprint;
 }

 public function render_pager() {
  $pages='';
  if($this->totalResults>10) $pagenum=(int)ceil($this->totalResults/10); else $pagenum=1;
  if($pagenum>10) $pagenum=10; # Maximum 10 pages
  if($pagenum>1) { # No need for pager with only 1 page
   $pages='<div class="search-results-pager">'."\n";
   $pages.='<p>'.$this->translate(2).":</p>\n";
   for($p=1;$p<=$pagenum;$p++)
    $pages.='<a href="'.$_SERVER['PHP_SELF'].'?q='.htmlspecialchars($this->q).'&amp;start='.($p==1?'':$p-1).'0&amp;send=Cerca"'.(($p==$this->selpage)?' class="selpage"':'').'>'.$p."</a>\n";
   $pages.="</div>\n";
  }
  return $pages;
 }

 public function render_form($id='form-search',$action='',$method='get') {
  if($action=='') $action=$_SERVER['SCRIPT_NAME'];
  $form='';
  $form.='<form id="'.$id.'" action="'.$action.'" method="'.$method.'">'."\n";
  $form.='<input type="text" name="q" placeholder="'.$this->translate(7).'"'.($this->q?' value="'.htmlspecialchars($this->q).'"':'').' />'."\n";
  $form.='<input type="submit" name="'.$this->submit_name.'" value="'.$this->translate(8).'" />'."\n";
  $form.="</form>\n";
  return $form;
 }

 public function initEngine() { return $this->html; } # If you want to use this instead of __toString, and then you print it with: $cse->initEngine();

 function __toString() { # Eventually change it with public function initEngine()
  if(isSet($_REQUEST[$this->submit_name])) { # Form correctly submitted
   $this->html.='<div class="search-results">'."\n";
   if($this->q && $this->q!='') { # User sent a query
    if($this->totalResults>0) {
     $this->html.='<h2>'.$this->translate(3).': <em>'.htmlspecialchars($this->q).'</em></h2>';
     $this->html.='<p class="rnum">'.($this->totalResults==1?'1 '.$this->translate(4):$this->totalResults.' '.$this->translate(5))."</p>\n";
    } else $this->html.='<p class="rnum">'.$this->translate(6)."</p>\n";
   } else $this->html.='<p class="rnum">'.$this->translate(1)."</p>\n"; # No search term specified
   $this->html.=$this->render_promoResults();
   $this->html.=$this->render_normalResults();
   $this->html.=$this->render_pager();
   $this->html.="</div>\n";
   return $this->html;
  }
  return '';
 }
}

#$cx="xxx";
#$apikey="yyy";
#$cse=new SearchEngine($cx,$apikey);
# To print form and results: echo $cse->render_form(); and echo $cse;
?>

 

Un esempio di HTML di default per l'implementazione: Chiaramente il codice del motore viene generato in PHP come visto qui sopra.

<!DOCTYPE html>
<html>
<head>
 <link rel="stylesheet" href="cse.css" />
</head>
<body>
<?php require_once('cse.php') ?>
<article class="custom-search-engine">
<h1>CSE Search Engine</h1>
<?php
echo $cse->render_form();
echo $cse;
?>
</article>
</body>
</html>

 

E lo stile CSS basato sul colore rosso: Questo è liberamente modificabile, ma lo metto a disposizione come punto di partenza.

.custom-search-engine { background-color:#FFF; }
 #form-search { width:100%; }
 #form-search input { max-width:100%; display:inline-block; }
 #form-search input[type="text"] { border:1px solid #000; width:75%; height:42px; color:#000; padding:5px; font-size:15px; }
 #form-search input[type="submit"] { border:1px solid #000; background-color:#C12724; color:#FFF; font-weight:bold; height:42px; padding:5px; font-size:15px; margin-left:-5px; width:25%; }
 .result:before,.result:after { display:block; clear:both; content:""; }
 .result { border-top:1px dotted #CCC; margin:10px 0; padding:10px 0; }
 .result a { display:block; width:100%; margin-bottom:5px; font-weight:normal; color:#C12724; }
 .result a.www { font-size:12px; display:block; text-overflow:ellipsis; overflow:hidden; white-space:nowrap; }
 .result img { width:auto; max-width:100%; border:1px solid #000; }
 .rsnippet { font-weight:normal; margin:0; }
 .rsnippet br { display:none; }
 .rnum { padding-bottom:10px; }
 .rtext { width:100%; display:block; }
 .rimg,.rimg+.rtext { display:inline-block; vertical-align:top; }
 .rimg { width:10%; margin-right:10px; }
 .rimg+.rtext { width:calc(85% - 10px); }
 .result:first-child { border-top:none; margin-top:0; }
 .search-results-pager { border-top:1px solid #000; width:100%; padding:10px 0; }
 .search-results-pager a { display:inline-block; margin:5px 3px; font-size:14px; font-weight:normal; color:#FFF; padding:10px; background-color:#C12724; border:1px solid #000; transition:background-color .5s,color .5s; text-decoration:none; }
 .search-results-pager a:hover { background-color:#FFF; color:#000; transition:background-color .5s,color .5s; text-decoration:none; }
 .search-results-pager .selpage { color:#000; background-color:#FFF; border:1px solid #C12724; font-weight:bold; }

 

La mia opinione sulle scelte di Google:

Utilizzando CSE gratuito gli utenti che eseguono le ricerche possono vedere degli annunci inseriti a pagamento. Molto spesso chi acquistava una licenza di Google Site Search pagava per un tot. di query in blocco. Una query è ogni singola ricerca fatta dall'utente. Capitava spesso di acquistare 150.000, o 500.000 query, pagate in anticipo, e poi alla fine ne consumavi meno della metà. Giustamente ora Google, attraverso l'uso delle API, farà pagare le query "a consumo", quindi in base a quello che viene realmente utilizzato. Per maggiori dettagli su costi e pagamenti leggi il prossimo capitolo. In questo modo, a mio avviso, molti utenti inizieranno ad utilizzare il CSE gratuito al posto del GSS, quindi si amplierà il numero di persone che vedranno annunci pubblicitari.
Chi invece non vuole gli annunci di altre aziende nel suo sito web, come molti enti della pubblica amministrazione, oppure come molte imprese private, pagherà tranquillamente per il servizio perchè è comunque un qualcosa in più che viene offerto agli utenti finali. Secondo me questo permetterà da un lato di risparmiare per chi deve implementare il motore, e dall'altro di guadagnare per i publisher, advertiser, e quindi per la stessa Google.

Quanto costa il CSE a pagamento?

Il costo è quello del consumo delle query. Ricordando che una query è ogni singola richiesta effettuata tramite il motore di ricerca, il costo effettivo stabilito da Google è di 5$ ogni 1000 query. Le prime 100 richieste di ogni giorno sono gratuite, quindi se in media il sito utilizza 120 query al giorno, ne pagheremo solo 20.

Google addebiterà tramite il Cloud una fattura nei primi giorni di ogni mese. Se ci siamo registrati per la prima volta al Cloud Google, avremo un bonus di 300€ da spendere nelle varie API, quindi finchè non consumiamo questo bonus la nostra carta di credito (o conto bancario) non sarà addebitata.

Supponiamo quindi di prevedere per un sito una media di 25.000 ricerche annue. Il costo in preventivo sarà di 125€ (25.000*5/1000). Considerate comunque che il costo reale, essendo a consumo, sarà calcolato su quello che veramente viene utilizzato dagli utenti. Inoltre, se il motore riceverà meno di 100 richieste al giorno, alla fine non pagheremo nulla, proprio perchè le prime 100 ricerche al giorno sono a costo zero.

Consulta i prezzi sul sito del Custom Search Engine.

 

Congratulazioni per aver implementato il motore di ricerca CSE nel tuo sito!

Spero che questo articolo ti sia stato utile, in ogni caso lasciami un commento e, se ti va, condividilo sui tuoi social! Buon lavoro.

Lascia un commento

*

*

*

Nessun commento presente. Sii il primo ad iniziare la discussione!