automatisierte crawl-abgleiche in r blogbeitragsbild

SEO-Monitoring mit Crawlabgleichen in R – 2020 Version

Bekanntlich ist es sinnvoll eine Website während der Umsetzung des SEO-Konzepts sowie im Rahmen der Betreuung genaustens im Auge zu behalten. Eine der mächtigsten Instrumente um immer auf dem neusten Stand zu sein, ist das zeitversetzte Crawlen und Abgleichen der Website basierend auf der URL Basis. Um den regelmäßigen Zeitaufwand dafür auf ein Minimum zu reduzieren, werden wir in diesem Beitrag alles vom Crawling bis zum Output-Excel automatisieren! Anders ausgedrückt, das Einzige was zu tun ist neben der initialen Einrichtung, ist die intellektuelle Analyse des Ausgabe-Excel.

Da unser letzter Blog-Beitrag zu diesem Thema schon einige Zeit her ist und wir uns bei [Get:Traction] natürlich auch ständig weiterentwickeln, ist es an der Zeit, mal wieder ein Update zu lancieren! Optimierung ist schließlich der Inbegriff unserer Kernkompetenz.

Der Output – Was kommt dabei raus?

Um abzuklären, ob der Crawlabgleich etwas für Dich ist, ist es sinnvoll zuerst einmal einen Blick auf den Output zu werfen:

Das Endergebnis ist nach wie vor eine Excel Datei, welche in einem Reiter für jede Metrik die Unterschiede zum vorherigen Crawl aufzeichnet. Dabei sind folgende Punkte abgedeckt:

  • Neue URLs
  • Nicht mehr verlinkte URLs
  • Änderungen bei URLs, die in beiden Crawls vorhanden sind:
    • Status Codes
    • Canonical-Anweisungen
    • Meta Robots
    • H1
    • Titles
    • Descriptions
    • Content Types
    • Klicktiefe
    • Dateigröße
SEO-Monitoring: Excel Datei mit Reitern für jede Metrik

Übersicht der Arbeitsschritte

Grundsätzlich haben wir 3 Schritte während des gesamten Prozesses:

– Wir crawlen die Website in Zeitabständen

– Wir vergleichen die Crawls miteinander

– Wir analysieren das Endergebnis

SEO Monitoring mit Screaming Frog, R und Excel

Crawlen der Website

Für diesen Arbeitsschritt benötigen wir die Screaming-Frog SEO Spider.

Erstellen der Ordnerstruktur und Einrichtung der Crawl-Jobs im Aufgabenplaner

Um unseren Export nach beliebigen Zeitabständen zu automatisieren und damit regelmäßig viel Zeit zu sparen, legen wir eine standardisierte Ordnerstruktur an und erstellen im Windows Aufgabenplaner Jobs für das Crawling.

Ordnerstruktur

Initial benötigen wir 4 Ordner, die alle im gleichen Verzeichnis abzulegen sind:

Ordnerstruktur

Die Struktur sieht folgendermaßen aus:

  • 01_crawl_abgleiche
    • /[Kunde]
      • Hier kommen die vom R-Skript generierten XLSX rein.
  • 02_sf_exports
    • /[Kunde]
      • /[yyyy.mm.dd.hh.mm.ss]
        • Hier legt der Screaming Frog automatisch seine Exporte ab. Zum einen den „internal_all.csv“. Zum anderen – um im Nachhinein im Detail nachsehen zu können – den gesamten Crawl als „.seospider“.
  • 03_sf_configs
    • Hier liegen die Screaming-Frog Konfigurationsdateien ab (sofern vorhanden).
  • 04_R
    • /[R-Projekt]
      • Hier liegt das „.R“-Projekt, welches aus den letzten beiden Crawls für eine Website den Abgleich bastelt und diesen in „01_crawlabgleiche/[Kunde]/“ abglegt.

Alle fett geschriebenen Ordner in der obigen Darstellung müssen einmal initial angelegt werden. Der Rest wird automatisch von unserem Skript und dem SF generiert.

Windows Job für Crawls

  1. Erstellen der 4 initialen Ordner.
  2. Erstellen eines Ordners für jeden Kunden unter 02_sf_exports.
  3. Wenn Screaming-Frog Konfigurationsdateien für die vorgesehenen Crawls vorhanden sind, diese im Ordner „03_sf_configs“ ablegen.
  4. Öffne die „Aufgabenplanung“ über die Windows-Suche.
  5. Es ist empfehlenswert einen neuen Ordner für die Crawlings sowie die R-Jobs in der Aufgabenplanung zu erstellen:
