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.

Wie wir sehen, hat Gmail die Werte true und false für uns in kleine Checkboxen konvertiert, ohne dass wir dazu zusätzlich HTML oder CSS schreiben mussten. So können wir schnell erkennen, welche Checks für welche URL fehlgeschlagen sind.

Schritt 10 - Schedule Trigger

Im letzten Schritt müssen wir unserem Workflow sagen, wie oft er ausgeführt werden soll. Noch nutzen wir den “When clicking Test Workflow” Trigger. Diesen ersetzen wir durch den “Schedule Trigger", der standardmäßig auf 1 Mal täglich um Mitternacht eingestellt ist. Wichtig ist hier, dass wir vorher die “Workflow Settings” überprüfen, um sicherzustellen, dass die korrekte Zeitzone eingestellt ist.

Jetzt speichern wir unseren Workflow ein letztes Mal und drücken den Button “Activate”. Unser Workflow ist damit fertig und wir stolze Schöpfer unseres eigenen SEO-Frühwarnsystems.

Schritt 11 - Error Workflow

Es kann passieren, dass unser Workflow hin und wieder nicht funktioniert. In so einem Fall möchten wir ebenfalls benachrichtigt werden. Anders als Zapier oder Make erhalten wir in n8n keine Benachrichtigung, sobald ein Workflow fehlschlägt. Das müssen wir uns selbst erarbeiten:

Wir nutzen den “Error Trigger”, der immer dann aktiv wird, wenn ein unvorhergesehener Fehler in unserem Workflow auftaucht. Im Output des Triggers finden wir die Workflow ID und den Namen. Anschließend versenden wir eine kurze E-Mail und verarbeiten ID und Namen in unserer Nachricht.

Speichern nicht vergessen. Nun öffnen wir unser Frühwarnsystem erneut und gehen in die Settings, wo wir zuvor die Zeitzone überprüft haben. Dort findet sich die Option “Error Workflow”, wo wir unseren gerade erstellten Workflow aus dem Dropdown auswählen können.

Und das wars. Jedes mal, wenn der Workflow fehlschlägt, bekommen wir nun eine E-Mail mit dem Link und können uns auf Spurensuche begeben. Ähnlich wie bei Zapier speichert n8n eine Historie der ausgeführten Workflows. Dort können wir leicht erkennen, welcher Knoten Probleme macht.

Ausblick

Unser Workflow ist sehr rudimentär, illustriert aber hoffentlich die Möglichkeiten von n8n. Hier noch ein paar Ideen für eine Version 2 unseres Frühwarnsystems:

Slack-Nachricht statt Email

Anstatt einer Email können wir unsere Testergebnisse natürlich auch als Slack-, Discord- oder Telegram-Nachricht versenden.

Anbindung ans Ticket-System

Ideal wäre, wenn der Workflow auch direkt ein Ticket mit dem korrekten Adressaten erstellt. Dank der JIRA- oder Asana-Knoten ist das leicht machbar.

Datenbank statt Google Sheets

Wollen wir sorglos viele Daten in unser Protokoll schreiben, ist Google Sheets nicht der richtige Ort dafür. Jepp, auch Postgres- und BigQuery-Knoten sind vorhanden.

Content Change Alert

Ein klassisches Problem ist, dass der Content einer Seite verändert wurde, ohne dass Team SEO darüber Bescheid weiß. Dazu müsste das HTML der geprüften Seiten bei jedem Test gespeichert und beim nächsten Test (am nächsten Tag) verglichen werden.

Mehr SEO Checks

In unserem Beispiel führen wir sehr einfache Tests durch. Aktuell prüfen wir beispielsweise nicht, ob eine URL auch in der Sitemap XML zu finden ist oder via robots.txt gesperrt wurde. Auch prüfen wir nur das serverseitige HTML. In einer Version 2 können wir die Seite vollständig rendern lassen, allerdings braucht es dazu auch mehr Ressourcen und externe Dienste oder zusätzliche Libraries wie Puppeteer, Playwright oder Selenium.

Warum das Ganze?

Wäre es nicht deutlich leichter, auf fertige Lösungen zu setzen statt ein Tool zu nutzen, um ein anderes zu imitieren? Das ist immer eine Frage der Anforderungen. In meinem Fall möchte ich maximale Flexibilität, um das Verhalten des Workflows ganz an meine oder Kundenbedürfnisse und Tools anzupassen.

  • Tests ergänzen? Kein Problem.
  • Direkt Tickets in meinem Projektmanagement erstellen lassen? Easy.
  • Das Ganze mit Daten aus Drittanbieter-APIs anreichern? Machbar.

Außerdem kostet mich n8n in der “self hosted”-Variante erstmal nichts, außer der Infrastruktur, auf der es “laufen” soll (die nicht kostenlos ist). Man hat also nicht direkt das nächste Abo am Hals. Auf der anderen Seite steigt dafür aber der Maintenance-Aufwand. Die Frage ist also Flexibility oder Convenience? Was Skalierbarkeit angeht, kann ich zum aktuellen Zeitpunkt keine Aussage treffen und nur auf Reddit Posts oder n8n’s Queue Mode verweisen.

Über uns.

Statt Dienstleistungen an der Oberfläche bieten wir Ihnen vom ersten Moment an maßgeschneiderte Lösungen für Ihre Anforderungen. ganztags. ist nah an Ihren Themen und identifiziert sich damit wie einer Ihrer Mitarbeiter. Unsere Kunden durchlaufen keine Ebenen von Projekt- und Produktmanagern. Unser Team spricht direkt 1-zu-1 mit dem Kunden. Somit halten wir nicht nur den Wasserkopf, sondern auch das Budget auf einem nötigen Niveau.


Wie wir die Wünsche unserer Kunden erfüllen, erklärt unsere Arbeitsweise.

Lernen Sie uns kennen!

Hallo ganztags.

mein Name ist {Name} und ich benötige Hilfe mit {Projekt}. Bitte kontaktiert mich unter {Kontaktmöglichkeit}.

Alternativ schreiben Sie uns direkt eine Mail an hallo@ganztags.net.*

*Telefonisch sind wir bei Erstkontakt nicht erreichbar. Nachdem uns neulich das Farbband für das Fax ausgegangen ist, haben wir die Telefone gleich mit entsorgt. Ein direkter Austausch per Meets, Zoom o.ä. ist aber natürlich kein Problem.