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.
Für die Verwendung der GTmetrixAPI muss man sich zunächst auf der Seite registrieren und einen Account auswählen.
GTmetrix - Accounts und kosten
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).
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 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 protected]: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
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.
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 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.
Read the full article