SEO-Frühwarnsystem mit n8n
 18.09.2024 |   Autor: Lars Antrack |   Lesezeit ca. 14 Minute(n) 

SEO-Frühwarnsystem mit n8n

Idealerweise haben wir ein System, dass uns sofort warnt, sobald eine Website Probleme macht. Natürlich gibt es bereits Tools dafür. Es gibt aber auch gute Gründe, sich so ein Frühwarnsystem selbst zu bauen. Und genau das machen wir in diesem Beitrag.

Manche Seiten sind für uns besonders wichtig. Sei es, weil sie viel Traffic bekommen, strategische Rankings halten oder sonst wie bedeutsam für die User Journey sind. Diese Seiten sollten immer erreichbar sein und problemlos von Google und anderen relevanten Diensten verarbeitet werden.

Idealerweise haben wir ein System, dass uns ohne großen Verzug informiert, sobald die Seiten Probleme machen. Und das nicht erst nach einer Woche, sondern sofort. Natürlich gibt es bereits Tools dafür wie Testomato oder Ghost Inspector.

Man kann es sich aber auch selbst bauen, und genau das machen wir jetzt auch.

Mit n8n bauen wir uns einen Workflow, der wichtige Seiten täglich prüft, dabei verschiedene SEO-Kriterien untersucht und schließlich eine E-Mail versendet, sobald ein Fehler identifiziert wurde.

Schritt 0 - Credentials einrichten

Unser Workflow wird mit verschiedenen Google Diensten wie Sheets und Gmail arbeiten. Damit n8n diese Dienste nutzen darf, müssen wir diese Dienste in der Google Developer Console aktivieren und unsere Credentials (Client ID und Client Secret) an n8n übergeben. Diesen Schritt müssen wir nur einmal pro Dienst einrichten und können die Credentials dann in jedem anderen Workflow wiederverwenden.

Schritt 1 - Die Liste der Key Pages

Wir erstellen eine einfache Tabelle in Google Sheets mit zwei Tabellenblättern.

Tabellenblatt 1 -> Key Pages
URLs die wir täglich prüfen lassen möchten. Diese URLs sollten uns bereits bekannt sein und werden hier eingetragen.

Tabellenblatt 2 -> Logs
Ein Protokoll unserer Checks. Wichtig ist hier die Benennung der Spalten. Short and Sweet, ohne Leerzeichen. Das hilft uns später bei der Arbeit mit n8n. Mit Ausnahme der Spaltenüberschriften ist die dieser Tabelle leer und wird später von n8n befüllt.

Schritt 2 - Google Sheets mit n8n auslesen

In n8n legen wir einen neuen Workflow an und starten mit zwei Knoten:

  1. dem manuellen Workflow Trigger und
  2. einen Google Sheets Knoten.

Es gibt eine Reihe verschiedener Knoten für das Arbeiten mit Google Sheets. Da wir erstmal nur unsere Key Pages auslesen wollen, entscheiden wir uns für den “Get row(s) in Sheet” Knoten.

Diesen müssen wir nun konfigurieren. Dazu die passenden Credentials aus Schritt 0 auswählen (Ggf. die nötigen APIs in der Google Developer Console aktivieren) und dem Knoten beibringen, aus welchem Sheet und welchem Tabellenblatt die URLs ausgelesen werden sollen.

Ein Klick auf “Test Step” sollte uns mit unseren Key Pages belohnen.

Exkurs: Alle Knoten in n8n basieren auf dem Empfang und dem Senden von JSON Daten. Für das Anzeigen von Daten haben wir drei Optionen: Table, JSON und Schema. Per Standard ist der “Table” Mode ausgewählt. Dabei zwingt n8n die Daten in zwei Dimensionen: Spalten und Zeilen. Als Spalten verwendet n8n alle Keys der ersten Ebene des JSON Objektes. Verschachtelte Werte der höheren Ebenen (2, 3 und so weiter) werden als eine “Zelle” angezeigt. Jede “Zeile” einer Tabelle bezeichnet n8n als Item und jeder Knoten wird (insofern nicht anders konfiguriert) so oft ausgeführt, wie sich Zeilen (also Items) in der Tabelle befinden.

Der Modus “JSON” zeigt uns “Rohdaten” und “Schema” gibt uns alle Keys in dem JSON Objekt mit der Information, um welchen Datentyp es sich dabei handelt: Zahl, Text, Array, Objekt und so weiter. Das praktische daran ist, dass wir im “Table” oder “JSON” Modus die Daten einfach per Drag and Drop in die Input-Felder unserer Knoten ziehen können. Das erspart uns lästiges Tippen.

