SEO APIs : GTmetrixAPI & Google Page Speed Insights

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.

GTmetrixAPI

Für die Verwendung der GTmetrixAPI muss man sich zunächst auf der Seite registrieren und einen Account auswählen.

GTmetrix - Accounts und kosten
GTmetrix – Accounts und kosten

In diesem Tutorial verwende ich den „Basic“ Account dieser reicht nach meiner Meinung für einfache Prüfungungen 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).

GTmetrix - API Key
GTmetrix – API Key

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 wieviele 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“.

curl - Aufruf der GTmetrixAPI
curl – Aufruf der GTmetrixAPI

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 einpaar Minuten dauern.

Wenn das Ergebnis bereit steht 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.

GTmetrix - grafischer Report auf der Webseite
GTmetrix – grafischer Report auf der Webseite

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 Login Daten 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 bereit steht 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 bereit steht (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.

Google Page Speed Insights - API Key erzeugen (1)
Google Page Speed Insights – API Key erzeugen (1)

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 solange an bis das Ergebnis zurück geliefert 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. 

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.