Dieses Problem kam mir in den Sinn, als ich an Verbesserungen für die Voyp Context Assistant Funktion arbeitete. Das Ziel war es, dass der Assistent Informationen über Unternehmen in einem bestimmten Gebiet abruft und deren Details zusammenstellt. Die wichtigsten Details für diesen Anwendungsfall wären der Name, die Adresse und die Telefonnummer.
Um diese Informationen abzurufen, fällt dies in die Kategorie SKU Ortsdetails (Erweitert), die 0,020 USD pro Anfrage kostet (20,00 USD pro 1000). Ich weiß nicht, wie es Ihnen geht, aber für mich macht diese Preisgestaltung die Nutzung der Places API etwas außerhalb meiner Möglichkeiten.
Also habe ich etwas nach Alternativen zur Google Places API recherchiert, und es gibt einige kostenlose und günstigere Optionen als Google Places, aber nicht ohne einen Haken. Die meisten – wenn nicht sogar alle – sind günstiger als Google, zum Beispiel die TomTom Search API, die 0,5 USD pro 1000 kostet, 40x günstiger als Google. Mapbox berechnet die ersten 100.000 Anfragen gar nichts, danach werden 0,75 USD pro 1000 Anfragen fällig, was immer noch erheblich günstiger als Google ist. Die Overpass API von OpenStreetMap ist sogar kostenlos, allerdings mit einigen Einschränkungen.
Das Problem bei diesen (und anderen) Lösungen ist, dass die Qualität der Ausgabe nicht einmal annähernd an die Google Places API heranreicht. Sie alle haben viel weniger Datensätze, besonders in weniger besiedelten Gebieten, und viele Details fehlen, insbesondere die Telefonnummer, die für Voyp das wichtigste Datum ist. Google muss das natürlich wissen und nutzt dies aus, indem sie diese absurden Preise verlangen.
Wie können wir also dennoch die Qualität der Google Places Ausgabe erhalten, aber nur einen Bruchteil des Preises bezahlen? Die Antwort lautet SERP (Search Engine Results Page).
Eine Suchergebnisseite (englisch Search Engine Results Page, SERP) ist eine Webseite, die von einer Suchmaschine als Antwort auf eine Suchanfrage eines Benutzers angezeigt wird. Die Hauptkomponente einer SERP ist die Auflistung der Ergebnisse, die von der Suchmaschine als Reaktion auf eine Schlüsselwortanfrage zurückgegeben werden.
Ist es legal?
Google-Suchergebnisse gelten als öffentlich zugängliche Daten, so dass das Scrapen erlaubt ist. Es gibt jedoch einige Arten von Daten, die Sie nicht scrapen dürfen (z.B. personenbezogene Daten, urheberrechtlich geschützte Inhalte). Es ist daher am besten, wenn Sie vorab einen Rechtsberater konsultieren.
Zu viele Anfragen in kurzer Zeit können jedoch dazu führen, dass Ihre IP gesperrt wird, also seien Sie vorsichtig.
Damit das geklärt ist, kommen wir zurück zum praktischen Aspekt der Umsetzung. Es gibt zwei Möglichkeiten, diesen Ansatz zu verfolgen: 1) Nutzung einer SERP-API oder 2) Eigenimplementierung.
SERP-APIs
Der Ansatz mit einer SERP-API ist natürlich der einfachste. Sie bezahlen einen gewissen Betrag pro Anfrage, ähnlich wie bei der Google Places API, aber zu einem vernünftigeren Preis. DataForSEO scheint nach einer kurzen Recherche der kostengünstigste Pay-As-You-Go-Plan zu sein. Die meisten SERP-APIs erfordern ein Abonnement oder haben etwas teurere PAYG-Preise. DataForSEO berechnet 0,002 USD pro Anfrage, das ist bereits 10x günstiger als die Google Places API, und das allein ist schon eine enorme Verbesserung. Aber natürlich können Sie noch einen Schritt weitergehen und den anderen Ansatz wählen.
Do It Yourself
Die gesamte Arbeit selbst zu erledigen, ist natürlich viel schwieriger, aber auch am lohnenswertesten. Google macht es Ihnen sehr schwer, und zu versuchen, aus einer sehr kryptischen und ausführlichen HTML-Ergebnisseite Sinn zu machen, kann Ihre Augen rollen lassen und Sie zum Weinen bringen. Anstatt also zu versuchen, alle Ergebnisdaten zu verstehen und zu parsen, dachte ich, dass dies ein großartiger Anwendungsfall für den Einsatz von KI wäre, um die ganze schwere Arbeit zu erledigen.
Für diese Übung werde ich Gemini verwenden, insbesondere das Gemini 1.5 Flash-Modell, da es eines der günstigsten Out-of-the-Box-Modelle ist: 0,35 USD pro Million Token.
Wir werden nach „italienischen Restaurants in New York, USA“ suchen, und so würde eine typische Suchergebnisseite aussehen:
Google SuchergebnisseiteDer erste Schritt ist also, die Suchergebnisse mit NodeJs zu laden:
const axios = require("axios");
const {GoogleGenerativeAI} = require("@google/generative-ai");
let query = "italian restaurants in New York, US"
let searchUrl = `https://www.google.com/search?hl=en&q=${encodeURIComponent(query)}`;
let response = await axios.get(searchUrl)
// Komplette HTML-Seite
let serp = response.data;
const genAI = new GoogleGenerativeAI(apiKey);
const model = genAI.getGenerativeModel({
model: "gemini-1.5-flash",
systemInstruction: "Analysiere die HTML-Daten von Google SERP Ortsergebnissen und extrahiere die Liste der Orte im JSON-Format {name, adresse, bewertung, preis}",
});
const chatSession = model.startChat({generationConfig: {
temperature: 1,
responseMimeType: "application/json", // JSON-Format
}});
const result = await chatSession.sendMessage(serp);
console.log(result.response.text());
An diesem Punkt haben wir bereits das HTML-Ergebnis, aber die Ergebnisseite ist recht umfangreich, normalerweise zwischen 150k und 200k, das entspricht etwa 100k Token laut Google’s AI Studio. Das bringt uns auf etwa 0,035 USD pro Anfrage, was über dem Preis der Google Places API liegt.
Der nächste Schritt ist, die HTML-Seite zu kürzen und unnötige Daten zu entfernen. Dafür verwenden wir die recht praktische cheerio-Bibliothek
const axios = require("axios");
const cheerio = require('cheerio');
const {GoogleGenerativeAI} = require("@google/generative-ai");
let query = "italian restaurants in New York, US"
let searchUrl = `https://www.google.com/search?hl=en&q=${encodeURIComponent(query)}`;
let response = await axios.get(searchUrl)
// Lade komplette Seite
const $ = cheerio.load(response.data);
// Entferne unnötige Tags
$("head,header,script,style,footer,img,svg").remove();
// Extrahiere gekürzte HTML-Seite
let serp = $.html()
const genAI = new GoogleGenerativeAI(apiKey);
const model = genAI.getGenerativeModel({
model: "gemini-1.5-flash",
systemInstruction: "Analysiere die HTML-Daten von Google SERP Ortsergebnissen und extrahiere die Liste der Orte im JSON-Format {name, adresse, bewertung, preis}",
});
const chatSession = model.startChat({generationConfig: {
temperature: 1,
responseMimeType: "application/json", // JSON-Format
}});
const result = await chatSession.sendMessage(serp);
console.log(result.response.text());
Dies reduziert die Größe der HTML-Daten erheblich und nun liegen wir bei ca. 20.000 Token. Das sind etwa 0,007 USD pro Anfrage, also sparen wir schon etwas Geld, aber die SERP-AI-Anfrage ist mit 0,002 USD pro Anfrage immer noch günstiger. Also, was können wir zusätzlich tun, um die Kosten für die LLM-Inferenz weiter zu senken? Was wäre, wenn wir das HTML ganz weglassen und nur den Textanteil verwenden, würde das funktionieren? Tja, probieren geht über studieren…
const axios = require("axios");
const cheerio = require('cheerio');
const {GoogleGenerativeAI} = require("@google/generative-ai");
let query = "italian restaurants in New York, US"
let searchUrl = `https://www.google.com/search?hl=en&q=${encodeURIComponent(query)}`;
let response = await axios.get(searchUrl)
// Lade komplette Seite
const $ = cheerio.load(response.data);
// Entferne unnötige Tags
$("head,header,script,style,footer,img,svg").remove();
// Extrahiere nur den Textanteil der HTML-Daten
let serp = $.text()
const genAI = new GoogleGenerativeAI(apiKey);
const model = genAI.getGenerativeModel({
model: "gemini-1.5-flash",
systemInstruction: "Analysiere den HTML-Text von Google SERP Ortsergebnissen und extrahiere die Liste der Orte im JSON-Format {name, adresse, bewertung, preis}",
});
const chatSession = model.startChat({generationConfig: {
temperature: 1,
responseMimeType: "application/json", // JSON-Format
}});
const result = await chatSession.sendMessage(serp);
console.log(result.response.text());
Jetzt haben wir nur noch den Textanteil, das sind etwa 1000 Token. Das entspricht ungefähr 0,0007 USD pro Anfrage, das ist fast 30x günstiger als die Google Places API bei einer akzeptablen Ausgabequalität, die für die meisten Fälle ausreichend ist.
Das AI-Modell kann die Textdaten immer noch recht gut verstehen und ein schönes JSON-Dokument zurückgeben:
[
{
"name":"Tony's Di Napoli",
"bewertung":4.6,
"adresse":"147 W 43rd St",
"preis":"€€"
},
{
"name":"Ulivo",
"bewertung":4.4,
"adresse":"4 W 28th St",
"preis":"€€"
},
{
"name":"Ai Fiori",
"bewertung":4.4,
"adresse":"400 5th Ave #2",
"preis":"€€€€"
},
{
"name":"Patsy's Italian Restaurant",
"bewertung":4.3,
"adresse":"236 W 56th St",
"preis":"€€€"
},
{
"name":"Eataly NYC Downtown",
"bewertung":4.3,
"adresse":"101 Liberty St",
"preis":"€€"
},
{
"name":"Amici",
"bewertung":4.7,
"adresse":"165 Mulberry St",
"preis":"€€"
},
{
"name":"Gemma",
"bewertung":4.3,
"adresse":"335 Bowery",
"preis":"€€"
},
{
"name":"Carmine's",
"bewertung":4.5,
"adresse":"200 W 44th St",
"preis":"€€"
},
{
"name":"Masseria dei Vini",
"bewertung":4.6,
"adresse":"887 9th Ave",
"preis":"€€€"
},
{
"name":"Carmine's Italian Restaurant - Upper West Side",
"bewertung":4.4,
"adresse":"2450 Broadway",
"preis":"€€"
}
]
Sie können die Grenze natürlich noch weiter ausreizen und Open-Source-Modelle verwenden, wenn Sie möchten. Ich habe es mit Llama 3 ausprobiert, was einwandfrei funktionierte, und Sie zahlen nur einen Bruchteil des Preises. Bedenken Sie aber, dass die Hosting Ihres eigenen Llama 3-Modells recht teuer sein kann.
Sie erinnern sich vielleicht, dass ich in meinem Fall die Telefonnummer benötige, die in diesem Ergebnis nicht enthalten ist. Um dies zu erreichen, muss ich eine weitere Suchanfrage für den einzelnen Ort starten, für den wir die Informationen benötigen.
In diesem Fall müssen wir im Knowledge-Graph-Bereich suchen und von dort aus die Geschäftsdetails mit einer ähnlichen Technik wie zuvor extrahieren.
Dies ist nur ein Beispiel dafür, wie Sie Generative KI einsetzen und damit Ihre Prozesse und Produktivität optimieren und nebenbei noch Geld sparen können. Bleiben Sie dran für weitere Tipps!