Jak pracować z danymi PDF w Python
Aspose.PDF FOSS dla Python przedstawia metadane XMP dokumentu PDF jako model danych w pamięci zbudowany z obiektów XmpPacket, XmpField, XmpArray, XmpStruct i XmpProperty, przy czym NamespaceProvider rozwiązuje prefiksy przestrzeni nazw na URI. Umożliwia to odczyt, tworzenie i przepisywanie strukturalnych metadanych — tytułów, dat, list słów kluczowych, pól niestandardowych — bez ręcznego pisania RDF/XML. Biblioteka jest czystym Python i jest instalowana poleceniem poniżej.
Przewodnik krok po kroku
Krok 1: Zainstaluj pakiet
Zainstaluj pakiet Aspose.PDF FOSS:
git clone https://github.com/aspose-pdf-foss/Aspose-PDF-FOSS-for-Python.git
cd Aspose-PDF-FOSS-for-Python
pip install -e .Zweryfikuj instalację, importując XmpPacket i wyświetlając komunikat potwierdzający:
from aspose_pdf import XmpPacket
print("aspose-pdf-foss-for-python is ready.")Krok 2: Zaimportuj wymagane klasy
Zaimportuj klasy modelu danych XMP oraz resolver przestrzeni nazw:
from aspose_pdf import (
NamespaceProvider,
XmpArray,
XmpField,
XmpPacket,
XmpProperty,
XmpStruct,
)Krok 3: Utwórz pakiet i ustaw proste właściwości
XmpPacket() zaczyna się od pustej listy pól. set_value(prefix, name, value, uri=...) ustawia lub zastępuje prostą właściwość, a get(prefix_or_uri, name) odczytuje ją ponownie jako XmpField; get akceptuje zarówno zarejestrowany prefiks, jak i pełny URI przestrzeni nazw:
from aspose_pdf import XmpPacket
packet = XmpPacket()
packet.set_value("dc", "title", "Quarterly Report")
packet.set_value(
"dc", "creator", "Automation Pipeline",
uri="http://purl.org/dc/elements/1.1/",
)
title_field = packet.get("dc", "title")
print(title_field.value) # "Quarterly Report"Krok 4: Przechowuj typowane wartości
XmpPacket zapewnia typowane wygodne akcesory, które konwertują do i z formy tekstowej, którą XMP przechowuje wewnętrznie: set_date/get_date, set_int/get_int, set_real/get_real oraz set_bool/get_bool. Każdy getter zwraca None, gdy właściwość jest nieobecna, zamiast zgłaszać błąd:
from datetime import datetime
packet.set_date("xmp", "CreateDate", datetime(2026, 7, 29, 9, 30))
packet.set_int("pdf", "PageCount", 42)
packet.set_real("custom", "ConfidenceScore", 0.97, uri="https://example.com/ns/custom/1.0/")
packet.set_bool("custom", "IsFinal", True, uri="https://example.com/ns/custom/1.0/")
print(packet.get_date("xmp", "CreateDate"))
print(packet.get_int("pdf", "PageCount"))
print(packet.get_real("custom", "ConfidenceScore"))
print(packet.get_bool("custom", "IsFinal"))Krok 5: Przechowuj zlokalizowany tekst i uporządkowane tablice
set_localized_text zapisuje właściwość alternatywną językowo (rdf:Alt), taką jak dc:title, w określonym języku, domyślnie "x-default". set_array zapisuje uporządkowaną (Seq), nieuporządkowaną (Bag) lub alternatywną (Alt) tablicę z prostej listy wartości, a get_array odczytuje ją ponownie jako listę łańcuchów znaków:
packet.set_localized_text("dc", "description", "Quarterly summary", lang="en")
packet.set_localized_text(
"dc", "description", "Resumen trimestral",
uri="http://purl.org/dc/elements/1.1/", lang="es",
)
print(packet.get_localized_text("dc", "description", lang="es"))
packet.set_array("dc", "subject", ["finance", "quarterly", "internal"], kind="Bag")
print(packet.get_array("dc", "subject"))Krok 6: Tworzenie wartości strukturalnych przy użyciu XmpStruct
Niektóre właściwości XMP (zapisy wymiarów, wpisy historii) są wartościami strukturalnymi, a nie prostym tekstem. Utwórz jedną przy pomocy XmpStruct, dodaj obiekty członkowskie XmpField za pomocą add, odczytaj członka z powrotem po nazwie przy użyciu get, a następnie dołącz strukturę do pakietu przy pomocy add:
dimensions = XmpStruct()
dimensions.add(XmpField(prefix="stDim", name="w", value="612"))
dimensions.add(XmpField(prefix="stDim", name="h", value="792"))
dimensions.add(XmpField(prefix="stDim", name="unit", value="pt"))
print(dimensions.get("w").value) # "612"
packet.add(XmpField(prefix="xmpTPg", name="MaxPageSize", value=dimensions))Krok 7: Rejestrowanie własnych przestrzeni nazw XMP
NamespaceProvider jest wstępnie załadowany ze standardowymi przestrzeniami nazw XMP (Dublin Core, Adobe XMP, PDF i inne). Wywołaj register(prefix, uri), aby dodać własne mapowanie — zwraca ono samego dostawcę, więc wywołania mogą być łańcuchowane — a następnie dołącz dostawcę do pakietu, aby serializacja mogła rozwiązać własny prefiks do jego URI:
from aspose_pdf import NamespaceProvider, XmpPacket
provider = NamespaceProvider()
provider.register("custom", "https://example.com/ns/custom/1.0/")
packet = XmpPacket(namespace_provider=provider)
packet.set_value("custom", "batch_id", "run-2026-07-29")Krok 8: Analiza i serializacja pakietu
XmpPacket.parse(data, provider=...) odczytuje surowe bajty lub tekst pakietu XMP do XmpPacket. serialize() i to_bytes() renderują pakiet z powrotem do bajtów pakietu XMP:
xmp_bytes = packet.to_bytes()
restored = XmpPacket.parse(xmp_bytes)
print(restored.get("dc", "title").value)Typowe problemy i rozwiązania
get() lub zwrócony jest typowany getter None mimo że właśnie ustawiłem właściwość
Potwierdź, że argument prefix (lub URI) przekazywany do get/get_int/get_date/etc. dokładnie odpowiada temu, który został podany w odpowiadającym wywołaniu set_* — get dopasowuje się zarówno do prefiksu pola, jak i jego URI przestrzeni nazw, ale niezgodny niestandardowy prefiks w jednym wywołaniu i URI w drugim spowoduje pominięcie.
Niestandardowy prefiks przestrzeni nazw generuje niekompletny wynik podczas serializacji
Prefiks, który nie należy do standardowych przestrzeni nazw XMP (dc, xmp, pdf i podobnych), wymaga albo explicite argumentu uri= w każdym wywołaniu set_*, albo NamespaceProvider z zarejestrowanym tym prefiksem i dołączonym do pakietu za pomocą XmpPacket(namespace_provider=provider) przed wywołaniem serialize()/to_bytes().
get_bool, get_int, lub get_real returns None dla wartości, o której wiem, że jest ustawiona
Te gettery typowane zwracają None, gdy przechowywany tekst nie jest parsowany jako oczekiwany typ — na przykład, get_int dla właściwości, której wartość została ustawiona przy użyciu set_value jako tekstu dowolnego zamiast set_int. Użyj odpowiadającego settera typowanego (set_int, set_real, set_bool), aby logika parsowania gettera była zgodna ze ścieżką zapisu.
XmpPacket.parse raises ValueError w pakiecie pochodzącym z niewiarygodnego źródła
parse odrzuca każde zadeklarowanie <!DOCTYPE lub <!ENTITY jako zabezpieczenie przed wrogim wejściem przeciwko atakom XXE i billion-laughs. Legalny pakiet XMP nigdy nie wymaga DTD; potraktuj wyjątek jako sygnał, że strumień źródłowy jest uszkodzony lub niebezpieczny, zamiast omijać to zabezpieczenie.
get_array returns None zamiast listy
get_array zwraca None, gdy nazwana właściwość nie istnieje lub nie została zapisana jako XmpArray (na przykład, jeśli została ustawiona przy użyciu set_value zamiast set_array). Użyj set_array przy zapisywaniu właściwości, aby przechowywana wartość pasowała do kształtu, którego oczekuje get_array.
Najczęściej zadawane pytania
Jaka jest różnica między set_value a typowanymi setterami, takimi jak set_int?
set_value przechowuje dokładnie to, co value przekażesz. Typowane settery (set_date, set_int, set_real, set_bool) konwertują ich wejście na tekstową formę, której XMP używa wewnętrznie, i mają dopasowane typowane gettery, które ją odczytują, więc używaj ich, gdy potrzebujesz pełnej bezpieczeństwa typów w obu kierunkach, zamiast surowych łańcuchów.
Jak różnią się rodzaje XmpArray (Bag, Seq, Alt)?
Bag jest nieuporządkowanym zestawem wartości, Seq jest uporządkowaną listą, a Alt przechowuje wartości alternatywne (najczęściej alternatywy językowe, jak używa set_localized_text). Przekaż żądany rodzaj do set_array(..., kind="Bag") lub zbuduj XmpArray(kind=...) bezpośrednio.
Czy mogę odczytać surową listę właściwości w pakiecie, nie znając ich nazw wcześniej?
Tak — iteruj packet.fields, który zawiera każdy XmpField, XmpArray i XmpProperty dodany do pakietu w kolejności wstawiania.
Czy potrzebuję NamespaceProvider dla standardowych przestrzeni nazw XMP?
Nie. dc, xmp, pdf oraz inne standardowe prefiksy rozwiązują się automatycznie. NamespaceProvider jest potrzebny tylko dla własnych prefiksów, które wymyślisz sam.
Jak kwalifikatory XmpProperty różnią się od zwykłego XmpField?
XmpProperty opakowuje podstawowy XmpField wraz z listą pól kwalifikatorów (dodanych przy użyciu add_qualifier), stosowaną w rzadszych przypadkach, gdy sama własność wymaga dodatkowych metadanych RDF dołączonych do jej wartości, poza kwalifikatorem xml:lang, który już obejmuje XmpField.language.