Shopify-Texte per Admin-API pflegen: Ablauf, Fallen, Prüfung
Wer mehr als eine Handvoll Shopify-Texte ändern muss, sollte den Editor zulassen und ein Skript schreiben. Nicht wegen der Geschwindigkeit, sondern wegen der Prüfbarkeit. Hier ist der Ablauf, den wir benutzen, und die Fallen, in die wir dabei getreten sind.
Der Shopify-Editor ist gut für eine Änderung. Bei fünfundzwanzig wird er zur Fehlerquelle: Man kopiert, scrollt, speichert, und ob am Ende alles so im Shop steht wie gedacht, prüft niemand. Für den Wärmeleisten-Shop wandwarm.de mit 27 Produkten, 14 Seiten und 7 Kategorien haben wir deshalb den Weg über die Admin-API genommen. Dieser Beitrag beschreibt den Ablauf so, dass man ihn nachbauen kann, und die Stellen, an denen es gehakt hat. Die Fallstudie dazu steht im Beitrag Shopify-SEO per Admin-API: die Fallstudie wandwarm.de.
Schritt 1: alles sichern
Bevor irgendetwas geändert wird, eine Abfrage über alle Objekte: Produkte mit descriptionHtml, seo und Handle, Seiten mit body, Kategorien mit descriptionHtml, Artikel mit body, dazu Weiterleitungen und Menüs. Das Ergebnis landet als eine JSON-Datei mit Datum im Projekt. Sie ist drei Dinge zugleich: Sicherung für den Rückweg, Eingabe für alle Änderungen und später der Beleg, was vorher da stand. Als am dritten Tag die SEO-Titel von neun Produkten verschwunden waren, kamen die alten Werte aus genau dieser Datei.
Schritt 2: Änderungen als Ersetzungen mit Zählprüfung
Das Skript nimmt den gesicherten Text und wendet Ersetzungen an. Jede Ersetzung nennt die alte Stelle, die neue Stelle und die erwartete Trefferzahl. Kommt die alte Stelle null- oder zweimal vor, bricht das Skript mit Fehler ab. Das klingt pedantisch und hat uns zweimal gerettet: einmal, weil ein Satz in zwei Produkttexten stand und nur in einem geändert werden sollte, einmal, weil der Ausgangstext sich gegenüber der Sicherung geändert hatte.
Dazu kommt eine Sperrliste. Bei unserem Kunden waren das Gesundheitsaussagen, Wörter wie „beseitigt“ im Zusammenhang mit Schimmel, Superlative und Prozentangaben ohne Quelle. Jeder fertige Text läuft gegen diese Liste, bevor er geschrieben wird. Neue Texte aus anderen Quellen, etwa von einem Autor, werden genauso geprüft.
Schritt 3: Dateien statt Direktupload
Das Skript schreibt jeden fertigen Text zweimal: als lesbare HTML-Datei zum Gegenlesen und als JSON-Zeichenkette für die Mutation. In der JSON-Fassung werden alle unsichtbaren Zeichen als \u-Sequenzen geschrieben. Alte Importe stecken voller weicher Trennstriche und geschützter Leerzeichen; wer die im Klartext kopiert, verliert sie oder schleppt sie mit, und beides sieht man nicht.
Der Upload geht dann Objekt für Objekt: pageUpdate mit body, productUpdate mit descriptionHtml, collectionUpdate mit descriptionHtml, articleUpdate mit body. Nicht parallel. Zwei gleichzeitige Seitenänderungen endeten bei uns reproduzierbar mit einem 500er, nacheinander liefen sie durch.
Schritt 4: Live vergleichen
Nach dem Upload holt ein zweiter Lauf die Live-Texte: /pages/handle.json für Seiten, /products.json?limit=250 für Produkte, /collections.json für Kategorien. Vor dem Vergleich wird Leerraum normalisiert, weil Shopify nach Listenpunkten eigene Zeilenumbrüche einfügt. Alles andere muss zeichengleich sein. Blogartikel stehen nicht als JSON bereit, dort hilft der Atom-Feed des Blogs, dessen Inhalt in einem CDATA-Block liegt.
Dieser Vergleich hat das gefunden, was kein Mensch beim Gegenlesen sieht: ein kyrillisches „д“ statt eines „d“ in „weggedämmt“, in einem Text von 16.000 Zeichen.
Metadaten: zwei Wege, eine Falle
Produkte und Kategorien haben ein Eingabefeld „seo“ mit title und description. Seiten, Blogs und Artikel haben das nicht, dort laufen Titel und Beschreibung über die Metafelder global.title_tag und global.description_tag (Typ single_line_text_field), die man mit metafieldsSet in einem Aufruf für bis zu 25 Objekte setzt.
Die Falle sitzt beim Feld „seo“: Es wird als Ganzes übernommen. Wer nur die Beschreibung schickt, löscht den Titel. Wir haben das bei neun Produkten und drei Kategorien erst in der Gegenprüfung gesehen. Seitdem gilt: title und description immer zusammen senden, auch wenn sich nur eines ändert.
Eine Kategorie aus dem Index zu nehmen geht ohne Theme über das Metafeld seo.hidden mit dem Wert 1. Shopify setzt dann noindex und nimmt die Adresse aus der Sitemap.
Weitere Fallen
- Bot-Schutz: Der Storefront beantwortet Abrufe von außen nach wenigen Anfragen mit 403 oder 429. Für Prüfläufe über die ganze Sitemap haben wir den Abruf in den Browser verlegt, aus einem offenen Tab des Shops heraus, mit einer Sekunde Pause je Adresse.
- CDN-Cache: Wer eine Datei im Shopify-Dateibereich ersetzt, bekommt unter der alten Adresse noch die alte Fassung. Die Adresse mit Versionsparameter aus GenericFile.url liefert die neue.
- Handles umbenennen: productUpdate und collectionUpdate kennen redirectNewHandle, articleUpdate ebenfalls. Die Weiterleitung legt Shopify an, die internen Links in anderen Texten nicht. Vorher mit der Sicherung suchen, wo die alte Adresse verlinkt ist.
- Massenänderungen: bulkOperationRunMutation wäre der richtige Weg für hundert Objekte. In unserem Zugang war die Mutation gesperrt, also blieb es bei Einzelaufrufen mit Aliasen, neun bis zwölf je Anfrage.
- Bilder-Alt-Texte: gehören zur Datei, nicht zum Produkt. fileUpdate mit einer Liste aus id und alt setzt 33 Alt-Texte in einem Aufruf. Vorher die Bilder in klein herunterladen und ansehen, aus Dateinamen wie „E48CCC4C.jpg“ lässt sich kein Alt-Text schreiben.
- Startseiten-Titel: Titel und Meta-Beschreibung der Startseite stehen in den Onlineshop-Konfigurationen und haben keine API. Das bleibt ein Klick im Admin.
Was das Ganze kostet
Das Skript für die erste Runde hatte etwa 200 Zeilen und war in zwei Stunden geschrieben, die Texte selbst waren der weit größere Teil der Arbeit. Dafür ließ sich jede Runde danach in Minuten wiederholen und prüfen. Wer regelmäßig Shopify-Texte pflegt, sollte das einmal bauen und dann behalten. Für die Pflege von WordPress-Seiten verfolgen wir denselben Gedanken, nachzulesen im Beitrag zum WordPress-Wartungsvertrag: Was nicht geprüft ist, ist eine Annahme.
Häufige Fragen
Kann ich in Shopify per API nur einen Satz in einem Text ändern?
Nein. Seiten, Produkte, Kategorien und Artikel nehmen den Text nur als Ganzes an (body, descriptionHtml). Man liest den aktuellen Text, ändert ihn lokal und schickt ihn komplett zurück. Deshalb lohnt sich ein Skript, das die Ersetzung prüft, bevor irgendetwas hochgeht.
Löscht Shopify den SEO-Titel, wenn ich nur die Meta-Beschreibung setze?
Ja, bei productUpdate und collectionUpdate. Das Eingabefeld „seo“ wird als Ganzes übernommen; ein nicht mitgeschicktes Feld wird leer. Also immer title und description zusammen senden, auch wenn sich nur eines ändert. Bei Seiten, Blogs und Artikeln laufen Titel und Beschreibung über die Metafelder global.title_tag und global.description_tag, dort passiert das nicht.
Wie prüfe ich, ob der Text wirklich live ist?
Jeder Shopify-Storefront liefert die öffentlichen Inhalte auch als JSON: /pages/handle.json, /products.json und /collections.json. Ein Skript vergleicht diese Fassung mit dem Soll, nach dem Normalisieren von Leerraum, weil Shopify nach Listenpunkten eigene Umbrüche einfügt. Blogartikel gibt es als Atom-Feed unter /blogs/handle.atom.