Neuen Ordner erstellen
  1. Navigiere in den neu erstellten Ordner
  2. Erstelle mit Rechtsklick -> „Neue Aufgabe erstellen“ eine neue Aufgabe
  3. Im Dialogfeld müsst ihr dem Job nun einen Namen geben (Hier empfiehlt es sich die Namensgebung zu standardisieren und das Intervall mit einzubeziehen z.B. „1month_kunde“. Unter dem Reiter „Trigger“ könnt ihr das gewünschte Intervall und die Ausführungstage festlegen. Wir crawlen bspw. wöchentlich. Das sähe dann so aus:Navigiere in den neu erstellten Ordner
  4. Erstelle mit Rechtsklick -> „Neue Aufgabe erstellen“ eine neue Aufgabe
  5. Im Dialogfeld müsst ihr dem Job nun einen Namen geben (Hier empfiehlt es sich die Namensgebung zu standardisieren und das Intervall mit einzubeziehen z.B. „1month_kunde“. Unter dem Reiter „Trigger“ könnt ihr das gewünschte Intervall und die Ausführungstage festlegen. Wir crawlen bspw. wöchentlich. Das sähe dann so aus:
SEO Monitoring: Trigger bearbeiten
  1. Im Reiter Aktion legen wir nun den Job folgendermaßen an:
  • Kopiert euch den Pfad der „ScreamingFrog CLI“ mittels „Shift-Rechtsklick“ -> „Als Pfad kopieren“
  • („C:\Program Files (x86)\Screaming Frog SEO Spider\ScreamingFrogSEOSpiderCli.exe“)
  • Klickt auf „Neu“ und tragt den Kopierten Pfad im Feld „Programm/Skript“ ein
  • Kopiert nun in das Feld „Argumente hinzufügen“ folgenden Text:

–project-name Kunden_Crawlabgleiche –task-name 1month_tue_1200_kunde –crawl „https://www.beispiel.de/“ –config „C:\03_sf_configs\kunde.seospiderconfig“ –headless –output-folder „C:\02_sf_exports\kunde“ –timestamped-output –save-crawl –export-tabs Internal%3AAll –export-format csv

  • Ersetzt nun alle fett markierten Stellen im Text durch eure eigenen Angaben.
    • Kunden_Crawlabgleiche ist der Projektname im Screaming-Frog. Kann dort angelegt werden. (Optionaler Parameter)
    • 1_month_tue_1200_[kunde] ist der Name des Tasks im Screaming-Frog.
    • Die URL ist die zu crawlende website
    • Die beiden Pfade geben einmal den Ort der zu nutzenden SF-Config für den Crawl an und den Output Folder. Hier muss der Pfad in den jeweiligen Kunden Ordner unter /02_sf_exports rein.
  • Eine Dokumentation zu den genutzten Parametern findet sich hier.

Das Endergebnis sieht dann so aus:

Endergebnis

Jetzt könnt Ihr auf OK klicken und den Job speichern. Damit haben wir unseren automatisierten Crawl!

Abgleichen der Websites

Für diesen Arbeitsschritt benötigen wir R Studio. Hier eine kurze Anleitung und Erklärung zur Installation.

Anlegen des R-Skripts und Erstellen von Windows-Jobs

Anlegen des R-Skripts

  • Ladet euch den Ordner hier von unserem Git-Hub herunter.
  • Hier befinden sich 2 Dateien („website_change_monitor.R“ und „FUNS.R“)
  • Öffnet R Studio und legt ein neues R-Project im Ordner /04_R/ an.
  • Kopiert jetzt die beiden Dateien aus dem Download in euren Projektordner („/04_R/[Projektname]/“)

Jetzt müssen wir nur noch 2 Pfade im R Skript anpassen und schon haben wir alles für unseren Windows Job vorbereitet!

Navigiert hierzu in R Studio in eurem Projekt in die Datei „website_change_monitor.R“ und passt folgende Variablen an:

Variablen anpassen

PATH_TO_XLSX_EXPORTS ist der Pfad in euren /01_crawl_abgleiche Ordner.

PATH_TO_SF_EXPORTS ist der Pfad in euren /02_sf_exports Ordner.

(Tipp: Um auf der sicheren Seite zu sein könnt ihr „C:/“ eingeben und dann mit TAB und den Pfeiltasten bis in den jeweiligen Ordner navigieren)

Speichert das Skript jetzt mit „STRG & S“.

Damit das Skript funktioniert müsst ihr ein paar R Packages installieren. Das geht ganz einfach indem ihr am oberen Rand in R Studio auf den hints „install“ durchklickt. Sollten die Meldungen nicht aufpoppen, könnt ihr alle Packages folgendermaßen installieren:

Als Beispiel einmal:

Die Namen aller benötigten Packages findet ihr unter #1 Libraries (alle die mit „library()“ geladen werden) im Skript.Die Namen aller benötigten Packages findet ihr unter #1 Libraries (alle die mit „library()“ geladen werden) im Skript.

Anlegen des Windows-Jobs für das R-Skript

  1. Legt euch wieder im Aufgabenplaner einen neuen Ordner für R an.
  2. Erstellt hier eine neue Aufgabe mit Namen und Trigger. Der Trigger sollte natürlich nach dem ausführen der Crawl Jobs gesetzt werden.
  3. Gebt im Feld „Programm/Skript“ den Pfad zu eurer R-Installation ein, ungefähr so: „C:\Program Files\R\R-3.6.1\bin\Rscript.exe“
  4. Im Feld „Argumente hinzufügen“ geben wir nun den Dateinamen des R-Skripts an: „website_change_monitor.R“
  5. Im Feld „Starten in“ geben wir den Pfad des Ordners an, in dem sich das Skript befindet: „/04_R/[Projektname]/“
  6. Jetzt können wir den Job speichern!
Job speichern

Das wars! Jetzt haben wir einen völlig automatisierten Crawlabgleich. Zur Einrichtung eines weiteren Abgleichs, müsst ihr nur einen Ordner für den neuen Kunden in 02_sf_exports erstellen und einen Crawl-Job im Windows Aufgabenplaner erstellen. Das Skript registriert den neuen Kunden von alleine. Eure Output-Excels findet ihr nun im Ordner /01_Crawl_Abgleiche/.

Das Skript in allen Einzelheiten

Libraries

Hier werden die zum Ausführen des Skripts benötigten R-Packages geladen. Um diese beim Ausführen des Skripts nutzen zu können, müsst ihr diese vorher installieren (Beschrieben in Punkt Anlegen des R-Skripts).

Vars

Hier werden die Pfade für die Imports und Exports festgelegt. Zusätzlich legen wir hier unseren Slack API Key als Variable (Auskommentiert da nicht essenziell) an, um erfolgreich ausgeführte Scripts sowie Fehlermeldungen in unseren Scripts- Slack Channel automatisiert zu pushen! Damit hat man also auch ein Warnsystem implementiert. Die Variable „#TESTING_WEBSITE“ ist standardmäßig auskommentiert mit einem #. Sollte man Adhoc nur eine einzige Website vergleichen wollen, kann man hier das # entfernen und in die Gänsefüßchen die Website schreiben. Dabei einfach den Namen des Ordners verwenden, wie ihr ihn im sf_exports Verzeichnis erstellt habt.

Slack

Hier wird dem „slackR“ Package die nötige Information mitgegeben. Den API Key, in welchen Channel soll gepusht werden? Unter welchem Namen sollen die Nachrichten dort auftauchen?

Die Sektion ist im download-Skript ebenfalls auskommentiert, da die Nutzung von SlackR natürlich nicht obligatorisch ist.

Get Files

Find all CSV’s

Hier werden alle Dateien mit .csv-Endung rekursiv aus dem in der Variable PATH_TO_SF_EXPORTS enthaltenen Pfad aufgelistet.

Split paths

Nun splitten wir die Pfade der aufgelisteten Dateien in dem Dataframe „df_file_paths“ in einzelne Spalten auf.

Extract website, datetime

Hier determinieren wir, welche Teile der in „df_file_paths“ enthaltenen Pfade welches Element sind und geben den entsprechenden Spalten die Namen.

Convert to date

Hier konvertieren wir das bisher als Character zu verstehende Datum in der Spalte „datetime“ in ein richtiges Datums-Datenformat. Dazu nutze ich die äußerst nutzvolle Library Lubridate.

Check if two Crawls exist

Wir gruppieren nach der Spalte „website“ und zählen, ob die Anzahl an Elementen pro Gruppe mehr als 1 beträgt. Anschließend lösen wir die Gruppierung wieder auf.

Get last two crawls

Wir gruppieren abermals nach „website“ und fügen dem Dataframe die Spalte „date_rank“ hinzu. Dieser wird mithilfe der rank() Funktion absteigend durch die Spalte „datetime“ berechnet. Abschliessend filtern wir auf Einträge mit „date_rank“ 1 & 2 und lösen die Gruppierung wieder auf.

Get Website for iteration

Um für jede Website einen einzelnen Crawl Abgleich bauen zu können, müssen wir jede Website als einzelnes Objekt einer Liste betrachten und die gleichen Aktionen für jedes Objekt in dieser Liste iterativ durchführen. Hierzu bedienen wir uns eines einfachen for()-loops. Diese Liste bereiten wir nun mithilfe der unique-Funktion vor. Diese gibt uns, nach Einspeisung der Spalte „websites“ aus „df_file_paths“, einen Vector ohne doppelte Werte zurück. Sprich – wir haben eine Liste aller unserer Websites, die als Iterationsobjekt benutzt werden können.

Filter for testing

Bevor wir mit dem eigentlichen Abgleich loslegen, gucken wir hier nach, ob die vorher angesprochene Variable „TESTING_WEBSITE“ besteht und wenn ja, filtern nur auf die dort enthaltene Website.

Compare Crawls

Wir iterieren über unseren „websites“ vector und führen für jede enthaltene Website folgende Aktionen aus:

Am Anfang des Loops reduzieren wir das in dieser Iteration behandelte Datenset auf die Daten einer einzigen Website und setzen temporäre Variablen für das neue und alte Crawldatum. Wir geben eine Message in der Console aus und lesen beide Crawls, mithilfe der in „FUNS.R“ definierten Funktion „read_crawl“, ein. Dazu fügen wir eine Spalte mit neuem/altem Datum an.

Create website-dir if not exists

Der Kommentar ist ziemlich treffend – wir kreieren (wenn nicht vorhanden) ein Verzeichnis mithilfe des Iterationsobjektes sowie des Pfads aus PATH_TO_XLSX_EXPORTS.

Get new (linked) URLs

Abermals eine Message in die Console. Hier filtern wir innerhalb eines Anti-Joins vom neuen auf den alten Crawl, auf URLs, in dessen „content“ Spalte „html“ vorkommt oder die „status“ Spalte dem String „Connection Timeout“ entspricht.

Dabei bleiben nur noch die im neuen Crawl enthaltenen URLs zurück, die im alten nicht mehr existieren. Wir wählen per „select“ aus, welche Spalten wir haben wollen und konvertieren das ganze in ein Dataframe, da die XLSX Library kein Tibble versteht.

Get no longer linked/ deleted URLs

Message in die Console. Gleiches Vorgehen wie bei Punkt 4.5.2 Nur hier Anti-joinen wir vom alten auf den Neuen Crawl.

Get Status Code of not linked/deleted URLs

Message. Hier prüfen wir den Status Code der nicht mehr verlinkten Seiten, da wir ihn ja über den Crawl nicht kennen. Der Grund ist, dass wir prüfen wollen, ob eine Seite korrekterweise nicht mehr verlinkt ist. Antwortet die URL mit „404 Not Found“, ergibt es vollkommen Sinn, sie nicht mehr zu verlinken. Antwortet die Seite hingegen mit „200 OK“, stellt sich die Frage, warum sie nicht mehr verlinkt ist, wenn sie doch erreichbar ist. Im Hintergrund rennt hier ein parallelisierter Status-Code-Fetcher los. Je nachdem, wie viele URLs nicht mehr verlinkt sind, kann die Abfrage durchaus länger dauern. Auf der Console seht ihr aber die gerade abgefragten URLs, sodass ihr zumindest einen groben Überblick habt, dass sich noch etwas tut und dass das Skript nicht hängen geblieben ist. (Anhand dieses Punktes ist unter anderem erkennbar, dass es sinnvoll ist, die Crawls jeweils mit gleicher Konfiguration durchzuführen).

Get identical URLs

Was wohl 😉. Wir filtern auf HTML URLs beider Crawls und machen uns eines inner-Joins habhaft, um die identischen URLs des Abgleichs zu erhalten. Dazu fügen wir im Datenset Spalten für jedes Detail einer URL an, welche angeben, ob sich der entsprechende Wert geändert hat.

Get identical URL with changes

Da wir nun das Datenset an URLs definiert haben, welches in beiden Crawls enthalten ist, können wir nun die einzelnen Bestandteile dieses Datensets betrachten und vergleichen.

Verwaiste Seiten:

Hier definieren wir, je nach dem, was in unserer Spalte change_orphan_pages, die wir in Punkt 4.5.5 gebildet haben, steht, ob es sich um eine nicht mehr oder neu verwaiste Seite handelt. Dies speichern wir als Dataframe, um es später in der Excel abbilden zu können.

Dieses Vorgehen bleibt für jede Metrik das gleiche. Darum werde ich hier nicht auf jeden Unterpunkt eingehen.

Get biggest resources

Hier sortieren wir bei Images, JavaScript und CSS, absteigend nach der Größe (size_bytes) und speichern diese als DataFrame.

Create Excel

Hier nutzen wir die Library XLSX, um eine Arbeitsmappe zu erstellen und diese mit unseren sheets in der „sheet_order“ variable und den im Loop erstellten Data-Frames zu befüllen. Mit saveWorkbook() wird dem Export der Pfad übergeben und der Crawlabgleich, mit neuem und altem Datum im Namen, im entsprechenden Ordner der Website abgespeichert.

Send Slack notification (Fail, Success)

Zu guter Letzt unsere Slack-Notification. Hier wird eine Message aus dem momentanen Iterationsobjekt und einer vorgefertigten Nachricht zusammengebaut und mithilfe der vorher erstellten Variablen und des Packages SlackR in den Channel gepostet. Alle Slack-Inhalte sind im download-Skript auskommentiert, da die Nutzung von „SlackR“ rein optional ist.

Damit ist der For-Loop abgeschlossen und das Skript erstellt die Crawlabgleiche aller vorhandenen Websites.

Viel Spaß beim Abgleichen! Wenn ihr Fragen oder Anregungen habt, gerne kommentieren oder mir direkt eine Mail schreiben 🙂

r4seo: Dokumentation und Reproduktion von SEO-Anasylen mit R

R4SEO – Dokumentation, Reproduktion und Kommunikation von SEO-Analysen mit R

Ich muss leider mit einem Problem beginnen, das wohl viele von uns kennen. Stell Dir dazu kurz vor, dass Dein Kunde Deine Analyse nach einem halben Jahr in der Schublade wiedergefunden hat und jetzt gerne mit der Umsetzung Deiner Maßnahmen beginnen möchte. Oder Du bist Angestellter in einem Unterneh-men und das Management hat Dir endlich die Ressourcen zur Verwirklichung bereitgestellt. So oder so, es ist einige Zeit vergangen, Du hast Dich anderen Dingen gewidmet und um ehrlich zu sein, weißt Du nicht mehr so genau, was und wie Du damals im Detail analysiert hast.
Alles kein Problem, schnell das Analysedokument wieder hervorgekramt, reingeschaut – sieht alles echt gut aus, hast Dir damals wirklich etwas dabei gedacht. Beispielsweise das Diagramm hier gefiel Deinem Kunden / Vorgesetzten wirklich gut:

R4SEO Diagramm Eigen-vs Fremdwahrnehmung

Abbildung 1: Eigen- vs. Fremdwahrnehmung der eigenen Webpages anhand von Page Views und Internal Link Score

Damals wusstest Du natürlich ungefähr, für welche Seiten die unbeschrifteten Punkte stehen. Heute…  – natürlich nicht mehr. Also gut, auch kein Problem: Excel-Datei mit den Daten suchen. Excel-Datei in einem anderen Ordner als dem Ordner mit der Analyse-Datei finden. Vergangenheits-Ich verfluchen, weil es mal wieder nichts dokumentiert hat. Das richtige Tabellenblatt suchen (Tabelle 34, ist doch logisch, hättest Du auch direkt drauf kommen können). Formeln suchen. Formeln verstehen. Excel-Datei suchen, die in der Formel referenziert wird, suchen. Sich für das nächste Mal wirklich, wirklich, wirklich vornehmen, alle Dateien besser zu strukturieren und alle Arbeitsschritte zu dokumentieren.

Oder anders herum. Da jetzt ein halbes Jahr vergangen ist, haben sich natürlich die Page Views geändert. Außerdem hast Du ein bisschen an der internen Verlinkung herumgeschraubt. Auch kein Problem: Excel auf, neuen Crawl importieren. Ach ja, Du hast die URLs ja vorher segmentiert. Wo liegen noch mal die Segment-Muster? Gut, passt wieder. Aktuelle Google Analytics-Daten an die URLs schreiben. Damals natürlich nicht mit Analytics Edge abgefragt, also doch wieder manuell Hand anlegen.

Ok, ich höre ja schon auf. Ich denke, Du fühlst den Schmerz und weißt, was ich meine.

Wir haben hier also ein massives Problem, was sehr viel Zeit, Geld und vor allem Nerven kostet. Denn im Grunde darf keiner dieser Arbeitsschritte nötig sein. Jede Analyse sollte zu jedem Zeitpunkt nachvollziehbar und reproduzierbar sein. Warum stehen wir dennoch immer wieder vor dem gleichen Problem? Dazu ein kleiner Abstecher in die Theorie.

Prozessschritte einer Datenanalyse

Jeder Analyseprozess besteht aus vier bzw. fünf Teilschritten. Als Erstes müssen die Daten aus den jeweiligen Quellen (Datenbanken, APIs, CSVs, usw.) in das Analyse-Tool – meistens Excel – importiert werden. Im nächsten Schritt werden die Daten aufgeräumt, um sie für den nachfolgenden Prozessschritt handhabbar zu machen. Aufräumen meint unter anderem das Normalisieren von Werten, weite in lange Tabellen überführen, Daten im JSON-Format in tabellarische Form bringen et cetera.

Nun folgt die eigentlich spannende Tätigkeit: das Verstehen. Das Verstehen ist dabei ein iterativer Prozess aus Datentransformation und -visualisierung. Du formulierst Fragen, die Du an die Daten stellst. Um sie zu beantworten, musst Du die Daten in den meisten Fällen in eine aggregierte Form bringen. Konkret möchtest Du beispielsweise wissen, wie häufig welcher Status Code in einem Crawl vorkommt. Oder Du reduzierst die täglichen Rohdaten aus der Google Search Console (GSC) respektive Google Analytics (GA) auf eine höhere Zeitebene, sprich: Monate, Quartale, Jahre. Transformation heißt auch, dass Du die Rohdaten um zusätzliche Dimensionen anreicherst, indem Du beispielsweise URL- oder Phrasensegmente bildest. Die Transformation wechselt sich mit der Visualisierung ab. Denn Auffälligkeiten in den Daten sind in Diagrammen zumeist einfacher zu erkennen als in den zugrundeliegenden Tabellen.

Anwendungsbrüche bei einer Datenanalyse

Abbildung 2: Anwendungsbrüche bei einer Datenanalyse mit Excel und Word / Power Point

Während dieser explorativen Datenanalyse überführst Du Deine Einsichten in ein Dokument, mit dessen Hilfe die Insights an den jeweiligen Empfänger kommuniziert werden sollen. Das ist zumeist Word oder Power Point. Und an dieser Stelle tritt der Bruch ein, der das oben beschriebene Problem bedingt. Analyse und Kommunikation finden in unterschiedlichen Anwendungen statt. Möchten Du oder der Empfänger auf die zugrundeliegenden Daten zugreifen, ist das nicht mehr ohne weiteres möglich, denn sie sind nicht aneinandergekoppelt. Natürlich kannst Du im Word immer wieder auf die entsprechende Excel(-Tabelle) hinweisen, aber das ist unglaublich mühsam und fehleranfällig. Meistens lässt man es dann doch … wird schon keiner nachfragen.

Und von der anderen Richtung kann das finale Dokument nicht unmittelbar – beispielsweise mit aktualisierten Daten –  neu generiert werden. Im Grunde musst Du jedes Diagramm und jede Beschreibung noch einmal anfassen.

Und dann bleibt noch der Punkt mit der Dokumentation. Selbst wenn Du Dir in Excel eine Power Query-Abfrage gebaut hast, die das Importieren und Transformieren der Daten weitestgehend automatisiert und somit reproduzierbar macht, ist Excel einfach nicht dafür ausgelegt, eine umfassende Dokumentation der einzelnen Schritte zu erstellen. Und sind wir ehrlich, niemand mag seine Arbeit dokumentieren, und wenn es einem das Tool dann auch noch extra schwer macht, nehmen wir das doch gerne als Vorwand, um es dann ganz sein zu lassen. Ist dann ja nicht mein Problem, sondern das des Zukunft-Ichs.

So, ich denke, das waren genug Probleme. Wie sieht die Lösung aus? – Mit einem Buchstaben gesagt: R.

Datenanalyse mit RStudio und RMarkdown

Abbildung 3: Reproduzierbare und Nachvollziehbare Datenanalyse mit RStudio und RMarkdown

Mit etwas mehr Buchstaben: R + RStudio + RMarkdown. R ist eine statistische Programmiersprache. Kernmerkmale sind, dass sie sich sehr gut auf Daten in tabellarischem Format versteht und ein umfangreiches Spektrum an Funktionen zur Datentransformation, -aggregation und -visualisierung bietet. Also all das, was wir brauchen. RStudio ist eine IDE, eine graphische Oberfläche, die auf R, das standardmäßig auf der Kommandozeile ausgeführt wird, aufsattelt und somit die Handhabung deutlich vereinfacht und komfortabler macht. RMarkdown schließlich ist eine Library, die es ermöglicht, R-Skripte und Markdown in einer Datei zu verwenden, um daraus direkt Dokumente zu erzeugen.

Aber genug der theoretischen Hinführung. Die Vorteile werden am konkreten Beispiel am deutlichsten.

RStudio & RMarkdown: Eine schnelle Einführung


Zwei Hinweise noch vorweg: Ich kann an dieser Stelle natürlich keine Einführung in R bieten. Ich denke aber, dass die Syntax und die verwendeten Funktionen weitestgehend selbsterklärend sind. Ich beschreibe daher nur grob, was die einzelnen Code-Abschnitte tun. Solltet Ihr durch diesen Beitrag Lust bekommen haben, Euch mit R zu befassen, findet Ihr im Internet eine Vielzahl an Tutorials zur Installation und ersten Schritten in R und RStudio. Wenn Ihr Euch tiefergehend mit der Thematik beschäftigen wollt, kann ich Euch die Online-Lern-Plattform https://www.datacamp.com wärmstens ans Herz legen. Einige Kurse sind dort kostenlos, sodass Ihr euch einen ersten Eindruck verschaffen könnt. Ein Buch, das nicht nur R, sondern vor allem auch die zugrundeliegenden Konzepte der Daten Analyse sehr gut vorstellt, ist R for Data Science von Hadley Wickham. Dem Autor der Library tidyverse, die ich im Folgenden ausgiebig verwenden werde. Das Buch könnt Ihr auch online unter https://r4ds.had.co.nz/ kostenlos lesen.

🎁 Um Euch das Abtippen / Kopieren zu ersparen, findet Ihr den Code hier zum Downloaden:
https://github.com/netzstreuner/r4seo_reproduzierbare_analysen


Legen wir los!

Damit Ihr eine grobe Vorstellung davon bekommt, wovon ich überhaupt rede, hier ein Screenshot von RStudio. (1) ist der File Explorer. Hier seht Ihr Eure Skripte, Ordner, CSVs et cetera. (2) ist der Code Editor, in dem Ihr Skripte schreibt. (3) ist das Environment. Wenn Ihr Variablen oder Funktionen definiert, werden sie Euch hier angezeigt. (4) ist die Console, auf der Nachrichten vom Code ausgegeben werden. Oder Ihr nutzt sie, um ad hoc Code auszuführen, der nicht Bestandteil des Skripts sein soll.

Einführung in das RStudio Interface

Abbildung 4: Das RStudio-Interface

Im linken, unteren Panel (2) habe ich bereits ein sogenanntes R-Notebook erzeugt, das standardmäßig eine Beispiel-Befüllung enthält. Im Detail sieht es wie folgt aus:

R Notebook Beispiel

Abbildung 5: R Notebook mit Beispiel-Befüllung

Durch einen Mausklick kann das Notebook gerendert werden, sodass ein HTML-Dokument erzeugt wird.

Ausführung Code-Chunk

Im GIF seht Ihr, wie ich zuerst einen sogenannten Code-Chunk ausführe, der einen Plot generiert. Dazu gleich im Detail mehr. Anschließend rendere ich den Output. Da das Format ein HTML-Dokument ist, bietet es eine gewisse Interaktivität. Zu sehen ist, wie ich den Code, der den Plot generiert, ein- und ausklappe. Für eine Zeile Code ist das recht müßig, aber Ihr seht hier das Key-Feature, welches die Dokumentation der Analyse ermöglicht. Da jederzeit der zugrundeliegende Code des Notebooks angezeigt werden kann, dokumentiert sich die Analyse quasi selbst. In Kombination mit Kommentaren im Code ist dadurch jederzeit nachvollziehbar, wie Daten transformiert oder aggregiert wurden, um die Tabellen oder Plots im Output-Dokument zu erstellen.

Im folgenden Screenshot seht Ihr das Notebook und den Output noch einmal gegenübergestellt, damit Ihr im Detail nachvollziehen könnt, welche Elemente wie gerendert werden.

Vergleiche R Notebook-Skript und gerenderter Output

Abbildung 6: R Notebook-Skript und gerenderter Output im Vergleich

Am Anfang des Notebooks seht Ihr ein YAML, über das Meta-Angaben wie der Titel und das Output-Format definiert werden können. Anschließend folgt ganz normaler Text in Markdown-Syntax. Das Besondere an den Notebooks sind die in Backticks eingefassten Code-Chunks. In diesen kann R-Code geschrieben werden, der beim Rendering des Notebooks ausgeführt wird. Analyse-Code und Beschreibung der Insights stehen somit in einem Dokument und bilden eine Einheit, die jederzeit erneut ausgeführt werden kann.

Konfiguration des Notebooks

Nachdem Ihr gesehen habt, wie R-Notebooks grundsätzlich funktionieren, möchte ich Euch exemplarisch durch die einzelnen Analyse-Schritte führen. Dazu lösche ich den gesamten Beispielinhalt und beginne, meinen eigenen zu schreiben.

Zunächst gilt es, das Notebook resp. das daraus resultierende Analyse-Dokument zu konfigurieren. Wie bereits erwähnt, könnt ihr mittels YAML bestimmte Meta-Angaben und Einstellungen definieren. Meine sehen so aus:

Außerdem kann ohne Weiteres auch noch ein Logo eingehängt werden.


Hinweis: Immer, wenn ich wie gerade Code-Snippets angebe, müsst Ihr Euch die einfach als Code-Chunk denken. Der Übersichtlichkeit halber verzichte ich auf die Einfassung.


Das Resultat sieht dann wie folgt aus.

Beispiel-Output

Abbildung 7: Beispiel-Output mit benutzerdefinierter Formatierung, TOC und Überschriften

Wie Ihr im YAML seht, können über eine normale CSS-Datei Formatierungen vorgenommen werden. Hier ändere ich exemplarisch Font und unterstreiche Überschriften. Außerdem lasse ich automatisch ein Inhaltsverzeichnis basierend auf den nummerierten Überschriften generieren. Je nach Empfänger der Analyse macht es Sinn, den Code immer in eingeklappter Form einzubinden, um den Textfluss nicht unnötig zu stören.

Daten importieren und aufbereiten

Gut, beginnen wir mit der eigentlichen Arbeit. Als Erstes müsst Ihr natürlich die benötigten Daten importieren. Als Beispiel verwende ich hier zwei Screaming-Frog-Crawls – einen aktuellen und einen älteren. Letzteres dient später dazu, zu prüfen, welche Veränderungen es zwischen zwei Zeitpunkten auf der Website gab.

Ach ja, eins noch vorweg:  Für die nachfolgenden Arbeiten benötigen wir zwei Packages, die den Funktionsumfang von R erweitern. Das ist zum einen tidyverse, eine sehr mächtige Sammlung verschiedenen Sub-Packages zum Einlesen, Aufbereiten und Visualisieren von Daten, zum anderen janitor. Letzteres stellt einige komfortable Hilfsfunktionen bereit, um Daten ad hoc zu aggregieren. Eine sehr gute Einführung in die Syntax und die wichtigsten Funktionen von tidyverse findet Ihr in dieser Präsentation. Die beiden Packages ladet Ihr, indem Ihr den nachfolgenden Code direkt unterhalb des YAML einfügt.

Einige Libraries geben beim Laden eine Nachricht aus. Die wollt Ihr natürlich nicht im Analyse-Dokument haben. Auch der Code an sich macht im Output-Dokument keinen Sinn. Dem Empfänger dürfe egal sein, welche Packages Ihr verwendet. Daher kann auf Ebene der einzelnen Code-Chunks sehr genau definiert werden, wie diese sich beim Rendern verhalten sollen. Hier unterdrücke ich mit message=FALSE die Lade-Nachricht und mit include=FALSE die Darstellung des Codes.

So, jetzt aber wirklich an die Arbeit. Daten laden. Wie gesagt, wir brauchen zwei Crawls. Diese habe ich in einem Unterverzeichnis _data abgelegt. Dadurch schaffe ich Ordnung in meinem Workspace, indem ich Skript-Dateien, Daten und – später – Exporte voneinander trenne.

Das Laden geht denkbar einfach mit einer Zeile Code von der Hand.

Die Funktion read_csv liest die Datei des angegebenen Pfades ein. Der Screaming Frog schreibt immer einen Kommentar in die erste Zeile der CSV, die wir hier mit skip = 1 direkt überspringen. Die Daten werden eingelesen und in einen sogenannten DataFrame, sprich: eine Tabelle, in der Variable crawl gespeichert. So sieht sie aus.

Crawlimport Dataframe

Abbildung 8: DataFrame des importierten Crawls

Die Spaltennamen sind für ein programmatisches Umfeld recht unschön. Sie enthalten Groß- und Kleinschreibung sowie Leer- und Sonderzeichen. Wollt Ihr die Spalten in der jetzigen Form ansprechen, müsstet Ihr sie jedes Mal in Backticks einfassen, da andernfalls das Leerzeichen die Ausführung unterbricht. Also schnell eine kleine Hilfsfunktion hinzugenommen. clean_names() normalisiert die Benennung automatisch.

Normalisierte Spaltenüberschriften

Abbildung 9: Normalisierte Spaltenüberschriften

Genauso wie die Spaltenüberschriften liegen einige der Spaltenwerte in Groß- und Kleinschreibung vor. Das ist problematisch, wenn Ihr bspw. wissen wollt, wie häufig welcher Content Type vorkommt.


Hinweis: Wenn Ihr im Folgenden Code seht, der mit > eingeleitet wird, sind das Eingaben, die ich auf der Konsole durchgeführt habe. Sie sind also kein Bestandteil des Analyseskripts, sondern dienen nur dazu, schnell einen Blick auf die Daten zu werfen.


Hier dürfte Euch erst einmal egal sein, ob UTF nun groß- oder kleingeschrieben ist. Entsprechend definiert Ihr Euch eine eigene Funktion, die die Werte der angegebenen Spalten zu Kleinbuchstaben ändert.

Diese wendet Ihr dann ebenfalls auf den Crawl an.

Wie Ihr im obigen Screenshot gesehen habt, gibt es nach der Normalisierung immer noch zwei Ausprägungen für HTML-Seiten – mit und ohne Angabe des Character-Encodings. Euch interessiert aber nur, ob es eine HTML-Seite ist. Genauso ist die Angabe des Bildformats zu spezifisch – Image allein reicht vollkommen aus. Daher definiert Ihr eine weitere Funktion, die die Werte der Spalte content auf weniger Ausprägungen reduziert und in die neue Spalte content_type schreibt.

Bei Analyse ist es immer sinnvoll, einzelne Website-Bereiche getrennt voneinander zu betrachten. Klassisch kann hier nach Seiten-Templates (Startseite, Produktübersichtsseite, Produktdetailseite usw.) differenziert werden. Insbesondere bei Verlagswebsites bietet sich zusätzlich eine inhaltliche Segmentierung nach Ressorts (Politik, Wirtschaft, Feuilleton etc.) an. Auch hierfür definiert Ihr eine Funktion und wendet sie auf den Crawl an.

Schließlich gebt Ihr noch an, bei welchen Spalten es sich um kategoriale Daten handelt. Das heißt, die Werte in der jeweiligen Spalte können nur eine bestimmte Anzahl an vorgegebenen Ausprägungen annehmen. In der Spalte indexibility kann bspw. nur indexable oder non-indexable stehen. Wichtig ist dies insbesondere bei Spalten wie dem Status Code, deren Werte zunächst Zahlen sind (200, 301, 404 etc.). Auf den konkreten Grund gehe ich später im Detail ein.

In Summe sehen das Einlesen und Aufbereiten wie folgt aus. Da Ihr die Aufbereitung als Funktionen definiert habt, könnt ihr Sie natürlich ohne Weiteres sowohl auf den aktuellen als auch auf den alten Crawl anwenden.

Ad-hoc-Betrachtung der Daten

Oben habt Ihr das Package janitor geladen. Janitor macht das Betrachten von Daten on the fly wunderbar einfach.

Wollt Ihr wissen, wie häufig die einzelnen Content Types vorkommen, könnt Ihr den crawl an die Funktion tabyl() geben. Neben dem Aufkommen erhaltet Ihr damit auch direkt die Anteile.

adorn_totals() (adorn = schmücken) fügt eine Zeile mit der Summe hinzu.

Die Prozent-Formatierung ist recht unleserlich. adorn_pct_formatting() schafft Abhilfe.

In gleicher Weise könnt Ihr bspw. überprüfen, wie häufig jeder Status Code bei jedem Content Type vorkommt.

Visualisierung der Crawl-Daten mittels ggplot

Mit den oben gezeigten Funktionen könnt Ihr Euch einen schnellen Überblick verschaffen. Wesentlich intuitiver für das Verständnis von Mengengerüsten sind allerdings Diagramme – insbesondere bei der Kommunikation der Analyse.

Im Folgenden zeige ich Euch daher, wie Ihr ein Barchart der Status Codes plotten könnt. Hier zunächst einmal der Code, den wir gleich Schritt für Schritt durchgehen.

Ihr gebt den aktuellen Crawl an die count()-Funktion, die das Aufkommen der einzelnen status_codes zählt. Das kennt Ihr im Prinzip schon von den obigen Screenshots. Das Ergebnis ist eine Aggregationstabelle. Diese leitet Ihr wiederum an die Plotting-Funktion ggplot(). Innerhalb dieser definiert Ihr, dass der Status Code auf der x-Achse, die Anzahl (n) auf der y-Achse dargestellt werden soll. Außerdem gebt Ihr noch an, dass die Balken entsprechend der Status Codes eingefärbt werden sollen (fill). Dazu gleich mehr. Führt Ihr den Code bis hierhin – also bis zur und einschließlich der dritten Zeile – aus, erhaltet Ihr zunächst nur folgenden Plot.

Beispiel Plotleinwand

Abbildung 10: Leinwand des Plots mit Achsen-Beschriftung

ggplot() zieht zunächst einmal nur eine Leinwand und das Koordinatensystem auf, auf der die Daten dargestellt werden sollen. Ihr habt allerdings noch nicht angegeben, welche Art von Diagramm verwendet werden soll. Das macht Ihr mit geom_bar(). Der Code bis dort ausgeführt, plottet das nachfolgende Diagramm.

R Plot Statuscodes

Abbildung 11: Barchart der Status Codes

An dieser Stelle wird nun auch ersichtlich, warum wir bei der Aufbereitung der Crawl-Daten die Spalte status_code als kategorial definiert haben. Derartige Daten werden von ggplot() automatisch mit möglichst unterschiedlichen Farbwerten geplottet. Hättet Ihr dies nicht getan, würde ggplot() den Status Code, der ja aus Zahlenwerten besteht, als kontinuierlichen Datentyp auffassen und entsprechend ein kontinuierliches Farbspektrum verwenden. An der Legende könnt Ihr den Unterschied sehr gut erkennen.

R Plot Beispiel Füllfarbe

Abbildung 12: Darstellung der Füllfarbe bei Kontinuierlichen Werten

Mittels geom_text() gebt Ihr an, dass die Anzahl der Status Codes noch einmal als Label über die Balken geschrieben werden soll. Ich hoffe, langsam wird deutlich, wie Plotting mittels ggplot() funktioniert. Ihr tragt Schicht für Schicht auf die Leinwand auf, bis Ihr das gewünschte Aussehen konfiguriert habt. Eigentlich auch nicht wesentlich anders im Vergleich zu Excel, nur dass Ihr tippt statt in Dropdowns zu klicken.

R Plot Beispiel Balkenbeschriftung

Abbildung 13: Beschriftung der Balken

Die Legende ist redundant, denn die farblichen Balken dienen allein der schnellen visuellen Unterscheidbarkeit. Sie encodieren hier nicht unbedingt eine zusätzliche Information. Also unterdrückt Ihr die Generierung der Legende via guides(fill = FALSE).

R Plot Beispiel entfernte Legende

Abbildung 14: Entfernte Legende

Der graue Hintergrund ist meiner Meinung nach sehr aufdringlich. Also verwende ich hier ein helleres Theme (theme_light()).

R Plot Beispiel helles Theme

Abbildung 15: Anwendung eines hellen Themes auf den Plot

Bisher habt Ihr nur die Darstellung der Daten beeinflusst. Natürlich können auch die Textelemente des Plots verändert werden. Mittels labs() gebt Ihr die Beschriftung an. theme() dient dazu, ihre Darstellung genauer zu formatieren. Der Titel soll fett geplottet werden, Untertitel und Caption in Grau.

R Plot Beschriftung mit Untertitel

Abbildung 16: Beschriftung des Plot mit (Sub-)Titel, Caption und Achsen-Labels

Zu guter Letzt könnt Ihr natürlich noch die Farben der Balken bestimmen, um beispielsweise Eure Unternehmensfarben zu verwenden. Oder weil Ihr – wie ich – die Standardfarben einfach nicht so großartig findet. Ich gebe die Farbcodes hier als Variable COLOR_SCHEMA an, sodass ich sie nur einmal am Anfang des Skripts definieren und jederzeit schnell ändern kann.

Hier sind Input und Output im Detail gegenübergestellt. Wie Ihr sehen könnt, habe ich noch ein bisschen Text um den Plot gesetzt. Die Besonderheit ist, dass ihr in RMarkdown Code nicht nur innerhalb der Code-Chunks ausführen könnt, sondern kleine Code-Snippets auch innerhalb des Fließtextes. r nrow(crawl) zählt zum Beispiel die Zeilen des Crawls. Dadurch könnt ihr prinzipiell Text-Templates verwenden, innerhalb derer sich die Zahlen entsprechend der zugrundeliegenden Daten dynamisch ändern.

R plot notebook und output Gegenüberstellung

Abbildung 17: Gegenüberstellung des Plots im Notebook und im Output-Dokument

Interne Verlinkung in eine SQLite-Datenbank schreiben

Im obigen Plot sind nur acht URLs zu sehen, die mit 404 antworten. Trotzdem ist es natürlich immer von Interesse nachzusehen, woher diese URLs verlinkt werden – und vor allem mit welchem Ankertext. Denn ist dieser immer gleichlautend, kann man davon ausgehen, dass es sich um Template-Links handelt. Sie können also sehr schnell an einer Stelle behoben werden. Im Gegensatz zu Content-Links, die zumeist einzeln angepackt werden müssen. Ich möchte nun also die URLs des Crawls, die nicht erreichbar sind, mit den Link-gebenden Seiten aus dem all_inliks-Export des Screaming Frog zusammenführen.


Hinweis: Mir ist bewusst, dass Ihr auch einfach im all_inlinks-Export auf die 404er filtern könntet. Ich brauche hier aber ein Beispiel, um Euch einen Join aus einer Datenbank zu veranschaulichen. Bitte entschuldigt also den kleinen Umweg.


Als erstes müsst ihr natürlich die all_inlinks.csv importieren. Das Verfahren kennt Ihr bereits.

Der Report ist im vorliegenden Fall sehr klein.

Je nach Größe der Website kann der Report aber sehr schnell mehrere Millionen Zeilen und somit mehrere GigaByte groß sein. Da ich in meinen Analysen nur ad hoc auf ihn zurückgreife, möchte ich Ihn nicht die ganze Zeit im RAM vorhalten. Aus diesem Grund habe ich mir angewöhnt, ihn direkt in eine SQLite-Datenbank zu schreiben. Der große Komfortfaktor einer SQLite-Datenbank ist, dass Ihr – wie Ihr sofort sehen werdet – sie mit einer Zeile Code direkt auf Eurem Rechner initialisieren könnt. Ihr braucht also keinen Server, auf dem Ihr erst einmal eine Datenbank installieren müsst. Mit folgendem Code erstellt Ihr die Datenbank, wenn sie noch nicht vorhanden ist. Andernfalls verbindet Ihr Euch mit einer bestehenden.

Als nächstes schreibt Ihr den DataFrame all_inlinks in die Datenbank. Auch das ist erneut denkbar einfach. Gibt es die Tabelle inlinks noch nicht in der Datenbank, wird sie automatisch erzeugt. Außerdem definiert Ihr, auf welchen Spalten in der Datenbank ein Index gebildet werden soll. Mit rm() löscht Ihr den DataFrame wieder aus dem RAM.

Anschließend muss die gerade in der Datenbank erstellte Tabelle natürlich referenziert werden, um Anfragen gegen sie zu fahren. Auch das ist ganz einfach und erfolgt durch dir folgende Code-Zeile.

inlinks ist jetzt die Referenz. Referenz meint hier, dass hier wirklich nur eine Verbindung besteht. Es werden keine Daten nach R geladen. Das seht Ihr, wenn ihr die Referenz aufruft.

Source und Database zeigen an, dass es sich hier tatsächlich um eine reine Datenbank-Tabelle handelt. Dass keine Daten nach R geladen werden, seht Ihr auch am [?? x 9]. Die Datenbank teilt Euch zwar mit, dass die Tabelle 9 Spalten hat. Allerdings ist zum jetzigen Zeitpunkt – da keine explizite Anfrage gegen die Datenbank besteht – nicht ermittelbar, wie viele Zeilen (??) in der Tabelle vorliegen.

Dieses Verhalten geht sogar so weit, dass selbst die Formulierung einer Anfrage nicht ausgeführt wird, solange dies nicht explizit von Euch gewünscht ist.


Bitte entschuldigt, wenn das gerade etwas nerdig ist. Aber das ist wirklich ein wahnsinnig komfortables Verhalten, wenn Ihr mit sehr großen Datenmengen bei begrenzten RAM-Ressourcen arbeiten müsst.


Wie müsst Ihr euch dieses Verhalten konkret vorstellen? Im Folgenden definiere ich eine Abfrage gegen die Datenbank. Es soll durchgezählt werden, wie häufig eine Seite intern verlinkt wird. Anschließend wird die Tabelle absteigend nach der Link-Zahl sortiert und dann nur die URLs geladen, die mehr als 200 Inlinks haben. Die Abfrage an sich wird in die Variable top_linked_pages geschrieben. Ruft Ihr die Abfrage auf, seht Ihr im Kopf der Antwort, dass es sich um eine lazy query handelt. Die Abfrage ist also faul, sie gibt nur ein paar Beispiel-Zeilen zurück, kennt aber nicht die Gesamtmenge ([?? x 2]), solange Ihr nicht explizit angebt (collect()), dass die Abfrage ausgeführt werden soll.

Apropos Abfrage, Ihr seht kein SQL. Simple Abfragen können gänzlich mit R-Funktionen beschrieben werden, die dann automatisch in SQL übersetzt werden. Um Euch das SQL anzeigen zu lassen, könnt Ihr show_query() verwenden. Die SQL-Abfrage ist vielleicht nicht gerade elegant, erfüllt aber ihren Zweck.

Jetzt aber zurück zur Ermittlung der Verlinkung der 404er. Ihr habt jetzt alle internen Links in der Datenbank vorliegen und könnt Sie nun mit Eurem Crawl zusammenführen.

Die resultierende Tabelle könnt Ihr Euch direkt im Analyse-Dokument ausgeben lassen. Sie bietet einige interaktive Elemente, indem sie bspw. eine Pagination darstellt, die Ihr klicken könnt. Um hier eine Handlung für die IT zu generieren, könnt Ihr die Tabelle natürlich direkt als CSV exportieren. Es gibt auch Packages, um eine Excel zu erzeugen. Der Einfachheit halber belasse ich es an dieser Stelle aber bei einer CSV.

R Gegenüberstellung Notebook und Output

Abbildung 18: Gegenüberstellung der Tabelle im Notebook und im Output-Dokument

Delta-Betrachtung eines alten und des aktuellen Crawls

Aufschlussreich ist es auch immer, zu sehen, welche Änderungen sich im zeitlichen Verlauf an einer Website ergeben haben. Hier wollen wir exemplarisch betrachten, welche Ressourcen nicht mehr, weiterhin oder neu verlinkt werden. Weitere denkbare Betrachtungsobjekte sind Title- und Description-Änderungen sowie Status-Code-Wechsel. Der Einfachheit halber gehe ich an dieser Stelle aber nur auf die Ressourcen ein. Das zugrundeliegende Verfahren könnt Ihr aber adaptieren.

Zunächst brauchen wir eine Funktion, die uns einen Indikator bereitstellt, ob eine Ressource nur im alten, nur im aktuellen oder in beiden Crawls vorkommt. R bietet von Haus aus keine solche Funktion, daher müssen wir eine eigene schreiben. Sie ist nicht sonderlich komplex, aber lang. Daher erspare ich Euch die Definition.

Hinweis: Die Funktion habe ich übrigens nicht selbst geschrieben, sondern von Stack Overflow übernommen. Ein weiterer großer Vorteil von R resp. programmatischer Analyse im Allgemeinen. Man muss nicht ständig das Rad neu erfinden. Deutlich klügere / erfahrenere Menschen als wir es sind, standen mit Sicherheit schon einmal vor den gleichen Problemen und haben sich Rat im Internet gesucht. Stack Overflow ist damit die reinste Fundgrube, an der Ihr Euch scharmlos bedienen könnt und solltet.

Mit folgendem Code legt ihr die beiden Crawls übereinander und erhaltet als Resultat eine Tabelle mit Flag-Spalte.

R Delta Tabelle

Abbildung 19: Delta-Tabelle mit Flagspalten

Diese Tabelle gebt Ihr wiederum an eine Plotting-Funktion.

Anhand des resultierenden Plots können wir sehr leicht Veränderungen an den Mengengerüsten der Ressourcen erkennen. Hier sind offensichtlich verhältnismäßig viele HTML-, Bilder- und JS-Ressourcen entfallen. Ein genauer Blick in die Daten würde uns zeigen, dass die AMP-Variante der Website deaktiviert wurde.

R Beispiel Plot Crawlvergleich

Abbildung 20: Plot der Delta-Betrachtung zweier Crawls

Daten aus Google Analytics mittels API-Abfragen beschaffen

Fast geschafft! Zu guter Letzte möchte ich Euch noch zeigen, wie Ihr auch externe Datenquellen in Eure Analyse integrieren könnt. Exemplarisch möchte ich dies für Google-Analytics-Daten (GA) machen. Im Grunde kann aber jede Quelle, die eine API bereitstellt, hinzugezogen werden. Denkbar sind bspw. auch die Google Search Console oder Sistrix.


Hinweis: Und Ihr seid nicht einmal auf Datenquellen mit API beschränkt. Selbst Web Scraping könnt Ihr mit R verwirklichen. Allerdings muss ich zugeben, dass dazu Python deutlich komfortabler ist.


Für GA gibt es dankbarerweise ein Package – googleAnalyticsR. Vom gleichen Autor, Mark Edmondson, gibt es auch noch Packages für die Authentifizierung (googleAuthR), welches wir auch verwenden werden, und die Google Search Console (searchConsoleR).

Zunächst müsst Ihr natürlich die benötigten Packages laden.

Anschließend müsst Ihr über options() Eure API-Credentials angeben, die Ihr in der Google Cloud Platform generieren könnt, und dem Skript die Berechtigung für Euren Google-Account erteilen.

gar_auth() wird beim ersten Ausführen Euren Browser öffnen, in dem Ihr den Skript-Zugriff bestätigt. Dadurch wird eine Authentifizierungsdatei erzeugt. Fortan müsst Ihr diesen manuellen Schritt also nicht mehr durchführen.

Hinweis: Damit steht im Übrigen einer automatischen Report-Erstellung auch nichts mehr im Wege! Holt Euch wöchentlich die aktuellen Daten, schreibt sie in einer Datenbank und generiert einmal in der Woche mittels RMarkdown einen automatischen Report, den Ihr Euch per Mail zuschicken lasst.

Nachdem die Authentifizierung durchgeführt wurde, könnt Ihr Euch die GA-Properties anzeigen lassen, um die benötigte viewID der anzufragenden Datensicht zu ermitteln.

Die Abfrage gegen die API sieht dann wie folgt aus.

Ihr definiert den abzufragenden Berichtszeitraum sowie die Kombination aus Metriken und Dimensionen. Großer Vorteil des Packages ist, dass es, wenn Ihr anti_sample auf TRUE gesetzt habt, überprüft, ob in der API-Antwort ein Sampling vorhanden ist. Sollte dies der Fall sein, bricht das Package die Anfrage automatisch in möglichst granulare Anfragen herunter, um das Sampling zu umgehen.

Die GA-Daten liegen nun in R vor. Um sie mit den Crawl-Daten zusammenführen zu können, muss noch kurz die Domain an den pagePath geschrieben werden.

Wir wollen uns nun im Detail angucken, ob Seiten unserer Website, die viele Page Views erhalten – also aus Nutzersicht stark nachgefragt werden – intern auch stark verlinkt sind – wir sie somit ebenfalls als bedeutsam erachten. Das ist ein klassischer Abgleich der Eigen- und Außenwahrnehmung unserer Webpages. Dazu joint Ihr die beiden DataFrames crawl und ga_data.

Anschließend plotten wir wieder den DataFrame.

R Beispiel Plot interne Verlinkung

Abbildung 21: Eigen- vs. Fremdwahrnehmung

Fazit

Und damit sind wir wieder beim Plot, der diesen Beitrag eingeleitet hat. Ich hoffe, ich konnte Euch einen ersten „kleinen“ Einblick in die Potenziale der Daten-Analyse mittels R bieten. Wenn Ihr es bis hierin geschafft habt, habt Ihr bereits viel gesehen und gelernt. Ich habe Euch gezeigt, wie Ihr mittels RMarkdown reproduzierbare Analyse-Dokumente erstellen könnt, die jederzeit nachvollziehbar sind, da der Code erhalten bleibt und sich somit selbst dokumentiert. Wir sind die verschiedenen Phasen eines Analyse-Prozesses durchlaufen, vom Import der Daten, über die Aufbereitung und Anreicherung der Daten, hin zur Transformation und Visualisierung. Ihr habt mit janitor ein Package kennengelernt, mit dem Ihr Euch on-the-fly einen Einblick in die Datenbasis verschaffen könnt. Darüber hinaus habt Ihr das grundlegende Konzept der Datenvisualisierung in R mittels ggplot() gesehen. Ihr habt eine SQLite-Datenbank initialisiert, um darin die interne Verlinkung zu speichern, sodass Ihr sie nicht im RAM vorhalten müsst. Um der zeitlichen Dimension Rechnung zu tragen, haben wir zwei Crawls übereinandergelegt, um Unterschiede zu ermitteln. Zum Schluss habt Ihr erfahren, wie Ihr Externe Datenquellen wie die Google-Analytics-API anzapfen könnt, um Eure Crawl-Daten anzureichern.

Wenn Ihr Lust auf R bekommen habt, möchte ich Euch noch einmal das Buch R for Data Science von Hadley Wickham wärmstens ans Herz legen – und ja, ich habe schamlos, nein, in ehrfürchtiger Devoation den Titels seines Buches adaptiert. Mir ist bewusst, dass der Einstieg in R nicht gerade einfach ist. Insbesondere wenn man bisher keine Erfahrung mit Programmierung hat. Aber lasst Euch gesagt sein, die hatte ich am Anfang auch nicht. Und ich habe es nicht bereut, Zeit ins Erlernen zu investieren. Auf lange Sicht lohnt es sich wirklich. Das folgende Diagramm veranschaulicht das sehr schön. Am Anfang ist die Lernkurve für R sehr steil. Da seid Ihr mit Excel deutlich schneller. Je komplexer die Aufgaben jedoch werden, desto weniger schwierig wird ihre Lösung mit R, während bei Excel die Schwierigkeit signifikant zunimmt.

R4SEO Vergleich Excel und R

Abbildung 22: Schwierigkeit und Komplexität von Excel und R. https://blog.revolutionanalytics.com/2017/02/the-difference-between-r-and-excel.html

Um Euch bei Euren ersten Schritten nicht allein zu lassen, möchte ich Euch ermuntern, der Gruppe OmPyR auf Facebook beizutreten, die mein Kollege Johannes Kunze und ich gegründet haben. Wir möchten Daten-Analysen mittels R, Python, KNIME aber auch Excel stärker in der Online-Marketing-Community verankern. Dazu wollen wir eine Plattform für den Austausch etablieren – denn aller Anfang ist schwer! Wir hätten uns in unserer Anfangszeit eine solche Gruppe sehr gewünscht und möchten Euch jetzt die Möglichkeit bieten, von unseren – aber auch den Erfahrungen der Community – zu profitieren, um den Einstieg so einfach wie möglich zu gestalten.

Also, ich hoffe, man liest sich. Bis dahin, happy R! 👋

Patrick Lürwer: Javascript SEO: Pre- vs. Post-Rendering-Abgleich von Screaming-Frog-Crawls mit R

JavaScript SEO: Pre- vs. Post-Rendering-Abgleich von Screaming-Frog-Crawls mit R

tl;dr

  • Der Blogbeitrag beschreibt ein Framework in Form eines R-Skripts, das ich zum Analysieren von Screaming-Frog-Crawls verwende.
  • Dazu wird die entsprechende Website mit aktiviertem und deaktiviertem JavaScript-Rendering gecrawlt.
  • Die Crawls werden dann in R eingelesen und um einige Metriken angereichert:
      • Indexierbarkeit von Seiten
      • Segmentierung der URLs
    • Google-Analytics-Daten (hier: PageViews)
    • Google-Search-Appearance-Daten (hier: Impressions, Clicks)
    • Interner PageRank der URLs
  • Interne Links werden in eine SQLite-Datenbank geschrieben.
  • Der gerenderte und der nicht gerenderte Crawl werden verglichen:
    • Welche URLs kommen nur in einem der beiden Crawls vor?
    • Für URLs, die in beiden Crawls vorkommen, wird überprüft, ob sich bspw. die Titles & Descriptions, die Wortzahl und die Crawl-Tiefe ändern.
  • Im Rahmen der anschließenden Analyse werden Diagramme generiert, die einen ersten Überblick zur Beantwortung der folgenden Fragen geben:
    • Wie ist der Anteil der (nicht) indexierbaren Seiten?
    • Liegen meine wichtigen Seiten hinsichtlich der Klicktiefe auf einer der vorderen Ebenen?
    • Welche Unterschiede bestehen auf meiner Website, wenn JavaScript aktiviert resp. deaktiviert ist?
    • Welche Verzeichnisse weisen die meisten URLs auf?
    • Welche Segmente weisen die meisten URLs auf?
    • Sind meine wichtigsten Seiten auch aus Nutzersicht die wichtigsten?

Google Search Console & R: gsc_r

Google Search Console & R: Nicht relevante Crawling-Fehler automatisch beheben & löschen

Bei get:traction gehen wir üblicherweise so vor, dass wir bei Neukunden erst einmal einen Blick in die Google Search Console (GSC) werfen, um uns bspw. einen Überblick über die dort aufgeführten Crawling-Fehler zu verschaffen.

Insbesondere die 404-Fehler („Nicht gefunden“) sind hinsichtlich der Nutzer besonders unerfreulich, da sie keine gute User Experience bieten. Notfalls kann der Nutzer über eine gut gemachte 404-Seite auf der Website gehalten werden – dies sollte jedoch nicht die Regel sein. Daher ist es wichtig, für nicht mehr vorhandene Seiten Weiterleitungen zur neuen URL oder zu ähnlichen Inhalten zu definieren.

Hört sich erst einmal einfach an, kann aber sehr schnell in Sisyphos-Arbeit ausarten. Je nach Größe und Art der Kunden-Website können schnell mehrere Tausend URLs als „Nicht gefunden“ gelistet werden.

Die Problemstellung mit den GSC-404-Crawlingfehlern

GSC Crawlingfehler

Hier beginnt das Problem allein schon bei der Datenerhebung, denn über die GSC werden nur 1.000 URLs je Property angezeigt und zum Export bereitgestellt. Zusätzlich enthält der Export über das GSC-Dashboard nicht die linkgebenden Seiten der vermeintlichen 404-Fehler. Aber auch schon bei einigen Hundert bemängelten URLs kann der zeitliche Aufwand erheblich sein, denn im Grunde muss jede URL einzeln angefasst werden.

Nicht jede gelistete URL muss zwangsläufig weitergeleitet werden. So kann die Übersicht URLs enthalten, die mittlerweile wieder mit 200 OK antworten oder bereits weitergeleitet werden. Oder es werden URLs gelistet, die gar nicht mehr verlinkt werden – weder intern noch von anderen Websites. Bei Ersterem wird dem Nutzer wieder die Seite oder eine ähnliche Seite angezeigt. Bei Letzterem gelangt der Nutzer gar nicht mehr auf diese Seite.

Nur solche URLs, die den HTTP-Statuscode 404 erzeugen und verlinkt sind, sind für mich also von besonderem Interesse. Allein für diese möchte ich herausfinden, warum die Seite nicht gefunden wird und ggf. Weiterleitungen definieren oder interne Links abbauen. Bis ich aber an diese URLs herankomme, muss viel stupide Fleißarbeit geleistet werden.

  • Workflow 404 Crawlingfehler Google Search Console GSC(1) Eine URL auswählen und überprüfen, ob sie noch mit 404 antwortet.
  • Wenn ja, (2) lasse ich mir die verlinkenden Seiten anzeigen und
  • rufe diese (3) einzeln auf, um zu überprüfen, ob hier der Link noch vorhanden ist.

Bei 100 nicht gefundenen URLs und bis zu 10 verlinkenden URLs sind das dann schon 1.100 URLs, die ich betrachten muss, bis ich mit der richtigen Arbeit beginnen kann. Da wäre es natürlich toll, wenn mir zumindest die Fleißarbeit irgendwie abgenommen werden könnte.

GSC-Crawlingfehler (404) automatisiert bewerten

Glücklicherweise lassen sich die meisten Aufgaben, die man mit „stupide“ und „Fleißarbeit“ beschreiben kann, automatisieren.

R to the rescue!

R ist eine statistische Programmiersprache, die wir bei get:traction unter anderem für die Analyse von Crawls und Logfiles sowie die Auswertung von Google Analytics-Daten verwenden. In Kombination mit der searchConsoleR-Library von Mark Edmondson lässt sich ein Programm schreiben, dass uns die zuvor beschriebene Arbeit abnimmt. Über die Library können die Crawling-Fehler über die GSC-API abgefragt und als behoben markiert werden.

Das Programm soll hier nicht im Detail beschrieben (der Code findet sich hier auf GitHub), sondern der Ablauf kurz skizziert werden.

Ablauf automatisierte GSC 404 Crawlingfehler Bewertung

  1. Beim ersten Ausführen muss dem Skript der Zugriff auf das jeweilige GSC-Konto eingeräumt werden. Die Authentifizierung erfolgt aktuell noch über den Browser, soll aber zukünftig über einen Google Service-Account erfolgen, damit das lästige Hantieren mit den OAuth-Dateien entfällt.
  2. Ist der Account verifiziert, lädt das Skript alle verifizierten Properties herunter, von der eine ausgewählt werden kann.
  3. Für diese Property werden dann alle URLs heruntergeladen, die als „not found“ bemängelt werden. Dies erfolgt sowohl für die Plattform Web als auch Smartphone.
  4. Im Anschluss beginnt der erste Teil der „Fleißarbeit“. Für alle not-found-URLs wird der Statuscode erhoben, um zu überprüfen, ob sie immer noch mit 404 antworten. URLs, die mittlerweile wieder mit 200 OK oder 301 Redirect antworten, werden in eine separate Tabelle geschrieben, um sie später über die API als behoben zu markieren. Wir erinnern uns: Ist die URL wieder erreichbar oder wird weitergeleitet, brauche ich natürlich keine Weiterleitung definieren. Sie ist für mich also nicht von Interesse und kann als behoben markiert werden. Die restlichen URLs, die weiterhin mit 404 antworten, werden weiterverarbeitet.
  5. Für diese URLs werden nun alle verlinkenden URLs ebenfalls auf ihren Statuscode überprüft. Ist der Statuscode bei einer verlinkenden URL 4xx, wird die verlinkte URL in die zuvor genannte Tabelle geschrieben, um sie als behoben zu markieren. Hier greift also die Prämisse, dass URLs, die zwar mit 404 antworten, aber nicht mehr verlinkt werden, aussortiert werden sollen, da der Nutzer sie sowieso nicht aufrufen kann. Folglich brauchen auch keine Weiterleitungen definiert werden. Alle bemängelten URLs, deren verlinkenden URLs jedoch mit 200 OK antworten, werden weiterverarbeitet.
  6. Bisher wurde von den (vermeintlich) Link-gebenden URLs nur der Statuscode ermittelt. In diesem Schritt wird nun auch der HTML-Quellcode der jeweiligen Seite heruntergeladen, um zu gucken, ob die bemängelte URL weiterhin verlinkt wird. Findet sich der Link nicht mehr im Quellcode, werden auch diese URLs in die Tabelle geschrieben, um sie in der GSC als behoben zu markieren.
  7. Zum Schluss liegen damit zwei Tabellen vor: 1) Eine Tabelle mit den „echten“ Crawling-Fehlern, also solche URLs, die immer noch mit 404 antworten und verlinkt werden. Sie werden als CSV exportiert, um sie händisch prüfen zu können. 2) Eine Tabelle mit den URLs, die über die API als behoben markiert werden können, weil die URLs wieder mit 200 antworten, weitergeleitet werden oder nicht mehr verlinkt werden.

