Skip to content

Technik Blog

Programmieren | Arduino | ESP32 | MicroPython | Python | Raspberry Pi | Raspberry Pi Pico

Menu
  • Smarthome
  • Arduino
  • ESP32 & Co.
  • Raspberry Pi & Pico
  • Solo Mining
  • Über mich
  • Deutsch
  • English
Menu

WordPress aufräumen: So entlarvst du ungenutzte Dateien mit Python

Posted on 27. Mai 202527. Mai 2025 by Stefan Draeger

In diesem Beitrag möchte ich dir gerne einen Weg aufzeigen, wie du sehr einfach und ebenso sicher nicht verwendete Dateien in deiner WordPress-Instanz finden kannst. Du benötigst dafür ein wenig technisches Verständnis – aber keine Sorge: Ich nehme dich an die Hand und führe dich Schritt für Schritt durch den gesamten Prozess.

WordPress aufräumen: So entlarvst du ungenutzte Dateien mit Python
Dieses Video auf YouTube ansehen.

Der Bedarf für diese Lösung entstand aus einem ganz praktischen Grund: Mein eigener Blog hat mittlerweile satte 174.000 Dateien angesammelt – da wird es höchste Zeit, etwas auszumisten und wieder Platz auf dem Server zu schaffen.

Für das Finden von nicht benötigten Dateien unter WordPress gibt es diverse Plugins. Diese haben jedoch alle den Nachteil, dass sie direkt auf der WordPress-Instanz laufen, Systemressourcen verbrauchen und – wie viele Kommentare zeigen – nicht immer zuverlässig oder sicher funktionieren. Daher habe ich mir folgenden Weg überlegt:

Ich exportiere die WordPress-Beiträge als JSON-Datei mit den wichtigsten Feldern (ID, Titel, Inhalt). Anschließend lade ich das Upload-Verzeichnis herunter und lasse ein Python-Skript prüfen, ob die Dateien in den Beiträgen referenziert werden. Alle nicht gefundenen Dateien können theoretisch als ungenutzt eingestuft und anschließend überprüft oder gelöscht werden.

⚠️ Wichtiger Hinweis:
Ich empfehle dir dringend, vor dem Löschen von Dateien ein vollständiges Backup deiner WordPress-Instanz anzulegen. Idealerweise testest du die Löschung zunächst in einer Staging-Umgebung oder auf einer Kopie deiner Seite. In seltenen Fällen kann es vorkommen, dass Dateien fälschlich als ungenutzt erkannt werden – insbesondere wenn sie über PageBuilder, Custom Fields oder Medien-IDs referenziert werden.

Inhaltsverzeichnis

  • Warum bestehende Plugins nicht immer zuverlässig arbeiten
  • Schritt 1 – Export der Tabelle wp_posts als JSON
  • Schritt 2 – Download des Upload-Verzeichnisses
  • Schritt 3 – Ausführen des Python-Skripts zur Suche nach verwaisten Bildern
  • Schritt 4 – Aufräumen der WordPress-Datenbank
  • Aufräumen via SSH
    • Optional: Dry-Run vor dem Löschen
    • Dateien sicher löschen (nach manueller Prüfung)
    • Optional: Löschvorgang protokollieren
  • Fazit

Warum bestehende Plugins nicht immer zuverlässig arbeiten

Es gibt diverse Plugins wie Media Cleaner, DNUI (Delete Not Used Image) oder ähnliche, die versprechen, ungenutzte Mediendateien automatisch zu erkennen und zu löschen. Grundsätzlich eine praktische Idee – doch in den Kommentaren dieser Plugins häufen sich Berichte über falsch gelöschte Dateien, die offenbar doch noch in Verwendung waren.

Die Wiederherstellung solcher Dateien ist oft aufwendig und erfordert entweder Backups oder manuelle Nacharbeit.

Meine Lösung setzt daher auf einen anderen Ansatz:
Sie analysiert die Inhalte kontrolliert und nachvollziehbar außerhalb des WordPress-Systems – und ist damit deutlich sicherer und transparenter.

Schritt 1 – Export der Tabelle wp_posts als JSON

Als Erstes werden die Spalten ID, post_title und post_content aus der WordPress-Tabelle wp_posts exportiert. Der Tabellenpräfix wp_ kann bei dir abweichen – diesen findest du in der Datei wp-config.php unter:

