HOWTO: Google-Ajax-Suche mit CSE

with tags Softwareschrott Technik -

Neulich erst habe ich mich aufgeregt dass man das Google-Iframe für die CSE-Suche so mies anpassen kann. Der Grund dafür ist natürlich dass Google nie vor hatte dies zu ermöglichen. Für derartige Ansprüche gibt es die sehr praktische AJAX-API oder alternativ das REST-Interface zu dieser API. Ich habe mich also mal wieder völlig unsinnig hingesetzt, mehrere Seiten Obfuscated-Code formatiert und gelesen, nur um 1-2 Details wie die Breite des Frames doch noch anzupassen.

Doch wie nutzt man diese API wirklich effektiv? Die ersten paar Beispiele erzeugen zwar eine nette AJAX-Suchfunktion, aber wirklich an die eigenen Bedürfnisse ist sie ja nicht angepasst. Besser ist da schon das RawSearchControl-Beispiel, an dem ich mich orientiert habe.
Es versucht viel eher die Daten selbst zu beschaffen um dann die Elemente per Hand zu zeichnen.

Meine Realisation sieht dann so aus:

Als erstes basteln wir uns eine HTML-Seite die folgende Tags enthällt:

    <script type="text/javascript" src="gajax.js" />
<div id="searchform">Bitte kurz warten!</div>
<div id="results"></div>
<div id="cursor"></div>

Wo die Tags stehen und wie sie genau bezeichnet sind ist natürlich egal, man sollte nur daran denken diese dann auch in gajax.js einzutragen

Wenn wir das haben, brauchen wir natürlich noch die Datei mit dem Javascript-Quellcode. Diese nennen wir mal gajax.js (ist ja auch schon oben referenziert) und werfen sie ins selbe Verzeichnis.
Inhalt sieht dann etwa so aus:

    /*Wir hätten gerne eine Suche
ohne bereits eingebundenes Google-CSS
die Sprache von Textschnipseln sollte Deutsch sein*/
google.load("search", "1", {"nocss" : true, "language" : "de_DE"});

// Diese Funktion wird gerufen wenn die Google-Scripte fertig geladen sind
function initialize() {
new RawSearchControl();
}

//Hier arrangieren wir das automatische laden von initialize()
google.setOnLoadCallback(initialize);

//hier erledigen wir die grundsätzlichen Einstellungen, z.B. wo soll was angezeigt werden
function RawSearchControl()
{
//hier werden die Ergebnisse eingeblendet
this.results = document.getElementById("results");
//Hier wird die Steuerfunktion zum Blättern eingeblendet
this.cursorElement = document.getElementById("cursor");
//Position für das Suchformlar
this.searchform = document.getElementById("searchform");
//wir wollen eine Websuche, keine Videosuche oder gar Geo-Suche
this.searcher = siteSearch = new google.search.WebSearch();
//wir beschränken die Suche auf unsere CSE
this.searcher.setSiteRestriction("CSESchlüsselIDhier:Eingeben");
//HTML für die Ergebnisse basteln wir selbst
this.searcher.setNoHtmlGeneration();
//diese Funktion wird ausgeführt wenn die Suche beendet ist
this.searcher.setSearchCompleteCallback(this,RawSearchControl.prototype.searchComplete);
//wir haben platz für mehrere Ergebnisse gleichzeitig
this.searcher.setResultSetSize(google.search.Search.LARGE_RESULTSET);
//wir erzeugen die Eingabe und Absendeelemente
this.searchForm = new google.search.SearchForm(true, this.searchform);
//beim Abschicken soll ohne reload onSubmit ausgeführt werden
this.searchForm.setOnSubmitCallback(this, RawSearchControl.prototype.onSubmit);
}

RawSearchControl.prototype.onSubmit = function(form) {
if (form.input.value)
{
//Es wurde Submit ausgeführt, zeit den searcher mit den Daten zu füttern
this.searcher.execute(form.input.value);
}
//Wir wollen doch die Daten nicht noch zusätzlich abschicken oder?
return false;
}