Fazit: Google Crawlingfehler schnell automatisiert bewerten und bearbeiten

Mit ein bisschen R-Magie können wir uns das Leben deutlich einfacher machen. Auf Knopfdruck bereinigen wir die GSC-Auflistung, sodass nur noch die Fehler angezeigt werden, die wirklich relevant sind und unserer Aufmerksamkeit bedürfen. Auch das Problem großer Websites mit (hundert-)tausenden von Crawling-Fehlern lässt sich damit begegnen. Wird das Skript ausgeführt, wird mit Sicherheit ein Großteil der 1.000 URLs als behoben markiert. Dadurch können neue URLs nachrücken, die wir dann durch ein erneutes Ausführen des Skripts weiter reduzieren, und so weiter.

Gerade bei der initialen Auswertung eines neuen Kundenaccounts befreien wir uns dadurch von unnötigem Ballast und können uns schnell auf das Wesentliche konzentrieren. Aber auch im laufenden Betrieb — bspw. wöchentlich — hilft die Automatisierung dabei, Ordnung zu halten.

Hier noch einmal der Link auf das GitHub-Repro.


[Update: 16.10.2017] Anleitung

Erst einmal vielen Dank für das rege Interesse an meinem Skript. Gerne reiche ich natürlich eine kurze Anleitung nach, wie es überhaupt ausgeführt wird.