Jetzt geht's ans Eingemachte, denn wir müssen die einzelnen Seiten aufrufen und deren HTML sowie dazugehörige Header untersuchen.

Schritt 3 - HTTP Requests

Damit wir jede URL einzeln prüfen können, nutzen wir als nächstes den “Loop over items” Knoten, der unsere “Input Tabelle” Zeile für Zeile bearbeitet. Der “Loop over items” Knoten hat zwei Output Ports: loop und done.

Exkurs: Wie zuvor gesagt, führt n8n die Schritte per Standard für alle Items bzw. Zeilen in der Tabelle durch. Wir müssten als keine Schleife (Loop over items) verwenden, haben so aber mehr Kontrolle und können das Ergebnis pro URL prüfen.

Die Output Ports bezeichnet man auch als “Branches”. In der “loop” Branche definieren wir, welche Schritte pro URL in unserer Liste durchlaufen werden sollen und sammeln die Ergebnisse dann in der “done” Branchen, um sie weiterzuverarbeiten.

Der nächste Knoten, den wir in der “loop” Branch anlegen, ist der “HTTP Request” Knoten, mit dem wir unsere Seiten abrufen können. Wichtig ist hier, dass der Knoten so eingestellt wird, dass er:

  • die Header der HTTP Response zurück gibt (Option: Include Response Headers and Status) und noch wichtiger
  • den Workflow nicht unterbricht (Option: Never Error), auch wenn die getestete Seite einen 404 Fehler zurückgibt.

Exkurs: Wann immer ein Knoten keinen Output erzeugt, beendet n8n den Workflow. Per Standard gibt der “HTTP Request” Knoten einen Fehler zurück, wenn die angefragte URL mit einem Status Code 404 antwortet. Damit unser Workflow nicht beendet wird, nutzen wir die Option “Never Error” aus den Knoten-Optionen:

Wie auch im Schritt zuvor können wir mittels des “Test Step” Buttons überprüfen, ob der Output unseren Vorstellungen entspricht. Aber vorsicht: Oft erscheint eine Warnung, dass n8n aufgrund der übertragenen Datenmenge keine Vorschau generieren möchte:

Wir können diesen Hinweis mit einem Klick auf “Show data anyway” einfach ignorieren (keine Empfehlung) oder den Output herunterladen und lokal untersuchen. Im Falle des Downloads erzeugt n8n eine JSON-Datei, die alle Daten enthält. Wir müssen einmal verstanden haben, aus welchen Schlüssel-Wert-Paaren unser Output besteht, damit wir diese in den nächsten Knoten weiterverarbeiten können.

Da wir nicht alle Daten benötigen, können wir den Output vereinfachen. Dazu nutzen wir den “Edit Fields (Set)” Knoten und definieren, welche Schlüssel-Wert-Paare wir brauchen. In unserem Fall sind das:

Exkurs: Input-Felder in n8n haben 2 Modi: Fixed und Expression. Im Expression Modus können wir auf jedes Item über das “$json” Shortcut zugreifen (siehe $json.data, $json.statusCode im Screenshot) oder auch kurze JavaScript Statements ausführen.

Da unser HTTP-Knoten aber nicht mehr die URL selbst beinhaltet, die wir aufgerufen haben, greifen wir dazu auf den vorherigen Knoten (Loop over items) zurück und ergänzen die “url” in unserem “Edit Fields” Knoten.

Nun haben wir alle Daten, die wir für unsere Tests benötigen.

Schritt 4 - HTML extrahieren

Aktuell haben wir HTML in einem großen String, also einem “Textfeld”. Wir möchten gezielt SEO relevante Tags untersuchen und dazu verwenden wir den “HTML” Knoten, der uns mit der Operation “Extract HTML” erlaubt, HTML Strukturen mittels CSS-Selektoren zu durchsuchen.

Hier müssen wir einmal definieren, wo genau sich in unserem Output aus dem Vorgängerknoten das HTML befindet, und definieren dann einzelne CSS-Selektoren, derer wir beliebig viele hinzufügen können.

Ein Selektor erwartet immer drei Argumente:

  1. Key - Wie das Feld im Output heißen soll, also quasi die Spaltenüberschrift.
  2. CSS-Selektor - You guessed it… den passenden CSS-Selektor.
  3. Return Value 👇

Hier haben wir verschiedene Optionen. Möchten wir den Text eines Tags extrahieren (beispielsweise des Title-Tags), stellen wir diese Option auf “Text”. Möchten wir aber den Wert eines Attributes erhalten, wählen wir “Attribute” und müssen anschließend das Attribut benennen, dessen Wert wir erhalten möchten.

