Wer eine Webseite oder einen Blog betreibt (so wie ich) kommt um das Thema SEO – Suchmaschinenoptimierung nicht herum.
Zunächst möchte ich auf die Tools von GTmetrix und Google Page Speed eingehen. Beide Tools bieten dem Entwickler eine REST Schnittstelle an. Und genau an diesen REST Schnittstellen möchte ich mich in diesem Tutorial bedienen. Das Ergebnis dieser Auswertungen erhält man in einem “handlichen” JSON Format. Dieses macht die weitere be-/verarbeitung deutlich einfach und man kann sich einen kleinen Service auf einer VM oder einem RaspberryPI einrichten welcher in einem bestimmten Intervall diese Checks durchführt.
Inhaltsverzeichnis
GTmetrixAPI
Für die Verwendung der GTmetrixAPI muss man sich zunächst auf der Seite registrieren und einen Account auswählen.
In diesem Tutorial verwende ich den “Basic” Account, dieser reicht nach meiner Meinung für einfache Prüfungen erstmal völlig aus.
Wenn man sich nun einen Account angelegt hat, so benötigt man noch einen API Key, dieser wird später für die Aufrufe benötigt. Dazu rufen wir die Adresse https://gtmetrix.com/api/ auf und klicken auf der Seite auf “Generate API Key” (rechts, oben).
Auf dieser Seite bzw. in dem Bereich wo der API Key für GTmetrix zu finden ist, findet man noch zusätzlich zwei wichtige Informationen, einmal wie viele API Aufrufe für heute verbleiben (“Credits left”).
Nachdem wir nun den API Key haben können wir auch schon den ersten Aufruf starten. Alles was wir dazu benötigen ist das kostenfreie Tool “curl” dieses kann man auf der Seite CURL Download herunterladen. Wurde die ca. 6 MB große Datei heruntergeladen und in ein Verzeichnis entpackt, so kann auf der Kommandozeile ein Aufruf abgesetzt werden.
Hier bietet die GTmetrix auf der Seite ein kleines Snippet, welches man verwenden kann (alle wichtigen Daten sind bereits befüllt).
curl --user email-adresse@domain.land:1404e31febdeb4931e41c2b8306916e8 \ --form url=http://example.com --form x-metrix-adblock=0 \ https://gtmetrix.com/api/0.1/test
In diesem Beispiel muss nur die Adresse welche geprüft werden soll von “http://example.com” auf die eigene Adresse geändert werden. In meinem Fall ist es die Adresse “https://draeger-it.blog”.
Wenn man das Snippet direkt von der Seite in die Konsole kopiert, so werden unnötige Zeilenumbrüche hinzugefügt, diese sorgen dafür dass, das Script nicht korrekt ausgeführt wird. Ich habe einen Umweg über einen Texteditor gewählt und in diesem die Zeilenumbrüche entfernt.
Wenn alles geklappt hat, so erhält man einen JSON Respond welcher wiefolgt aussieht:
{"credits_left":98,"test_id":"ZPZqwqe5","poll_state_url":"https://gtmetrix.com/api/0.1/test/ZPZqwqe5"}
Auch hier finden wir wieder die Daten wie viele Anfragen noch verbleiben und jeweils die Test-ID und die Adresse unter welcher das Ergebnis abgerufen werden kann. Die Tests werden bei GTmetrix in einer Art Que abgearbeitet, d.h. das Ergebnis steht nicht sofort bereit. Je nach Serverauslastung beim Dienstanbieter und natürlich der Webseite kann dieses auch ein paar Minuten dauern.
Wenn das Ergebnis bereitsteht, erhält man unter der Adresse wiederum ein JSON Respond mit den Daten:
{ "resources":{ "report_pdf":"https://gtmetrix.com/api/0.1/test/ZPZqwqe5/report-pdf", "pagespeed":"https://gtmetrix.com/api/0.1/test/ZPZqwqe5/pagespeed", "har":"https://gtmetrix.com/api/0.1/test/ZPZqwqe5/har", "pagespeed_files":"https://gtmetrix.com/api/0.1/test/ZPZqwqe5/pagespeed-files", "report_pdf_full":"https://gtmetrix.com/api/0.1/test/ZPZqwqe5/report-pdf?full=1", "yslow":"https://gtmetrix.com/api/0.1/test/ZPZqwqe5/yslow", "screenshot":"https://gtmetrix.com/api/0.1/test/ZPZqwqe5/screenshot" }, "error":"", "results":{ "onload_time":48, "first_contentful_paint_time":81, "page_elements":2, "report_url":"https://gtmetrix.com/reports/example.com/1OJZEqbS", "redirect_duration":0, "first_paint_time":81, "dom_content_loaded_duration":null, "dom_content_loaded_time":47, "dom_interactive_time":47, "page_bytes":1930, "page_load_time":48, "html_bytes":606, "fully_loaded_time":99, "html_load_time":24, "rum_speed_index":81, "yslow_score":99, "pagespeed_score":99, "backend_duration":6, "onload_duration":0, "connect_duration":18 }, "state":"completed" }
Was mir in dieser Antwort jedoch fehlt, ist ein Hinweis welche Adresse geprüft wurde. Dieser kleine Datensatz wäre hilfreich, wenn man mehrere Webseiten prüfen möchte und somit die Daten auseinander halten muss.
Neben den Daten von GTmetrix erhalten wir zusätzlich einen Link zu einem Report von Google Page Speed, dieser ist jedoch auf englisch. Mit Google Page Speed und der API dazu beschäftigen wir uns etwas später.
Diese Daten finden sich auch auf der Webseite des Reports.
Wir haben also nun einen ersten Report über die API von GTmetrix erstellt und abgerufen. Da wir dieses nicht immer umständlich über die Kommandozeile erledigen wollen, schreiben wir uns ein kleines Tool.
Beispiel Tool zum abrufen eines Reports
Im nachfolgenden möchte ich zeigen wie man die Daten per Oracle Java abruft. Hierzu gibt es zwei Möglichkeiten einmal den normalen Weg über die Standardbibliotheken von Oracle Java oder aber man bedient sich einem Framework und hat das Ganze in einem kompakten 5 Zeiler. Der letztere klingt zunächst einmal viel besser, jedoch benötigen wir dazu einen Sack voll Abhängigkeiten und somit holen wir uns ggf. Defect & Bugs in das Tool welches wir ohne diese nicht hätten.
Das gesamte Eclipse Projekt mit allen Abhängigkeiten findest du am ende dieses Kapitels zum Download.
Zunächst einmal benötige ich eine Struktur welche mir die Daten für den Benutzer hält und eine Verbindung aufbaut. Hier nutze ich das Builder-Pattern.
package gtmetrixtest; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.JsonNode; import com.mashape.unirest.http.Unirest; import com.mashape.unirest.http.exceptions.UnirestException; public class GTmetrixClient { private static final String API_URL = "https://gtmetrix.com/api/0.1/test"; private String apiKey; private String username; private String url; private boolean useProxy = false; private String ipAddress; private int portNumber; private boolean useAuthentication = false; private String authUsername; private String authPassword; public GTmetrixClient(Builder builder) { this.apiKey = builder.apiKey; this.username = builder.username; this.url = builder.url; this.useProxy = builder.useProxy; this.ipAddress = builder.ipAddress; this.portNumber = builder.portNumber; this.useAuthentication = builder.useAuthentication; this.authUsername = builder.authUsername; this.authPassword = builder.authPassword; } public JsonNode execute() throws UnirestException { if (useProxy) { CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials(new AuthScope(ipAddress, portNumber), new UsernamePasswordCredentials(authUsername, authPassword)); HttpHost httpHost = new HttpHost(ipAddress, portNumber); Unirest.setProxy(httpHost); } HttpResponse<JsonNode> respond = Unirest.post(API_URL).basicAuth(username, apiKey).field("url", url).asJson(); return respond.getBody(); } public JsonNode pollTestReport(String pollStateUrl) throws UnirestException { if (useProxy) { CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials(new AuthScope(ipAddress, portNumber), new UsernamePasswordCredentials(authUsername, authPassword)); HttpHost httpHost = new HttpHost(ipAddress, portNumber); Unirest.setProxy(httpHost); } return Unirest.get(pollStateUrl).basicAuth(username, apiKey).asJson().getBody(); } protected String getApiKey() { return apiKey; } protected String getUsername() { return username; } protected String getUrl() { return url; } public String getAuthUsername() { return authUsername; } public String getAuthPassword() { return authPassword; } public static class Builder { private String apiKey = "-undefined-"; private String username = "jondoe"; private String url = "https://example.com"; private boolean useProxy = false; private String ipAddress; private int portNumber; private boolean useAuthentication = false; private String authUsername; private String authPassword; public GTmetrixClient build() { return new GTmetrixClient(this); } public Builder apiKey(String apiKey) { this.apiKey = apiKey; return this; } public Builder username(String username) { this.username = username; return this; } public Builder url(String url) { this.url = url; return this; } public Builder proxy(String ipAddress, int portNumber) { this.useProxy = true; this.ipAddress = ipAddress; this.portNumber = portNumber; return this; } public Builder authentication(String username, String password) { useAuthentication = true; this.authUsername = username; this.authPassword = password; return this; } } }
Wir erzeugen uns nun also einen GTmetrixClient dieser hält die Logindaten für die HTTPBasic Authentication sowie ggf. für die Proxyeinstellungen. Für dieses kleine Tool habe ich einen JUnit5 Testfall geschrieben, in welchem ich erstmal relativ einfach die Funktionen testen kann.
private static GTmetrixClient client; @BeforeAll static void setup() { GTmetrixClient.Builder builder = new GTmetrixClient.Builder(); builder.apiKey(API_KEY); builder.username(USERNAME); builder.url(URL); client = builder.build(); }
Dieses erzeugt uns zunächst einen Client. In der Klasse GTmetrixClient gibt es nun die Funktion “execute”, wird diese Aufgerufen so wird die Verbindung aufgebaut und ein Report angefordert.
private static final String JSON_KEY_CREDITS_LEFT = "credits_left"; private static final String JSON_KEY_POLL_STATE_URL = "poll_state_url"; private static final String JSON_KEY_TEST_ID = "test_id"; @Test void testServiceResult() { JsonNode result = null; try { result = client.execute(); } catch (UnirestException e) { e.printStackTrace(); } assertNotNull(result); Object creditsLeft = result.getObject().get(JSON_KEY_CREDITS_LEFT); Object pollStateUrl = result.getObject().get(JSON_KEY_POLL_STATE_URL); Object testId = result.getObject().get(JSON_KEY_TEST_ID); assertNotNull(creditsLeft); assertNotNull(pollStateUrl); assertNotNull(testId); assertTrue(creditsLeft.toString().trim().length()>0); assertTrue(pollStateUrl.toString().trim().length()>0); assertTrue(testId.toString().trim().length()>0); System.out.println(result.toString()); }
Man erhält jedoch zunächst ein JSON Respond mit den Meta Daten zum Report, denn wie bereits erwähnt wird diese Anfrage zum Report in einer Que abgelegt.
{"credits_left":96,"poll_state_url":"https://gtmetrix.com/api/0.1/test/q56Wk1bk","test_id":"q56Wk1bk"}
Nun können wir in gewissen Abständen abfragen, ob dieser Report bereitsteht. Dazu müssen wir die Adresse https://gtmetrix.com/api/0.1/test/q56Wk1bk aufrufen (zusätzlich mit HTTPBasicAuthentication).
Solange das Ergebnis des Reports nicht bereitsteht erhalten wir als Rückmeldung:
{"resources":{},"state":"queued","error":"","results":{}}
Der es gibt 3 definierte Werte für “state”
- started,
- que,
- complete
Solange der State nicht “complete” ist, hängt unser Report noch in der Que.
package gtmetrixtest; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import org.json.JSONObject; import org.junit.FixMethodOrder; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.runners.MethodSorters; import com.mashape.unirest.http.JsonNode; import com.mashape.unirest.http.exceptions.UnirestException; public class GTmetrixClientTestPoll extends AbstractGTmetrixClientTest { private static final String JSON_KEY_STATE = "state"; private static final String QUEUED = "queued"; private static final String STARTED = "started"; private static final String COMPLETE = "complete"; private JsonNode reportMetaData; @BeforeAll static void setup() { GTmetrixClient.Builder builder = new GTmetrixClient.Builder(); builder.apiKey(API_KEY); builder.username(USERNAME); builder.url(URL); client = builder.build(); } @Test void generateReport() throws UnirestException, InterruptedException { assertNotNull(client); reportMetaData = client.execute(); assertNotNull(reportMetaData); System.out.println(reportMetaData.toString()); JSONObject object = reportMetaData.getObject(); assertNotNull(object); String poll_state_url = (String) object.get(JSON_KEY_POLL_STATE_URL); assertNotNull(poll_state_url); assertTrue(poll_state_url.trim().length() > 0); String pollState; JSONObject jsonObject; do { Thread.sleep(1000); JsonNode pollTestReport = client.pollTestReport(poll_state_url); jsonObject = pollTestReport.getObject(); assertNotNull(jsonObject); pollState = (String) jsonObject.get(JSON_KEY_STATE); System.out.println(jsonObject.toString()); } while (pollState.equals(QUEUED) || pollState.equals(STARTED)); assertNotNull(jsonObject); assertTrue(pollState.equals(COMPLETE)); System.out.println(jsonObject.toString()); } }
In dem oben gezeigten Test fordere ich einen Report für eine Seite an und teste jede Sekunde, ob das Ergebnis bereitsteht (State != QUE oder STARTED).
Auf der Konsole sieht das dann folgendermaßen aus:
{"credits_left":84,"poll_state_url":"https://gtmetrix.com/api/0.1/test/Bc3kGTuK","test_id":"Bc3kGTuK"} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{},"state":"started","error":"","results":{}} {"resources":{"report_pdf":"https://gtmetrix.com/api/0.1/test/Bc3kGTuK/report-pdf","pagespeed_fil.....
Als Ergebnis erhalten wir wieder unser JSON Respond mit dem Report zur Seite.
Das gesamte Projekt findest du auf GitHub.com in meinem Repository. Oder hier bequem zum Download.
Nachdem wir nun über die API von GTmetrix einen Report erstellt haben möchten wir noch etwas mehr über die Seite erfahren und nutzen als nächstes Google Page Speed. Eigentlich könnten wir diese Daten auch über den Report von Google Page Speed abfragen, jedoch sind die Texte dort in Englisch, wem das nicht stört kann natürlich auch den Report verwenden. Jedoch möchte ich gerne einen Report mit deutschen Texten erhalten.
Google Page Speed Insights
Google bietet für viele der Services APIs an, dieses bietet einem Entwickler die Daten zu verwenden und in eigene Tools zu packen (hier muss die Lizenzbedingung geprüft werden). Ich möchte nun wie auch bei GTmetrix einen Report über eine Seite erstellen und abrufen.
Der Vorteil bei Google Page Speed Insights ist das hier kein Account angelegt werden muss.
Ich hatte bereits mit einer API von Google Erfahrung sammeln können (Weather API), diese wurde von heute auf morgen Abgeschaltet ich hoffe doch das diese hier verwendet etwas länger bestehen wird.
Eine Ausführliche Dokumentation zur API findest du unter https://developers.google.com/speed/docs/insights/v5/get-started ich möchte in diesem Kapitel aufzeigen wie man einen Report erzeugt.
Anmelden & API Key erzeugen
Zunächst einmal benötigst du einen Google Account, mit welchem du dich anmeldest. Wenn du dich angemeldet hast so muss ein API Key erzeugt werden.
Klicken wir zunächst auf die Schaltfläche “GET A KEY”. Es sollte sich dann ein Popup Dialog öffnen in welchem wir eine Auswahlliste mit der Beschriftung “Select or create project” finden. Wenn wir diese öffnen sehen wir bereits vorhandene Projekte und den Eintrag “+ Create a new project”. Wenn nun dieser Eintrag gewählt wird, so wird die Auswahlliste durch ein Eingabefeld ersetzt und wir können den Namen für das neue Projekt eingeben, ich wähle hier “GPSI – SEO Test”. Wurde der Name mit der Schaltfläche “Next >” bestätigt, so wird nach einer kurzen Ladetätigkeit der API Key angezeigt. Dieses markieren wir mit einem doppelklick und kopieren uns diesen (oder man nutzt die Schaltfläche zum Kopieren, rechts neben dem API Key).
Diesen API Key finden wir später auch in der API Console unseres Google Accounts.
Nun haben wir den API Key, was uns fehlt ist die Information was wir wohin senden müssen damit wir einen Report erzeugen können.
In der API findet man wiederum ein kleines Snippet von curl.
curl https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://developers.google.com
An dieses Snippet hängen wir unseren API Key als neues, zusätzliches Schlüssel / Wertepaar an.
&key=apikey
Statt “apikey” musst du natürlich deinen pers. API Key eintragen!
Ich musste zusätzlich die Adresse in doppelte Anführungszeichen setzen, da die Kommantozeile sonst hinter einem Backslash umgebrochen hätte.
Wird dann der Befehl auf der Kommandozeile aufgerufen so erhält man nach ein paar Sekunden warten einen sehr ausführlichen Report im JSON Format. Die Texte sind per Default jedoch auf Englisch, d.h. wir müssen noch einen zusätzlichen Parameter an die Adresse hängen damit wir deutsche Texte erhalten.
Dieser Parameter lautet “locale” und diesem wird als Werte die Locale übergeben. Für Deutschland wäre das der Werte “de_DE”.
Das Kommando für das generieren eines Reports der Seite https://draeger-it.blog in deutsch, sieht dann wiefolgt aus:
curl "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://draeger-it.blog&key=apikey&locale=de_DE"
Das Ergebnis ist dann ein sehr ausführlicher Report:
Google Page Speed Insights Report
Als nächstes möchte ich diesen Report wieder über ein Tool laden um diesen später weiterverarbeiten zu können.
Als Vorlage dient mir hier die Codebasis vom GTmetrixTest Projekt.
Beispieltool zum Abrufen eines Reports
Anders als bei GTmetrix benötigen wir keine Authentifizierung und die Requests werden per GET gesendet, d.h. wir bauen uns zunächst unsere Adresse mit einem StringBuilder zusammen.
StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(API_URL); urlBuilder.append("?"); urlBuilder.append("url="); urlBuilder.append(url); urlBuilder.append("&"); urlBuilder.append("key="); urlBuilder.append(apiKey); if(parameters != null) { parameters.keySet().stream().forEach(entry ->{ urlBuilder.append("&"); urlBuilder.append(entry); urlBuilder.append("="); urlBuilder.append(parameters.get(entry)); }); } Unirest.get(urlBuilder.toString()).asJson().getBody();
Hinter dem Objekt “parameter”, “versteckt” sich eine java.util.Map. In dieser Map stecken die optionalen Parameter.
Wie bereits erwähnt verwende, ich teile des Projektes von GTmetrixTest wieder.
In diesem Testfall erzeuge ich mir mit dem Builder einen Client auf welchem ich dann die Funktion “execute” aufrufe.
class GooglePageSpeedInsightsClientTest { private static final String API_KEY = "apiKey"; @Test void generateReport() throws UnirestException { GooglePageSpeedInsightsClient.Builder builder = new GooglePageSpeedInsightsClient.Builder(); builder.apiKey(API_KEY); Map<String, String> parameter = new HashMap<>(); parameter.put("locale", "de_DE"); builder.parameter(parameter); builder.url("https://draeger-it.blog"); GooglePageSpeedInsightsClient client = builder.build(); assertNotNull(client); JsonNode jsonRespond = client.execute(); assertNotNull(jsonRespond); System.out.println(jsonRespond.toString()); } }
Da es bei Google Page Speed Insights keine Que gibt, hält der Testfall so lange an bis das Ergebnis zurückgeliefert wird.
JSON Report von Google Page Speed Insinghts
Das Projekt findest du wiederum auf GitHub.com in meinem Repository unter https://github.com/StefanDraeger/GooglePageSpeedInsightsAPI-Test oder ganz bequem zum Download unter nachfolgendem Link.
Zusätzliche Parameter
Im Standardfall wird ein Lightehouse “Performance Report” erzeugt, mit dem Schlüssel “category” und den möglichen Werten:
- accessibility
- best-practices
- performance
- pwa
- seo
geändert werden.
Eine Liste aller zulässigen Parameter findest du auf der Seite https://developers.google.com/speed/docs/insights/v5/reference/pagespeedapi/runpagespeed
Fazit & Ausblick
Mit den beiden SEO APIs von GTmetrix & Google Page Speed Insights erhält eine gute Möglichkeit die SEO Werte seiner Webseite automatisiert berichten zu lassen.
Sicherlich gibt es bereits Tools wie Sistrix & Ryte welche dieses leisten können. Jedoch kosten diese im Monat etwas Geld und dieses kann den einfachen Nutzer schon abschrecken.
Als Nächstes möchte, ich nun ein Java Projekt erstellen welches in einem Apache Tomcat laufen soll und mit Quartz Jobs die Reports erzeugt.