💡 Voll funktionsfähiges Beispiel auf GitHub verfügbar:
document-version-metadata-diff-python

Was Sie erstellen

In diesem Leitfaden vergleichen Sie jede Metadaten‑Eigenschaft zwischen zwei Versionen eines Dokuments und geben exakt aus, was hinzugefügt, entfernt oder geändert wurde. Ein Metadaten‑Versions‑Diff ist ein eigenschafts‑level Vergleich von zwei Revisionen einer Datei und erkennt Signale, die ein Text‑Vergleich nie sieht: ein neuer Creator, eine erhöhte RevisionNumber, eine Bearbeitungssitzung, die nach dem Schließen der Überprüfung protokolliert wurde. Am Ende haben Sie eine funktionierende Lösung plus zwei fokussierte Detektoren und zwei Exportformate, alles aus einem ausführbaren Repository mit einem Beispiel‑Revisions‑Paar.

Skill level: intermediate Python‑Entwickler
What you need: Python 3, pip und zwei Revisionen eines Dokuments

Mein erster Durchlauf dieses Skripts meldete eine Änderung des Company‑Werts, an die sich niemand im Team erinnerte; diese eine Zeile zahlte die Einrichtung. Alles unten ist copy‑paste‑bereit und umfasst deutlich weniger als hundert Zeilen.

Die Pipeline ist bewusst langweilig: zwei Datei‑Öffnungen, drei Dict‑Comprehensions, eine Print‑Schleife. Langweilig ist der Punkt. Versions‑Streitigkeiten werden entschieden, ob die Methode erklärt und wiederholt werden kann, und ein Skript dieser Größe kann vollständig von jedem gelesen werden, der das Ergebnis hinterfragt.


1. Installation

pip install groupdocs-metadata-net==26.5

Das companion repository pinnt diese Version und liefert document-v1.docx und document-v2.docx, sodass der untenstehende Code wie angegeben läuft. Pinnen Sie die Version, mit der Ihr Audit ausgeführt wurde; Reproduzierbarkeit ist Teil des Beweises.


2. Der Kerncode

Lesen Sie beide Eigenschafts‑Bäume und klassifizieren Sie dann das Delta mit Set‑Logik. Das ist der gesamte Diff:

# Flatten a file's complete property tree into a dict
def read_props(path):
    props = {}
    with Metadata(path) as metadata:
        for p in metadata.find_properties(lambda p: p.name is not None):
            props[p.name] = (str(p.interpreted_value) if p.interpreted_value is not None
                             else (str(p.value) if p.value is not None else ""))
    return props

v1 = read_props("resources/document-v1.docx")
v2 = read_props("resources/document-v2.docx")

# Classify every key; changed entries keep both values
added = {k: v for k, v in v2.items() if k not in v1}
removed = {k: v for k, v in v1.items() if k not in v2}
changed = {k: (v1[k], v2[k]) for k in v1 if k in v2 and v1[k] != v2[k]}

print(f"added={len(added)} removed={len(removed)} changed={len(changed)}")
for k, (old_v, new_v) in changed.items():
    print(f"  {k}: {old_v} -> {new_v}")

Das ist das Minimum, das Sie benötigen. Erwarten Sie kleine Zahlen bei echten Revisions‑Paaren; ein Delta im zweistelligen Bereich bedeutet meist, dass die Datei einer Vorlagen‑Änderung oder einer Speicher‑Migration unterzogen wurde. Die nächsten Abschnitte erklären die Schlüssel‑Aufrufe und zeigen die häufigsten Anpassungen, die Teams zuerst hinzufügen.


3. Wie es funktioniert

  • Metadata: der Context‑Manager, der eine Datei öffnet und beim Verlassen freigibt; eine Instanz pro Revision.
  • find_properties: durchläuft eingebaute Felder, benutzerdefinierte Eigenschaften und XMP in einem Durchlauf und gibt alles zurück, was das Prädikat akzeptiert.
  • interpreted_value: die menschenlesbare Form einer Eigenschaft; die Bevorzugung führt dazu, dass Daten und Aufzählungen als Strings verglichen werden, die Sie in einem Bericht ausgeben können.
  • Qualified names as keys: eingebaute und benutzerdefinierte Felder können im Dict nicht kollidieren, sodass die Set‑Logik sicher bleibt.