Die 4. Option “Skip Selectors” ist optional. Hier können wir Ausschlüsse definieren, müssen wir aber nicht.

Wichtig ist, dass wir bei jedem Selektor die Schaltfläche “Return Array” aktivieren. So können wir schnell identifizieren, ob ein Tag mehrmals vergeben wurde. Es hat aber auch den schönen Vorteil, dass immer Daten zurückgegeben werden, auch wenn der Selektor keinen Wert enthält. So stellen wir sicher, dass unsere “Tabelle” immer aus den selben Spalten besteht (und nicht etwa weniger, wenn ein Selektor keinen Wert zurück gibt).

Achtung: Der “HTTP Request”-Knoten gibt nur das serverseitige HTML zurück. Werden unsere Seiten aber zu einem Großteil im Client gerendert, kommen wir mit diesem Ansatz nicht weit. Dazu später mehr im Teil “Ausblick”.

Schritt 5 - SEO Checks

Wir haben nun alles, was wir brauchen, um unsere Checks durchzuführen. Jetzt müssen wir sie nur noch schreiben.

Wann immer wir eine spezielle Lösung benötigen, zu der es keinen fertigen Knoten gibt, können wir n8n’s Code-Knoten verwenden. Das setzt zwar eine gewisse Programmierkenntnisse voraus, aber glücklicherweise spricht ChatGPT fließend JavaScript (und Python). Folgendes wollen wir prüfen:

  • Fehlt das Title-Tag?
  • Ist das Title-Tag mehrfach vorhanden?
  • Gibt die Seite einen 200er Status-Code zurück?
  • Gibt die Seite einen Redirect zurück?
  • Ist die Seite kanonisiert?
  • Steht die Seite auf noidex?
  • Sind mehrere Robots-Anweisungen zu finden?

Und dabei belassen wir es erstmal.

Wir konstruieren unsere Tests so, dass sie den booleschen Wert “true” zurückgeben, wenn ein Check zutrifft und “false”, wenn alles ok ist.

Hier machen wir uns das Datenmodell von n8n zu Nutze, denn wir können einfach neue Eigenschaften in unseren JSON-Daten ergänzen und damit die Spalten unserer Tabelle erweitern. Am Ende haben wir alle Checks und ihre Zustände (true, false) in unserer Tabelle und damit die Kernfunktionalität unseres Frühwarnsystems entwickelt.

Schritt 6 - Google Sheet aktualisieren & Protokoll erstellen

Jetzt möchten wir in Google Sheets protokollieren, wann der Test durchgeführt wurde. Dazu widmen wir uns nun der “done” Branch, da unsere URLs ja bereits erfolgreich verarbeitet wurden und fügen mit dem “Edit Fields” Knoten das aktuelle Datum hinzu, da dies aktuell noch nirgends zu sehen ist.

Aktiviert unbedingt die Option “Include Other Input Fields”, ansonsten bekommt ihr nur ein einsames Objekt mit einem Datum zurück.

Jetzt wollen wir zwei Dinge in Google Sheets bewerkstelligen:

  1. Das letzte Prüfdatum in unser Tabellenblatt “Key Pages” schreiben und
  2. unsere Checks im Tabellenblatt “Logs” protokollieren.

Dazu benötigen wir zweimal den Google Sheets-Knoten.

Für das Prüfdatum setzen wir im Google Sheets-Knoten das Feld “Operation” in den Modus “Update Row”. Das bedeutet, wir wollen eine Zeile aktualisieren, aber sonst nichts der Tabelle hinzufügen.

Damit der Knoten die richtigen Zeilen erkennt, müssen wir ihm beibringen, welcher Wert aus unserem n8n Output mit den Einträgen in der Tabelle übereinstimmen muss. In unserem Fall ist das die Spalte “URL” aus der Google Sheets-Tabelle und genau das tragen wir in das Feld “Column to Match On” ein.

Als nächstes müssen wir definieren, was aktualisiert werden soll. Der Knoten erkennt, welche Spalten in der Tabelle vorhanden sind und bietet sie uns im Bereich “Values to Update” an. Hier müssen wir nur eintragen:

  • wo die Daten aus n8n mit Google Sheets übereinstimmen ( $json.page) und
  • welche Zeile mit welchem Wert aktualisiert werden soll ( $now.format(“yyyy-MM-dd”))

Die Expression $now gibt das aktuelle Datum zurück. “.format” erlaubt uns, das Datum zu formatieren, da wir keinen vollständigen Zeitstempel benötigen, sondern nur das Jahr, den Monat und den Tag.