Installation von R und RStudio

Unter folgedem Link kann R heruntergeladen werden: https://cran.rstudio.com/index.html.

Wählt das für Euch passende OS aus; im Nachfolgenden werde ich die Installation für Windows beschreiben.

Installation für Windows

Am besten verwendet Ihr die base -Variante.

base-Variante

Ladet die R-3.4.2-win.exe herunter und führt sie aus.

R.-3.4.2-win.exe herunterladen

Nachdem Ihr dem Installationsdialog gefolgt seid, ist R im Verzeichnis C:\Program Files\R (oder dem von Euch gewählten Verzeichnis) installiert. Aktuell kann R jedoch nur auf der Kommandozeile ausgeführt werden. Deutlich mehr Komfort bietet daher eine IDE wie RStudio, die unter folgendem Link kostenlos heruntergeladen werden kann: https://www.rstudio.com/products/rstudio/download/#download. Auch hier ladet ihr euch den passenden Installer (RStudio-1.1.383.exe) herunter, führt ihn aus und folgt dem Installationsdialog.

Installationsdialog im Installer ausführen

Danach findet sich im Verzeichnis C:\Program Files\RStudio\ die RStudio-Installation und im Unterverzeichnis /bin die rstudio.exe zum Starten den Programms.

RStudio öffnen, ein neues Projekt anlegen und das Skript importieren