$table_prefix = 'wp_';

In manchen Fällen sind mehrere WordPress-Instanzen in einer Datenbank vorhanden. Achte also darauf, den richtigen Präfix zu verwenden.

Der Export über phpMyAdmin ist dabei besonders einfach: Du führst lediglich ein SELECT-Statement aus, das dir die relevanten Inhalte liefert. Der eigentliche Export als JSON erfolgt über die integrierte Exportfunktion.

SQL-Statement:

SELECT 
  ID as "id",
  post_title as 'Titel',
  post_content as 'Content'
FROM wp_posts
WHERE post_type in ('page', 'post');

Wenn du dieses SQL-Statement in phpMyAdmin ausführst, erscheint eine Tabelle mit den Ergebnissen. Scrolle nun ganz nach unten – dort findest du den Link „Exportieren“. Ein Klick darauf öffnet eine neue Seite, auf der du den Exporttyp von SQL auf JSON umstellst. Danach nur noch mit OK bestätigen, und der Export startet.

Je nach Anzahl der Beiträge kann der Download ein paar Sekunden dauern.

Schritt 2 – Download des Upload-Verzeichnisses

Im zweiten Schritt wird das komplette Upload-Verzeichnis deiner WordPress-Installation heruntergeladen. Dieses befindet sich standardmäßig unter:

/wp-content/uploads/

Ich verwende dafür das kostenlose Tool WinSCP, das eine einfache und übersichtliche Oberfläche bietet. Du benötigst lediglich die SFTP-Zugangsdaten zu deinem Webspace. Diese findest du in der Regel im Kundenbereich deines Hosting-Anbieters.

Falls dir kein direkter Zugriff per SFTP möglich ist, bieten viele Hoster eine alternative Lösung an: Du kannst das Verzeichnis online als ZIP-Archiv erstellen und anschließend herunterladen.

💡 Tipp: Achte darauf, die Ordnerstruktur beizubehalten – das Python-Skript analysiert später jede Datei im Originalpfad.

Schritt 3 – Ausführen des Python-Skripts zur Suche nach verwaisten Bildern

Für das Skript wird lediglich Python 3 benötigt – weitere externe Bibliotheken sind nicht erforderlich.
Das Skript kann direkt über die Kommandozeile im Projektverzeichnis ausgeführt werden:

python3 findunusedfiles.py

Während der Ausführung listet das Skript alle Bilder aus dem Upload-Verzeichnis auf und zeigt direkt in der Konsole an, ob sie verwendet (✅) oder nicht verwendet (❌) wurden.
Die Laufzeitausgabe sieht zum Beispiel so aus:

Nach Abschluss wird eine Datei namens unused_images.txt erzeugt.
Darin enthalten ist eine Liste aller Bildpfade, die im Export der WordPress-Datenbank (siehe Schritt 1) nicht referenziert wurden.

Diese Datei dient als Grundlage zur manuellen Prüfung oder zum späteren Löschen der nicht verwendeten Dateien.

🔗 Hinweis: Das hier verwendete Skript findunusedfiles.py sowie ein Beispiel-SQL-Generator findest du ebenfalls auf GitHub:
github.com/StefanDraeger/wp-unused-files-cleaner

Python Skript zur Suche nach nicht benutzen Bildern in wordPressHerunterladen
Quellcode
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Titel: WordPress Upload-Verzeichnis auf ungenutzte Dateien prüfen

Beschreibung:
Dieses Skript durchsucht das lokale Upload-Verzeichnis einer WordPress-Installation
nach Bilddateien und vergleicht diese mit den Inhalten aller Beiträge und Seiten,
die zuvor als JSON-Datei aus der Tabelle `wp_posts` exportiert wurden.
Dateien, die in keinem Beitrag referenziert sind, werden als potenziell ungenutzt gelistet.