Hier wird nichts an DOCX‑Strukturen geparst. Die product documentation listet über 170 Formate hinter demselben Aufruf, sodass das identische Skript PDF‑ oder XLSX‑Paare diffen kann.

Eine weitere Eigenschaft des Designs, die es wert ist, genannt zu werden: Die API‑Grenze endet bei den beiden read_props‑Aufrufen. Alles danach ist reines Python‑Standard‑Library, sodass Unit‑Tests, Schwellenwerte und Alarm‑Regeln nie die Dokument‑Ebene berühren. Teams, die dies in einen Service einbetten, cachen normalerweise die extrahierten Dicts pro Revision und lassen jede nachgelagerte Prüfung sie wiederverwenden, wodurch pro Version nur ein Datei‑Öffnen nötig ist, egal wie viele Fragen gestellt werden.


4. Häufige Anpassungen

Nur Eigentumsänderungen erkennen

Wenn die Frage lautet „Wer hat diese Datei berührt?“, filtern Sie zur Lesezeit mit Tag‑Prädikaten statt das vollständige Diff nachträglich zu filtern:

# Identity fields only, whatever the format calls them
def read_ownership(path):
    result = {}
    with Metadata(path) as metadata:
        props = metadata.find_properties(lambda p:
            Tags.person.creator in list(p.tags)
            or Tags.person.editor in list(p.tags)
            or Tags.person.manager in list(p.tags)
            or Tags.corporate.company in list(p.tags))
        for prop in props:
            result[prop.name] = (str(prop.interpreted_value)
                                 if prop.interpreted_value is not None
                                 else (str(prop.value) if prop.value is not None else ""))
    return result

Führen Sie dieselbe Delta‑Schleife über zwei dieser Dicts aus und verwenden Sie <missing> als Standard, sodass ein Feld, das verschwunden ist, trotzdem auftaucht. Das Prädikat benennt kein Feld, was es einem Detektor ermöglicht, jedes vom Library gelesene Format zu bedienen.

Den Bearbeitungszeitplan verfolgen

Ersetzen Sie das Prädikat durch Tags.time plus Namens‑Regeln und der Detektor meldet RevisionNumber, TotalEditingTime und LastPrinted‑Bewegungen:

props = metadata.find_properties(lambda p:
    Tags.time.modified in list(p.tags)
    or Tags.time.created in list(p.tags)
    or Tags.time.printed in list(p.tags)
    or (p.name is not None and ("Revision" in p.name
        or "EditTime" in p.name or "EditingTime" in p.name)))

Einen Prüfungsbericht exportieren

Ergebnisse, die nur in der Konsole bleiben, sterben dort. Vier Spalten decken das Spreadsheet und den SIEM‑Fall ab:

with open("output/diff.csv", "w", encoding="utf-8", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["change_type", "property", "old_value", "new_value"])
    for k, v in added.items():
        writer.writerow(["added", k, "", v])
    for k, v in removed.items():
        writer.writerow(["removed", k, v, ""])
    for k, (old_v, new_v) in changed.items():
        writer.writerow(["changed", k, old_v, new_v])

Das Repository enthält außerdem einen JSON‑Exporter mit einem stabilen Drei‑Map‑Schema für Dashboards und Case‑Management‑APIs.


5. Wo das in der Praxis läuft

Drei Einsatzszenarien tauchen immer wieder auf. Intake‑Pipelines diffen jedes eingehende Dokument gegen die bereits im System vorhandene Kopie und quarantänen Paare mit Identitäts‑Änderungen. Compliance‑Jobs führen den Diff nach einem Zeitplan aus und archivieren das CSV pro Paar, wodurch eine Eigenschafts‑Zeitlinie entsteht, die später niemand mehr rekonstruieren muss. Und Streit‑Tools führen beide Detektoren auf Abruf aus, weil bei einer Forderung die Einstiegsfrage immer „Wer hat die Datei berührt und wann?“ lautet, nicht „Was hat sich im vierten Absatz geändert?“.