Nachdem Ihr RStudio gestartet habt, kurz noch einige Anmerkungen zum Aufbau von RStudio.

Aufbau von RStudio

Direkt vorweg: Bei mir sieht die GUI etwas anders aus, als bei einer Erstinstallation, da ich sie meinen Bedürfnissen entsprechend angepasst habe. So sind meine Panels (mehr dazu gleich) anders angeordnet und ich verwende ein schwarzes Theme. An der grundlegenden Funktionalität ändert das natürlich nichts.

RStudio ist in vier Panels aufgeteilt, die verschiedene Aufgaben übernehmen:

  1. Der Filemanager listet alle Dateien des aktuellen Projekts (auf Projekte komme ich gleich zu sprechen) oder wenn kein Projekt ausgewählt ist, die Ordner der einzelnen Projekte.
  2. Der Editor dient dazu, Skripte zu schreiben und zu speichern.
  3. Das Enviroment-Tab listet alle Variablen eines Projekts und ermöglicht es so, sich die Daten genauer anzusehen.
  4. Die Console dient zum Ausführen von Skripten.

Als Erstes legen wir ein neues Projekt an, in dem wir das Skript speichern können. Dazu wählt Ihr in der Navigationsleiste File → New Project… → New Directory → New Project. Vergebt einen Projektnamen wie bspw. gsc_crawling_erros und wählt als Speicherort ein Verzeichnis aus. Ich speichere meine Projekte standardmäßig in der Dropbox, sodass ich E:/Dropbox/Rstudio als Verzeichnis angeben, in dem RStudio dann einen Ordner gsc_crawling_errors erzeugt. Habt Ihr das Projekt erstellt, wechselt RStudio automatisch in das Verzeichnis, dass zunächst nur die Projektdatei gsc_crawling_errors.Rproj enthält. Anschließend erzeugt ihr ein neus R-Skript. Drückt dazu STRG + SHIFT + N oder geht über die Navigationsleiste wie im Screenshot zu sehen.