Mit einem Klick auf “Test Step” können wir überprüfen, ob unsere Daten erfolgreich in unser Google Sheet geschrieben wurden.

Parallel dazu möchten wir die Ergebnisse des Tests in unsere zweite Tabelle schreiben. Das Vorgehen unterscheidet sich nur leicht von dem Schritt zuvor. Wieder nutzen wir den Google Sheets-Knoten, diesmal aber mit der Operation: “Append Row” und der Option “Map Columns automatically”. Diese Option setzt voraus, dass unser n8n JSON-Objekt dieselben “Spalten” enthält, wir das Google Sheet, in das wir die Daten schreiben wollen. Daher müssen wir sicherstellen, dass diese Spalten in der Google-Tabelle auch enthalten sind, da der Knoten sonst eine Fehlermeldung ausspuckt und den Workflow stoppt.

Haben wir den Tabellenkopf unserer Google-Tabelle korrekt angelegt, werden die Daten passend übertragen und die Tabelle jedes mal erweitert, wenn der Workflow ausgeführt wird. Damit ist auch unser Protokoll vollständig. Was jetzt noch fehlt, ist die passende E-Mail, wenn der Test einer URL fehlschlägt.

Schritt 7 - Liste filtern

Wir möchten E-Mails nur dann erhalten, wenn ein Test fehlschlägt und diese E-Mail soll nur fehlerhafte URLs beinhalten. Dazu müssen wir unsere Tabelle nach den Seiten filtern, bei denen mindestens einer der Checks den Wert “true” enthält. Hier hilft uns wieder die Code-Node:

Dieser kleine Code-Schnipsel filtert die Zeilen unserer JSON-Daten danach, ob irgendeine der Spalten, die mit “check_” beginnen, auf “true” steht. Falls ja, werden sie in den Output der Code Node geschrieben. Damit bleiben dann nur die Einträge übrig, die wir in unserer E-Mail haben wollen.

Wichtig: Generiert unser Code-Knoten keinen Output, beispielsweise weil keiner der Checks fehlgeschlagen ist und alle Werte damit auf “false” stehen, wird der nächste Knoten nicht ausgelöst. Das können wir in unserem Workflow nutzen, da wir keine E-Mail senden möchten, wenn alles in Ordnung ist. Dieses verhalten lässt sich nach Bedarf anpassen. So könnten wir …

Anschließend nutzen wir den “Edit Fields (Set)”-Knoten, um den Output einzuschränken. Da wir am Ende eine kleine Tabelle in unsere E-Mail schreiben möchten, lassen wir nur die Felder übrig, die wir zwingend brauchen. Da das immer noch die Mehrheit der Spalten betrifft und wir nicht manuell alles eintragen möchten, was wir behalten wollen, gehen wir den entgegengesetzten Weg und definieren, was wir nicht behalten möchten. Das erreichen wir mit 3 Optionen im Knoten:

  1. Include other input fields
    Damit weiß der Knoten, dass andere Felder aus dem Input mit ausgegeben werden sollen, auch wenn sie nicht konkret definiert wurden.
  2. Input fields to include
    Hier wählen wir die Option “All Except” aus dem Dropdown.
  3. Fields to exclude
    Hier tragen wir die Namen der Spalten ein, die wir nicht im Output haben wollen.

Schritt 8 - HTML generieren

Da in unseren Daten jetzt nur noch übrig ist, was wir auch in unserer E-Mail haben möchten, können wir den HTML-Knoten mit der Funktion “convertToHtmlTable” nutzen. Wie der Name vermuten lässt, werden unsere JSON-Daten hier in eine HTML-Tabelle konvertiert, die wir noch ein wenig stylen können.

Da wir unserer E-Mail aber gern noch etwas Text mitgeben möchten, verwenden wir noch einen HTML-Knoten mit der Funktion “generateHtmlTemplate” und binden dort einfach das HTML aus dem Vorgängerknoten ein.

Jetzt müssen wir unsere E-Mail nur noch versenden.

Schritt 9 - E-Mail versenden

Das ist denkbar einfach, denn natürlich gibt es dafür den passenden Knoten. Im Gmail-Knoten legen wir einmal Empfänger, Betreff und die Nachricht fest. Für die Nachricht wählen wir aus dem Dropdown “HTML” unter der Option “Email Type” und übergeben unser fertiges HTML aus dem Schritt zuvor. Drücken wir nun auf “Test Step” haben wir die fertige E-Mail im Posteingang und können das Ergebnis unserer Arbeit bewundern.