Ein viertes Muster, das Diffen einer Datei gegen ihren letzten bekannten guten Snapshot, nutzt denselben Code mit einem gespeicherten Dict auf einer Seite. In allen Fällen ist die Export‑Datei das Ergebnis; Konsolenausgaben sind nur Fortschritts‑Rauschen. Das Exit‑Code‑Verhalten des Skripts folgt dem main.py des Repositories, sodass Scheduler und CI einen fehlgeschlagenen Assert als fehlgeschlagenen Lauf behandeln, ohne zusätzlichen Aufwand. Keines dieser Szenarien benötigte mehr Code als das, was diese Seite zeigt.


Was zählt als eine Änderung, die es wert ist, markiert zu werden?

Alles, was der Diff klassifiziert, plus den von Ihnen hinzugefügten Kontext. Hinzugefügte und entfernte Eigenschaften sind immer einen Blick wert, weil sie bedeuten, dass die Struktur geändert wurde, nicht nur ein Wert. Für geänderte Einträge alarmieren die meisten Teams zuerst die Identitäts‑ und Revisions‑Gruppen und behandeln den Rest als informativ. Die Detektoren existieren, damit der erste Durchlauf nur einen Funktionsaufruf kostet.


5. Schnellreferenz: Schlüsselaufrufe

Aufruf Was es tut
Metadata(path) Öffnet die Datei; Context‑Manager übernimmt das Freigeben
find_properties(predicate) Gibt jede Eigenschaft zurück, die das Prädikat akzeptiert, über alle Ebenen hinweg
p.interpreted_value Menschenlesbarer Wert; greift auf p.value zurück, falls nicht vorhanden
Tags.person.* / Tags.corporate.company Identitäts‑Klassifizierung, format‑unabhängig
Tags.time.* Zeitstempel‑Klassifizierung für den Revisions‑Detektor

Siehe die complete API reference für die vollständige Such‑ und Tag‑Oberfläche. Der Tag‑Wortschatz ist umfangreicher als diese Zeilen; Herkunfts‑, Inhalts‑ und Rechts‑Tag‑Gruppen folgen demselben Mitgliedschaftstest.


6. Häufige Probleme & Lösungen

Der Diff ist riesig und wirkt wie Rauschen
→ Die beiden Pfade sind wahrscheinlich keine Revisionen derselben Datei. Fix: Herkunft prüfen, bevor Sie diffen; unzusammenhängende Dateien erzeugen sinnlose Deltas.

Ein bekanntes Autorenfeld erscheint nie im Eigentumsdetektor
→ Einige Produzenten speichern Identität in nicht getaggten benutzerdefinierten Feldern. Fix: Führen Sie einmal den vollständigen Diff aus, finden Sie den echten Feldnamen und erweitern Sie das Prädikat um eine Namens‑Regel.

Konsole zeigt eine Evaluierungsmodus‑Warnung
→ Keine Lizenzdatei gefunden. Fix: Setzen Sie LICENSE_PATH in main.py auf Ihre .lic‑Datei, oder behalten Sie den Evaluierungsmodus für die Entwicklung; die Logik ist identisch.

Daten werden als rohe Seriennummern ausgegeben
→ Das rohe p.value ist irgendwo in einen Reader geraten. Fix: Behalten Sie das interpreted_value‑erste Muster aus read_props bei; es ist der Grund, warum Berichte lesbar bleiben.


8. Was kommt als Nächstes?

Sie haben einen funktionierenden Metadaten‑Diff. So geht es weiter:

  • Batch it: Schleifen Sie das Skript über Dokument‑Paare und speichern Sie das CSV pro Paar; die Kosten pro Paar sind zwei Datei‑Öffnungen, und die CSV‑Dateien lassen sich sauber zu einer Bibliotheks‑Übersicht zusammenführen.
  • Schedule it: Das main.py des Repositories prüft jeden Schritt und gibt einen korrekten Exit‑Code zurück, der sich direkt in CI oder einen Scheduler einbinden lässt.
  • Walk the tutorial version: Der use case guide baut dieselbe Pipeline in drei abgestuften Tutorials auf.
  • See the whole project: document-version-metadata-diff-python mit dem bereitgestellten Revisions‑Paar.

Ressourcen