R-Script erzeugen

Es öffnet sich das Editor-Panel mit dem neuen Skript, das aktuell natürlich leer ist. Drückt als erstes STRG + S, um das Skript zu speichern und vergebt einen Dateibennenung wie bspw. gsc_crawling_errors.R.

Als Nächstes kopiert ihr das R-Skript von GitHub und fügt es in die gerade erstellte, leere Datei in RStudio ein. Euer RStudio sollte dann in etwa wie folgt aussehen.

RStudio nachdem ihr das R-Skript von GitHub eingefügt habt

Installation der benötigten Packages

Am Anfang des Skripts seht ihr einige Libraries, die ich importiere, um sie im Skript zu verwenden. Da Ihr R resp. RStudio komplett neu installiert habt, müsst ihr diese zunächst herunterladen. Führt dazu in der Konsole (bei mir rechts unten im Bild) folgenden Befehl aus:

Variablen definieren

Als Nächstes müsst ihr einige Variablen im Skript anpassen. Das macht Ihr an folgender Stelle:

  • Die Variable new_auth belasst Ihr zunächst auf TRUE. Sie dient dazu die initiale Authentifizierung durchzuführen (dazu nachher mehr).
  • Die Variable path_to_csv_export ändert Ihr zu einem beliebigen Pfad mit Dateiangabe. Dort wird das Skript die 404-Fehler als CSV speichern, die ihr euch genauer angucken müsst, da sie vom Skript als „echte“ 404er ermittelt wurden. Ich möchte die Datei bspw. auf meinem Desktop speichern, also trage ich ein: C:/Users/Patrick/Desktop/404_export.csv. (Hinweis: Nicht wundern, Pfadangaben in R/Rstudio müssen unter Windows nicht mit einem Backslash angegeben werden.)
  • Die client_id und client_secret müsst Ihr wahrscheinlich erst einmal erzeugen. Das machen wir jetzt.