Autor: Stefan Draeger
Webseite: https://draeger-it.blog
"""

import os
import json
import sys

# Konfiguration
UPLOADS_DIR = './wp-content/uploads'
JSON_FILE = './wp_posts.json'
IMAGE_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.tiff', '.bmp')


def format_size(bytes_value):
    if bytes_value < 1024:
        return f"{bytes_value} Bytes"
    elif bytes_value < 1024 ** 2:
        return f"{bytes_value / 1024:.2f} KB"
    elif bytes_value < 1024 ** 3:
        return f"{bytes_value / (1024 ** 2):.2f} MB"
    else:
        return f"{bytes_value / (1024 ** 3):.2f} GB"


def load_json_data(json_file):
    if not os.path.isfile(json_file):
        print(f"❌ Fehler: Datei '{json_file}' nicht gefunden.")
        sys.exit(1)
    try:
        with open(json_file, 'r', encoding='utf-8') as file:
            raw_json = json.load(file)
    except json.JSONDecodeError:
        print(f"❌ Fehler: Datei '{json_file}' ist kein gültiges JSON.")
        sys.exit(1)

    for obj in raw_json:
        if obj.get('type') == 'table' and obj.get('name') == 'wp_posts':
            return obj.get('data', [])
    print("❌ Fehler: Keine Daten zur Tabelle 'wp_posts' gefunden.")
    sys.exit(1)


def scan_uploads(content_list):
    if not os.path.isdir(UPLOADS_DIR):
        print(f"❌ Fehler: Upload-Verzeichnis '{UPLOADS_DIR}' nicht gefunden.")
        sys.exit(1)

    unused_files = []
    checked_count = 0

    for root, dirs, files in os.walk(UPLOADS_DIR):
        for file_name in files:
            if file_name.lower().endswith(IMAGE_EXTENSIONS):
                full_path = os.path.join(root, file_name)
                relative_path = os.path.relpath(full_path, '.').replace('\\', '/')
                checked_count += 1
                found = False

                for i in range(len(content_list)):
                    if relative_path in content_list[i]:
                        content_list[i] = content_list[i].replace(relative_path, '')
                        found = True

                if found:
                    content_list = [c for c in content_list if 'wp-content/uploads/' in c]
                    print(f"✅ Verwendet:         {relative_path}")
                else:
                    unused_files.append(relative_path)
                    print(f"❌ Nicht verwendet:   {relative_path}")
    return unused_files, checked_count


def write_unused_list(unused_files):
    with open('unused_images.txt', 'w', encoding='utf-8') as f:
        for path in unused_files:
            f.write(path + '\n')
    print("📝 Datei 'unused_images.txt' wurde erstellt.")


def write_sql_files(unused_files):
    with open('delete_attachments.sql', 'w', encoding='utf-8') as del_out:
        del_out.write("-- SQL-Befehl zum Löschen verwaister Medien aus wp_posts (Typ: attachment)\n\n")
        del_out.write("DELETE FROM wp_posts\nWHERE post_type = 'attachment'\nAND guid IN (\n")
        for i, path in enumerate(unused_files):
            end = ",\n" if i < len(unused_files) - 1 else "\n"
            del_out.write(f"    '{path}'{end}")
        del_out.write(");\n")
    print("🗑️  Datei 'delete_attachments.sql' wurde erzeugt.")

    with open('select_attachments.sql', 'w', encoding='utf-8') as sel_out:
        sel_out.write("-- SQL-Befehl zur Prüfung verwaister Medien aus wp_posts (Typ: attachment)\n\n")
        sel_out.write("SELECT ID, guid FROM wp_posts\nWHERE post_type = 'attachment'\nAND guid IN (\n")
        for i, path in enumerate(unused_files):
            end = ",\n" if i < len(unused_files) - 1 else "\n"
            sel_out.write(f"    '{path}'{end}")
        sel_out.write(");\n")
    print("🔍 Datei 'select_attachments.sql' wurde erzeugt.")


def write_log(unused_files, checked_count):
    total_size_bytes = sum(
        os.path.getsize(os.path.join('.', path)) for path in unused_files if os.path.isfile(os.path.join('.', path))
    )
    formatted_size = format_size(total_size_bytes)
    with open('cleanup_log.txt', 'w', encoding='utf-8') as log:
        log.write("📄 Ausführungsprotokoll – WordPress Dateiaufräumung\n")
        log.write("==================================================\n\n")
        log.write(f"📦 Verarbeitete Dateien:       {checked_count}\n")
        log.write(f"🗂️  Ungenutzte Dateien gefunden: {len(unused_files)}\n")
        log.write(f"💾 Speicherverbrauch (gesamt):  {formatted_size}\n\n")
        log.write("📝 Die folgenden Dateien wurden erstellt:\n")
        log.write("- unused_images.txt\n")
        log.write("- delete_attachments.sql\n")
        log.write("- select_attachments.sql\n")
    print("🧾 Logdatei 'cleanup_log.txt' wurde erstellt.")


def main():
    entries = load_json_data(JSON_FILE)
    content_list = [entry.get('Content', '') for entry in entries]
    unused_files, checked_count = scan_uploads(content_list)

    print("\nAnalyse abgeschlossen.")
    print(f"Geprüfte Dateien: {checked_count}")
    print(f"Nicht referenzierte Dateien: {len(unused_files)}\n")

    if unused_files:
        print("⚠️ Nicht referenzierte Bilddateien:")
        for path in unused_files:
            print(path)
        write_unused_list(unused_files)
        write_sql_files(unused_files)
        write_log(unused_files, checked_count)


if __name__ == "__main__":
    main()

Schritt 4 – Aufräumen der WordPress-Datenbank

⚠️ Wichtiger Hinweis vorab:
Bevor du Änderungen an deiner Datenbank vornimmst, solltest du ein vollständiges Backup deiner WordPress-Datenbank erstellen. So kannst du im Fall eines Fehlers jederzeit alles wiederherstellen.

WordPress speichert alle hochgeladenen Medien – also auch Bilder – in der Tabelle wp_posts. Dabei handelt es sich um Einträge mit dem Post-Typ attachment, wobei in der Spalte guid der Pfad zur Datei gespeichert ist.

Wenn du mit dem Python-Skript verwaiste Bilder identifiziert und vielleicht schon gelöscht hast, verbleiben deren Einträge trotzdem in der WordPress-Mediathek. Diese kannst du gezielt aus der Datenbank entfernen.

Vorgehen:

  • Öffne die Datei unused_images.txt, die du im vorherigen Schritt erhalten hast.
  • Jeder darin aufgeführte Pfad (z. B. wp-content/uploads/2023/08/beispiel.jpg) lässt sich mit einem einfachen SQL-Befehl entfernen:
DELETE FROM wp_posts
WHERE post_type = 'attachment'
AND guid LIKE '%wp-content/uploads/2023/08/beispiel.jpg';
  • Um diese Aufgabe bei vielen Dateien zu automatisieren, kannst du folgendes Python-Skript nutzen. Es erstellt dir aus unused_images.txt eine vollständige SQL-Datei mit allen nötigen Löschbefehlen:
# generate_delete_sql.py

with open('unused_images.txt', 'r', encoding='utf-8') as infile, open('delete_attachments.sql', 'w', encoding='utf-8') as outfile:
    outfile.write("-- SQL-Befehle zum Löschen verwaister Medien aus wp_posts (Typ: attachment)\n\n")
    for line in infile:
        path = line.strip()
        if path:
            outfile.write(f"DELETE FROM wp_posts WHERE post_type = 'attachment' AND guid LIKE '%{path}';\n")
  • Führe die generierte Datei delete_attachments.sql anschließend in phpMyAdmin oder über ein externes SQL-Tool aus.

Optional: Erst prüfen, dann löschen

Wenn du sicherstellen willst, dass die Statements korrekt sind, kannst du sie vor dem Löschen zunächst mit SELECT testen:

SELECT ID, guid FROM wp_posts
WHERE post_type = 'attachment'
AND guid LIKE '%wp-content/uploads/2023/08/beispiel.jpg';

Mit diesem Schritt entfernst du nicht nur die Bilddateien selbst, sondern auch die dazugehörigen Mediathek-Einträge – für eine wirklich aufgeräumte WordPress-Installation.

Aufräumen via SSH

⚠️ Wichtiger Sicherheitshinweis: Bevor du Dateien vom Server löschst, solltest du unbedingt ein vollständiges Backup deiner WordPress-Installation und der Datenbank anlegen. In seltenen Fällen können Dateien fälschlich als ungenutzt erkannt werden – z. B. wenn sie über PageBuilder, Shortcodes oder Custom Fields eingebunden sind.
Teste den Löschvorgang idealerweise zuerst in einer Staging-Umgebung.

Wenn du Zugriff auf deinen Webserver per SSH hast, kannst du die ungenutzten Dateien aus der Datei unused_images.txt direkt automatisiert löschen – ohne mühsames Durchklicken per Hand.

Voraussetzungen:

  • Die Datei unused_images.txt befindet sich auf dem Server (z. B. via WinSCP oder scp hochgeladen).
  • Du befindest dich im WordPress-Stammverzeichnis oder die Pfade in der Datei stimmen relativ zur aktuellen Position.

Optional: Dry-Run vor dem Löschen

Damit du vorab prüfen kannst, ob die Dateien wirklich existieren:

while IFS= read -r file; do
 if [ -f "$file" ]; then
  echo "Würde löschen: $file"
 else
  echo "❌ Nicht gefunden: $file"
 fi
done < unused_images.txt

Dateien sicher löschen (nach manueller Prüfung)

while IFS= read -r file; do
  if [ -f "$file" ]; then
    rm "$file"
    echo "✅ Gelöscht: $file"
  fi
done < unused_images.txt

Optional: Löschvorgang protokollieren

while IFS= read -r file; do
  if [ -f "$file" ]; then
    rm "$file"
    echo "$(date +"%F %T") Gelöscht: $file" >> deleted_files.log
  fi
done < unused_images.txt

Fazit

Ein überfülltes Upload-Verzeichnis ist nicht nur unübersichtlich, sondern kann auch unnötig Speicherplatz und Backup-Zeit kosten. Mit dem hier vorgestellten Ansatz hast du eine sichere, transparente und Plugin-freie Lösung, um gezielt ungenutzte Dateien in deiner WordPress-Installation zu identifizieren und aufzuräumen.

Durch die Kombination aus Datenbank-Export, Python-Skript und optionaler Löschung via SSH behältst du die volle Kontrolle – und vermeidest die Risiken automatischer Plugin-Löschungen.
Zudem hast du mit den generierten SQL-Dateien und dem Log eine solide Dokumentation deines Bereinigungsprozesses.

💡 Tipp zum Schluss: Behalte die Anzahl deiner Medien im Blick, deaktiviere nicht benötigte Thumbnails und führe diese Art von Aufräumaktion regelmäßig durch – so bleibt dein System langfristig sauber und performant.

Schreibe einen Kommentar Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Fragen oder Feedback?

Du hast eine Idee, brauchst Hilfe oder möchtest Feedback loswerden?
Support-Ticket erstellen

Newsletter abonnieren

Bleib auf dem Laufenden: Erhalte regelmäßig Updates zu neuen Projekten, Tutorials und Tipps rund um Arduino, ESP32 und mehr – direkt in dein Postfach.

Jetzt Newsletter abonnieren

Unterstütze meinen Blog

Wenn dir meine Inhalte gefallen, freue ich mich über deine Unterstützung auf Tipeee.
So hilfst du mit, den Blog am Leben zu halten und neue Beiträge zu ermöglichen.

draeger-it.blog auf Tipeee unterstützen

Vielen Dank für deinen Support!
– Stefan Draeger

Kategorien

Tools

  • Unix-Zeitstempel-Rechner
  • ASCII Tabelle
  • Spannung, Strom, Widerstand und Leistung berechnen
  • Widerstandsrechner
  • 8×8 LED Matrix Tool
  • 8×16 LED Matrix Modul von Keyestudio
  • 16×16 LED Matrix – Generator

Links

Blogverzeichnis Bloggerei.de TopBlogs.de das Original - Blogverzeichnis | Blog Top Liste Blogverzeichnis trusted-blogs.com

Stefan Draeger
Königsberger Str. 13
38364 Schöningen

Tel.: 01778501273
E-Mail: info@draeger-it.blog

Folge mir auf

  • Impressum
  • Datenschutzerklärung
  • Disclaimer
  • Cookie-Richtlinie (EU)
©2025 Technik Blog | Built using WordPress and Responsive Blogily theme by Superb
Cookie-Zustimmung verwalten
Wir verwenden Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wir tun dies, um das Surferlebnis zu verbessern und um personalisierte Werbung anzuzeigen. Wenn Sie diesen Technologien zustimmen, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn Sie Ihre Zustimmung nicht erteilen oder zurückziehen, können bestimmte Funktionen beeinträchtigt werden.
Funktional Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt. Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.
Optionen verwalten Dienste verwalten Verwalten von {vendor_count}-Lieferanten Lese mehr über diese Zwecke
Einstellungen anzeigen
{title} {title} {title}