Volltextsuche für eine statische Website
Neulich wollten wir auf unserer eigenen Seite nachschauen, wo wir schon über PPS und PPSU geschrieben haben – und haben gemerkt: Es gibt keine Suche. Bei inzwischen rund 160 Seiten Knowledgebase, Projekten und Blog ist das ein echtes Manko. Seit heute gibt es oben rechts in der Navigation ein Suchfeld. Hier steht, wie wir das gelöst haben.
Das Problem: keine Datenbank, kein Server
Unsere Website ist statisch. Ein selbst geschriebener Generator (ssg2) baut aus Markdown-Dateien fertige HTML-Seiten, die dann einfach auf den Webserver kopiert werden. Es gibt keine Datenbank und kein Programm, das auf dem Server läuft. Genau das macht die Seite schnell, sicher und billig – aber eine Suche, wie man sie von Wordpress & Co. kennt, fällt damit weg. Die fragt bei jeder Eingabe die Datenbank.
Die Möglichkeiten
| Ansatz | Vorteil | Warum nicht |
|---|---|---|
| Google-Suchfeld einbetten | in fünf Minuten erledigt | lädt Google-Skripte und Werbung, übermittelt Suchanfragen an Google – passt nicht zu unserer Datenschutzerklärung („keine externen Ressourcen“) |
| Suchdienst als Cloud-Service (Algolia u. ä.) | sehr komfortabel | externer Dienst, Konto, Kosten, wieder Daten bei Dritten |
| Eigenes Suchprogramm auf dem Server | volle Kontrolle | widerspricht dem ganzen Konzept einer statischen Seite |
| Suchindex im Browser (lunr.js, Fuse.js) | läuft ohne Server | der komplette Index muss bei jeder Suche geladen werden – bei 160 Seiten mehrere Megabyte |
| Pagefind | läuft ohne Server, lädt nur die nötigen Teile des Index | ✅ unsere Wahl |
Was Pagefind anders macht
Pagefind läuft einmal nach dem Bau der Website über alle HTML-Seiten und
schreibt einen Suchindex als lauter kleine Dateien in einen Ordner pagefind/. Dieser Ordner wird
mit der Website hochgeladen, mehr braucht es auf dem Server nicht.
Im Browser passiert dann Folgendes:
- Beim ersten Tastendruck lädt Pagefind ein kleines Programm (als WebAssembly) und eine Übersicht des Index.
- Für jedes Suchwort holt es nur den Teil des Index, in dem dieses Wort steht – nicht den ganzen.
- Für die Treffer lädt es kurze Textausschnitte und zeigt sie mit hervorgehobenem Suchwort an.
Bei unseren 156 indexierten Seiten ist der gesamte Index rund 2 MB groß. Eine typische Suche lädt
davon aber nur einen kleinen Bruchteil. Und weil die Seiten auf Deutsch sind (<html lang="de">),
nutzt Pagefind deutsche Wortstämme: Wer „Kondensatoren“ sucht, findet auch „Kondensator“.
Datenschutz: Alles liegt auf unserem eigenen Server. Es wird nichts an Dritte geschickt, es gibt keine Cookies und kein Tracking der Suchanfragen.
So haben wir es eingebaut
1. Pagefind installieren
Pagefind gibt es als Python-Paket, das das eigentliche Programm gleich mitbringt. Das passt gut, weil unser Generator ohnehin in Python geschrieben ist:
pip install "pagefind[extended]"
2. Nur echte Inhalte indexieren
Eine Suche, die Entwürfe, halbfertige Seiten oder bei jedem Treffer die Navigation mitliefert, nützt
niemandem. Pagefind kennt dafür ein Attribut: Sobald irgendeine Seite einen Bereich mit
data-pagefind-body markiert, indexiert es nur noch markierte Seiten und davon nur diesen
Bereich.
Unser Generator weiß ohnehin, welche Seiten veröffentlicht sind – er braucht diese Liste schon für die Sitemap und den RSS-Feed. Nach dem Bauen setzt er deshalb auf genau diesen Seiten das Attribut an den Hauptinhalt:
def mark_search_body(pages):
"""Setzt data-pagefind-body auf das erste <main> jeder Seite."""
for page in pages:
text = page.read_text(encoding="utf-8")
if "data-pagefind-body" in text:
continue
text = re.sub(r"<main(\s|>)", r"<main data-pagefind-body\1", text, count=1)
page.write_text(text, encoding="utf-8")
Entwürfe, Platzhalter-Seiten („in Arbeit“) und Seiten mit noindex bekommen das Attribut nicht und
landen damit auch nicht im Suchindex. Navigation und Fußzeile liegen außerhalb von <main> und
bleiben automatisch draußen. Die Seitenleiste „Neueste Beiträge“ steht zwar in <main>, ist aber
mit data-pagefind-ignore ausgenommen – sonst wäre jeder Artikel ein Treffer für die Titel der
neuesten Beiträge.
3. Pagefind nach dem Bauen aufrufen
Danach ruft der Generator Pagefind auf:
subprocess.run([sys.executable, "-m", "pagefind", "--site", str(output_dir)])
Eingeschaltet wird das pro Website mit einer Zeile in der Konfiguration (search: pagefind). Unsere
zweite Seite, kleinemoba.de, baut also unverändert ohne Suche, bis wir es dort auch wollen.
4. Suchseite und Suchfeld
Pagefind bringt eine fertige Oberfläche mit. Die Suchseite besteht im Kern aus wenigen Zeilen:
<link href="/pagefind/pagefind-ui.css" rel="stylesheet">
<div id="suche"></div>
<script src="/pagefind/pagefind-ui.js"></script>
<script>
const ui = new PagefindUI({ element: "#suche", showSubResults: true });
const q = new URLSearchParams(location.search).get("q");
if (q) ui.triggerSearch(q);
</script>
Das Suchfeld in der Navigationsleiste ist ein ganz normales Formular, das auf diese Seite verweist
(/suche.html?q=…). Die letzten beiden Zeilen im Skript übernehmen den Suchbegriff aus der Adresse
und starten die Suche sofort.
Was es kostet
- Geld: nichts. Pagefind ist Open Source.
- Bauzeit: ein paar Sekunden mehr pro Build.
- Speicher: rund 2 MB zusätzliche Dateien auf dem Server.
- Pflege: keine. Der Index wird bei jedem Build automatisch neu erzeugt.
Ausprobieren
Oben rechts im Suchfeld zum Beispiel PPS, Hall-Sensor oder NE555 eingeben. Und falls etwas nicht gefunden wird, das eigentlich da sein müsste: Schreibt uns, dann sehen wir uns das an.