Google API Client-ID erzeugen und Google Search Console API aktivieren

Ruft dazu folgende URL auf und meldet euch mit eurem Google-Account an : https://console.developers.google.com/apis/credentials.

Navigiert zu Credentials → Create credentials → OAuth client ID.

Google API Client-ID erzeugen und Google Search Console API aktivieren

Ihr erhaltet den Hinweis, dass Ihr zunächst einen Consent Screen konfigurieren müsst. Diese Einwilligung wird euch nachher angezeigt, wenn Ihr Euch mit dem Skript authentifizieren wollt. Klick also auf: Configure consent screen.

Credentials

Es reicht aus, wenn Ihr den Product name ausfüllt und dann auf Save klickt. Danach gelangt Ihr wieder auf den Auswahl-Screen für den Application type. Wählt hier Other, vergebt einen Namen und drückt auf Create.

Namen vergeben

In einem Fenster werden Euch dann Eure Zugangsdaten angezeigt, die Ihr entsprechend in die Variablen des Skripts eintragt.

Variablen des Scripts eintragen

Zu guter Letzt müsst Ihr die Search Console API an sich noch aktivieren. Wechselt dazu links in der Navigation auf Library und sucht nach Google Search Console API, wählt sie aus und aktiviert sie.

Google Search Console API aktivieren

