Beim Umstieg von WordPress zu einer statischen Website mit Publii bleiben oft alte Gutenberg-Kommentare und veraltete Bildpfade in der SQLite-Datenbank zurück. Diese Schritt-für-Schritt-Anleitung zeigt, wie du die Datenbank von Altlasten befreist und gleichzeitig fehlende SEO-Metadaten automatisiert generierst.

1. Datenbank-Architektur von Publii

Publii nutzt eine schlanke SQLite-Datenbank (db.sqlite). Für Beiträge sind im Wesentlichen zwei Tabellen relevant:

  • posts: Enthält die Hauptdaten wie ID, Titel, Slug und den eigentlichen Artikeltext (text).
  • posts_additional_data: Verwaltet erweiterte Einstellungen und SEO-Daten. Diese werden als JSON-Objekt im Feld value unter dem Schlüssel key = '_core' gespeichert.

2. WordPress-Gutenberg-Blocks & Altlasten entfernen

WordPress speichert Block-Informationen als HTML-Kommentare (z. B. <!-- wp:paragraph -->). Zudem verweisen alte Bilder meist noch auf Verzeichnisse wie /wp-content/uploads/.

Gutenberg-Kommentare entfernen

Mit folgendem Regulären Ausdruck (RegEx) lassen sich die Block-Tags aus dem Beitragsfeld posts.text herausfiltern:

import re

# Entfernt alle Gutenberg-Block-Kommentare
cleaned_text = re.sub(r'<!--\s*/?wp:[^>]*-->', '', raw_text)

Medienpfade anpassen

Alte absolute WordPress-Bildpfade (https://domain.de/wp-content/uploads/...) werden entweder durch relative Pfade oder die interne Medienstruktur von Publii (media/posts/ID/...) ersetzt.

3. SEO-Metadaten automatisch generieren

Damit Suchmaschinen die Inhalte optimal erfassen, liest Publii SEO-Titel und Meta-Beschreibungen aus dem JSON-Objekt der Tabelle posts_additional_data aus. Der entscheidende Schlüssel für die Meta-Beschreibung lautet hierbei metaDesc (nicht metaDescription).

Das folgende Python-Skript erstellt ein Backup, liest den Text jedes Beitrags aus, bereinigt ihn von HTML-Tags, kürzt ihn sauber am letzten Wort auf 155 Zeichen und schreibt das Ergebnis zurück in das JSON-Feld:

import sqlite3
import json
import re
import html
import shutil
import datetime

db_path = r'C:\Pfad\zu\deiner\Publii\input\db.sqlite'

# 1. Automatisches Sicherheits-Backup
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
shutil.copyfile(db_path, f'{db_path}.backup_{timestamp}')

conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# 2. Beiträge verarbeiten
cursor.execute("SELECT id, title, text FROM posts")
posts = cursor.fetchall()

for post_id, title, text in posts:
    # HTML-Tags entfernen & Text säubern
    clean_text = re.sub(r'<[^>]+>', ' ', text or '')
    clean_text = re.sub(r'\s+', ' ', html.unescape(clean_text)).strip()
    
    # Auf max. 155 Zeichen kürzen
    if len(clean_text) > 155:
        truncated = clean_text[:152]
        meta_desc = truncated.rsplit(' ', 1)[0] + '...' if ' ' in truncated else truncated
    else:
        meta_desc = clean_text or title

    # JSON-Struktur abrufen / erstellen
    cursor.execute("SELECT value FROM posts_additional_data WHERE post_id = ? AND key = '_core'", (post_id,))
    row = cursor.fetchone()
    core_data = json.loads(row[0]) if (row and row[0]) else {}

    # SEO-Keys setzen (Wichtig: "metaDesc")
    core_data['metaTitle'] = title.strip() if title else ''
    core_data['metaDesc'] = meta_desc

    json_val = json.dumps(core_data, ensure_ascii=False)

    if row:
        cursor.execute("UPDATE posts_additional_data SET value = ? WHERE post_id = ? AND key = '_core'", (json_val, post_id))
    else:
        cursor.execute("INSERT INTO posts_additional_data (post_id, key, value) VALUES (?, '_core', ?)", (post_id, json_val))

conn.commit()
conn.close()

4. Wichtige Fallstricke & Best Practices

Achtung: Bevor Datenbanänderungen durchgeführt werden, müssen sowohl die Publii-App als auch etwaige SQLite-Viewer (wie der DB Browser) vollständig geschlossen sein.

  • Prozess-Sperre: SQLite-Datenbanken werden bei Schreibzugriffen für andere Programme gesperrt. Schließe Publii vor der Ausführung von Skripten.
  • Arbeitsspeicher-Cache: Publii lädt die Datenbank beim Programmstart in den RAM. Wenn Änderungen bei geöffnetem Publii durchgeführt werden, können sie beim Speichern innerhalb der App überschrieben werden.
  • Site Rendern: Nach der Datenbankbereinigung muss in Publii einmalig der Button „Render site“ geklickt werden. Erst dadurch schreibt Publii die neuen Werte aus der Datenbank in den <head>-Bereich der generierten HTML-Dateien.