RawSearchControl.prototype.searchComplete = function()
{
//alte Ergebnisse werden erstmal gelöscht
this.clearResults();

//Mal sehen was wir an neuen Ergebnissen haben...
if (this.searcher.results && this.searcher.results.length > 0)
{
for (var i=0; i<this.searcher.results.length; i++)
{
var result = this.searcher.results[i];
var html = generateSingleResultHTML(result);
this.results.appendChild(html);
}
//nun noch schnell die Blätter-Funktion zeichnen...
RawSearchControl.prototype.drawCursorControl(this.searcher);
}
else
{ //Nichts gefunden
var div = createDiv("Leider wurde nichts entsprechendes gefunden",'error');
this.results.appendChild(div);
}
}

function generateSingleResultHTML(result)
{
/*
wir haben folgende Attribute von result zur Verfügung und sollen daraus eine HTML-Struktur bauen:
.unescapedUrl - die reine, blanke URL... ideal für einen Link
.titleNoFormatting - Die Überschrifft ohne <b>s, <i>s und ähnliche Formatierung
.content - Ein kurzes Textschnipsel aus dem Seiteninhalt
.visibleUrl - die Adresse ohne http:// oder genauem Pfad
.title - Der Titel 1:1 übernommen
.URL - die URL, mit http:// aber ohne Argumente, kann man auch anzeigen wenn man will
*/
}

//
RawSearchControl.prototype.drawCursorControl = function(searcher)
{
/*wir wollen das Steuerfeld zur Seitenauswahl ebenfalls selbst basteln
Sinnvolle Variablen zur Verwendung sind hier:
searcher.cursor.currentPageIndex - aktuelle SeitenID
searcher.cursor.pages - Array mit Seiten

jedem Element sollte via methodClosure ein Verweis auf searcher.gotoPage mit
der angesprungenen Seitennummer als Argument angehängt werden*/
}
}

//Aufräumarbeiten
RawSearchControl.prototype.clearResults = function()
{
removeChildren(this.results);
removeChildren(this.cursorElement);
removeChildren(this.pageinfo);
}

/**
* Hier kommen noch einige allgemeine Funktionen um die DOM-Struktur umzubauen
*/
function removeChildren(parent)
{
while (parent.firstChild)
{
parent.removeChild(parent.firstChild);
}
}

function methodClosure(object, method, opt_argArray)
{
return function()
{
return method.apply(object, opt_argArray);
}
}
function createDiv(opt_text, opt_className)
{
var el = document.createElement("div");
if (opt_text)
{
el.innerHTML = opt_text;
}
if (opt_className)
{
el.className = opt_className;
}
return el;
}
function createP(opt_className, opt_text)
{/*...Inhalt wie createDiv...*/}
function createLink(href, opt_text, opt_target, opt_className, opt_divwrap)
{/*...Inhalt wie createDiv...*/}
function createSpan(opt_text, opt_className)
{/*...Inhalt wie createDiv...*/}
//viele Weitere Elemente folgen...


Das war zwar jetzt etwas länger aber eigentlich überhaupt nicht schwierig oder? Als Ergebnis erhällt man eine nette kleine Suchfunktion bei der man zwar viel von Hand bastelt, dafür aber auch bei so ziemlich allem bestimmen kann wie es aussehen soll.
Die CSS-Anpassungen (wir haben Google ja gesagt es soll sein eigenes CSS nicht mitliefern) überlass ich dann mal euerer Fantasie.

Beim nächsten Mal gibts dann vlt. eine Realisierung direkt in PHP, das scheint mir mindestens genauso einfach zu sein, benötigt natürlich kein Java unso aber habe ich aber mal wieder erst entdeckt als die Hälfte des Codes schon fertig war. Wenn ich am WE mal wieder zu viel Freizeit habe, könnte es sogar sein dass ich doch mal wieder an meinem eigentlich schon verworfenen Sacred-Chao(IRCBot)-Quählcode rumschraube und dem Ding nach all den Jahren doch noch eine funktionierende !google-Funktion einbau.

Geschrieben von Dr. Azrael Tod
Later article
Intarwebs at last!