Hinweis: Die Aktivierung kann einige Minuten dauern. In der Zwischenzeit führe ich Euch weiter durchs Skript.

Das Skript ausführen

Bisher haben wir nur Werte ins Skript eingetragen, aber noch nichts ausgeführt. Um einzelne Zeilen oder Abschnitte eines Skripts auszuführen, können diese mit der Maus markiert und mittels STRG + ENTER ausgeführt werden. Als erstes führt Ihr die Zeilen 1 bis 65 aus; also bis zum und inklusive folgenden Skript-Abschnitt, der für die Authentifizierung zuständig ist.

Wird dieser Abschnitt ausgeführt, öffent sich Euer Browser und Ihr müsst die Authentifizierung mit dem Google-Account zulassen, für den Ihr die GSC-Fehler abfragen wollt (Sagt „Hallo“ zu eurem vorher erstellten Consent Screen).

SEO Monitoring: Zulassen anklicken

Habt Ihr auf Zulassen geklickt, seht Ihr im Filemanager von RStudio, dass eine .httr-oauth-Datei erzeugt wurde. Damit Ihr euch nicht jedes Mal neu authentifizieren müsst, setzt die Variable new_auth am Anfang des Skripts jetzt auf FALSE. Zukünftig wird das Skript die Datei dann verwenden, um sich gegenüber der API zu authentifizieren. Wollt Ihr einen anderen Google-Account authentifizieren, setzt die Variable wieder auf TRUE und führt die Schritte bis hierher erneut aus.

Als Nächstes führt Ihr dann folgende Zeile aus:

Dadurch erhaltet Ihr alle Properties, die für den Google-Account freigegeben sind. Klickt Ihr im Enviroment-Panel auf die Variable properties, werden sie Euch in tabellarischer Form angezeigt.

Variable properties in tabellarischer Form

Wählt eine der Properties aus und tragt sie in die nachfolgende Variable property ein.

Wie bereits oben erwähnt, kann die Aktivierung der GSC-API einige Minuten dauern. Wenn Ihr also zu schnell gelesen habt und folgende Fehlermeldung seht, macht genau das, was dort steht und holt euch erst einmal einen ☕. Nach ca. 5 Minuten, sollte die Aktivierung erfolgt sein.

Fehlermeldung

Das war’s erst einmal an manuellen Einstellungen. Führt jetzt die restlichen Zeilen des Skripts aus.

Einige (nerdige) Anmerkungen

Direkt vorweg ein Disclaimer: Wer schon einmal Websites gescrapt oder auch nur URLs automatisiert abgefragt hat, weiß, dass jedes Skript früher oder später knallt, weil nicht alle Eventualitäten im Vorfeld bedacht werden können … oder weil Google wieder mal an seiner API herumbastelt oder was auch immer. Ich prognostiziere also schon einmal, dass das Skript früher oder später nicht mehr in der gewünschten Form funktionieren wird. Da wir es aber bei get:traction selbst verwenden, werde ich es entsprechend aktualisieren und den neuen Code auf GitHub einstellen.

User-Agent

Über diese Konfiguration könnt Ihr einen anderen User-Agent definieren. Standardmäßig verwendet das httr-Package keinen User-Agent, was einige Websites nicht mögen und dann keinen Inhalt zurückgeben.

Error-Handling

Es kann immer mal wieder vorkommen, dass URLs nicht wie erwartet antworten (ノಠ益ಠ)ノ彡┻━┻ s. Disclaimer). Damit das Skript nicht abbricht, fange ich solche Errors mit einem simplen tryCatch, der dann einfach ein NA zurückgibt, die URL also „hinten runter fallen lässt“. In der Console wird aber zumindest die URL ausgegeben, bei der das Problem aufgetreten ist.

URLs als behoben markieren

Aktuell kommt es immer wieder zu folgender Fehlermeldung, wenn URLs als behoben markiert werden sollen:

Ursache dafür ist, dass die Funktion fix_sample_url() nicht mit URL-encodierten Zeichen (bspw. wie in folgender URL: podcast/beyond-pageviews-podcast/bp-2-3-warum-google-tag-manager/%20%E2%80%8E) umgehen kann. An einem Fix arbeite ich.

Außerdem kann es vorkommen, dass URLs nicht direkt aus der GSC verschwinden, wenn Ihr sie als behoben markiert habt. Um zu testen, ob der API-Call überhaupt funktionert, könnt Ihr ihn mit der Funktion fix_sample_url() testen. Tragt dazu eine URL aus der GSC ein. Wenn die Funktion mit TRUE antwortet, hat das Senden an die API auf jeden Fall funktioniert. Sollte das nicht geklappt haben, kann es sein, dass Ihr nur einen lesenden Zugriff auf die Property habt.

Fin?

Wie Ihr seht, gibt es an dem Skript noch einiges zu tun. Denn wie Tolstoi schon so schön gesagt hat:

— Alle glücklichen URLs antworten gleich, jede unglückliche URL antwortet auf ihre eigene Weise unglücklich.

Für Anregungen und Probleme, über die Ihr gestolpert seid, oder sogar Verbesserungsvorschläge bin ich daher immer offen.

Bis dahin: Happy R! 👋

Hier noch einmal der Link auf das GitHub-Repro.

Kontaktieren Sie uns & profitieren Sie von mehr Traktion durch SEO!

SEO Berlin – [sichtbar & erfolgreich]
Wielandstraße 9, 12159 Berlin
030 / 296 73 998
berlin@gettraction.de

SEO Darmstadt- [sichtbar & erfolgreich]
Heinrich-Hertz-Straße 6, 64295 Darmstadt
06151 / 860 69 85
darmstadt@gettraction.de

5 Gründe für get:traction:

  • individuelle SEO-Beratung & -Betreuung
  • keine Abnahmeverpflichtung
  • zertifizierte SEO-Experten
  • transparente & faire Arbeitsweise
  • nachhaltige SEO-Strategien