Wydobywanie danych z faktur PDF za pomocą Pythona

Aby wydobyć dane z faktury PDF za pomocą Pythona, należy odczytać tekst z pliku, a następnie wyodrębnić z niego nazwane pola. Dwa kroki, może czterdzieści linijek kodu, i działa to pięknie na pierwszej fakturze, na której tego spróbujesz. Następnie siódmy dostawca przesuwa numer faktury o trzy linijki wyżej, a twój parser zaczyna zwracać None o drugiej w nocy.

Ta druga część to prawdziwy temat tego artykułu. Odczyt jest łatwy. Utrzymanie poprawności to dopiero prawdziwa praca.

Najważniejsze informacje

  • Python wyodrębnia tekst z faktury w kilku linijkach. Utrzymanie jego poprawności przy setkach układów od dostawców to to, co faktycznie Cię kosztuje.
  • PDF to nie format danych. To typograficzny opis wydrukowanej strony, dlatego wyrażenie regularne napisane pod układ jednego dostawcy jest rzeczą kruchą.
  • Wyrażenia regularne i szablony dla poszczególnych dostawców skalują się liniowo wraz z listą dostawców. Modele wizyjne tego nie robią, ponieważ czytają stronę zamiast ciągu znaków.
  • Niezależnie od tego, co wydobywa dane, Twój własny kod musi sprawdzić arytmetykę. Pozycje, które nie sumują się do wartości netto, to najtańszy wykrywacz błędów, jaki kiedykolwiek napiszesz.
  • To rzadko sama ekstrakcja sprawia, że ludzie przestają tworzyć własne rozwiązania. Zazwyczaj jest to kolejka wyjątków, dopasowywanie dostawców i integracja z systemem księgowym.

Format PDF

Format PDF jest uniwersalny i pozwala dokładnie odwzorować papierowe dokumenty, takie jak faktury, bez ograniczania ich wyglądu. Jego geneza wywodzi się ze świata druku, a sam format został stworzony jako cyfrowa reprezentacja drukowanej strony. Dzięki tej elastyczności twórcy plików PDF mają pełną swobodę w zakresie projektowania, przy jednoczesnej zgodności z różnymi standardami i regulacjami.

Problem pojawia się jednak wtedy, gdy potrzebujemy wydobyć z pliku PDF znajdujące się w nim dane. Elastyczna i nierzadko skomplikowana natura PDF-ów jest przeciwieństwem potrzeby uporządkowania informacji — tej niezbędnej do zarządzania dużą ilością danych na co dzień przetwarzanych w firmach.

A screen capture of PDF file format layers
PDF file format layers

Plik PDF zapisuje, gdzie każdy znak znajduje się na stronie. Nie przechowuje faktu, że liczba w prawym dolnym rogu to suma. Ta relacja istnieje w Twojej głowie, a każda metoda ekstrakcji opisana w tym artykule to próba jej zakodowania.

Jakie są etapy wydobywania danych z faktury?

Faktury najczęściej spotykamy w formacie PDF. Faktura dokumentuje transakcję pomiędzy dostawcą a odbiorcą, gdzie produkty lub usługi wymieniane są na ustaloną kwotę. Oto etapy niezbędne, by wydobyć dane z tego dokumentu:

  1. Określ schemat danych, które chcesz wydobyć ze swoich faktur
  2. Przekonwertuj fakturę z obrazu na tekst
  3. Wyodrębnij tekst z faktury według zdefiniowanego schematu
  4. Zgromadź wyodrębnione dane

A screen capture of Invoice data extraction process
Invoice data extraction process

Zdefiniuj schemat danych dla swojej faktury

Faktury pochodzą od różnych wystawców i niemal każdy ma odmienny wygląd swoich dokumentów. Mimo tej różnorodności, sedno faktur pozostaje niezmienne: standardowo potrzebujemy informacji o sprzedawcy, kliencie, numerze faktury, dacie i szczegółowej liście pozycji z powiązaną ilością, opisem i kosztem. Świetnym sposobem na rozpoczęcie definiowania formatu faktury byłoby Twoje oprogramowanie księgowe, ponieważ najprawdopodobniej to właśnie tam ostatecznie przechowasz wyodrębnione dane z faktur, prawda? Jeśli chcesz po prostu formatu danych, który poradzi sobie ze wszystkimi przypadkami brzegowymi, polecam stronę schema.org, która wygodnie definiuje serię standardowych formatów danych dla wielu rzeczy, w tym dla faktur. Parseur definiuje domyślny schemat danych dla Twoich faktur, ale możesz go zmienić pod kątem swojego przypadku użycia, zmieniając nazwy pól w skrzynce odbiorczej, zgodnie z instrukcją. Gdy ustalisz swój format danych, możesz przejść do konwersji faktury z obrazu na tekst.

Na przykład taki schemat danych dla faktury możesz zdefiniować w formacie JSON Swagger:

{
    "InvoiceNumber": {
        "type": "string",
        "description": "Numer faktury"
    },
    "InvoiceIssueDate": {
        "type": "string",
        "description": "Data wystawienia faktury"
    },
    "Items": {
        "type": "array",
        "description": "Lista pozycji znajdujących się na fakturze",
        "items": {
            "type": "object",
            "properties": {
                "quantity": {
                    "type": "number",
                    "description": "Ilość pozycji"
                },
                "description": {
                    "type": "string",
                    "description": "Opis pozycji"
                },
                "unit_price": {
                    "type": "number",
                    "description": "Cena jednostkowa pozycji"
                },
                "price": {
                    "type": "number",
                    "description": "Łączna cena pozycji"
                }
            }
        }
    }
}

Zapisz to, zanim zaczniesz pisać jakikolwiek kod parsowania. Jest to kontrakt, który musi spełnić każda poniższa metoda, a także coś, co później przekażesz do modelu wizyjnego.

Przekonwertuj fakturę z obrazu na tekst

A screen capture of an invoice taken from a smartphone
Picture of an invoice taken from a smartphone

Plik PDF może zawierać obraz. Na przykład, pracownik może zrobić szybkie zdjęcie faktury aparatem w smartfonie. Następnie zapisuje je jako PDF i wysyła do działu księgowości. Twój dział księgowości ma za zadanie wydobyć dane z tej faktury i w jakiś sposób wprowadzić je do systemu księgowego bez żadnych błędów. Kolejny etap to konwersja obrazu na tekst za pomocą systemu Optycznego Rozpoznawania Znaków (OCR). Jednym z najpopularniejszych systemów OCR jest Tesseract. Tesseract jest napisany w językach C i C++. Aby korzystać z Tesseract w naszym programie w Pythonie, będziemy musieli użyć dowiązania (binding) takiego jak PyTesseract. Dowiązanie to sposób na wywołanie biblioteki oprogramowania (tutaj Tesseract) z języka, w którym nie została ona napisana (tutaj Python). Istnieje wiele takich systemów, a ich wyniki znacznie się różnią w zależności od podstawowej technologii i jakości skanu analizowanego dokumentu. Parseur w przejrzysty sposób wykrywa, czy Twój dokument to obraz, i sam dokona konwersji wewnętrznie na tekst. Gdy dane zostaną skonwertowane do postaci tekstowej, są gotowe do ekstrakcji.

Wyodrębnij tekst z faktury zgodnie ze swoim schematem danych

Po uzyskaniu tekstowej (lub przeszukiwalnej) wersji PDF-a, możesz użyć pythonowej biblioteki pdftotext do wydobycia danych z pliku w formie tekstu. Oto przykładowy kod do ekstrakcji tekstu z pliku PDF:

import pdftotext

# Wczytaj fakturę
with open("invoice.pdf", "rb") as file_handle:
    pdf = pdftotext.PDF(file_handle)

# Iteracja po wszystkich stronach
for page in pdf:
    print(page)

Nazwij ten skrypt convert_pdf_to_text.py i uruchom go, a otrzymasz fakturę jako tekst na standardowym wyjściu. Jeśli chcesz przekierować wyjście do pliku, możesz uruchomić:

$ python convert_pdf_to_text.py > invoice.txt

pdftotext daje Ci płaski ciąg znaków, co sprawdza się przy polach nagłówka, ale jest beznadziejne przy tabelach. Kiedy potrzebujesz pozycji z faktury (line items), sięgnij po pdfplumber, ponieważ zachowuje on współrzędne każdego słowa i może samodzielnie spróbować wyodrębnić tabelę:

import pdfplumber

with pdfplumber.open("invoice.pdf") as pdf:
    page = pdf.pages[0]

    # Słowa wraz z ich pozycjami na stronie
    for word in page.extract_words():
        print(word["text"], word["x0"], word["top"])

    # Oraz próba wyodrębnienia tabeli z pozycjami (line-item)
    table = page.extract_table()
    if table:
        for row in table:
            print(row)

Uruchom to na prawdziwej fakturze od dostawcy, a bardzo często okaże się, że table to None. To nie jest błąd. To pierwszy szczery sygnał, że ten problem jest trudniejszy niż się wydaje, do czego wrócimy poniżej.

Mając fakturę w formie tekstu, możesz wydobywać interesujące Cię dane, korzystając z dowolnej kombinacji następujących technik:

  • Możesz skorzystać z wyrażeń regularnych, aby wydobyć żądane dane. Wyrażenia regularne są potężnym sposobem na ekstrakcję danych z tekstu, ale są też bardzo kruche. Jeśli format faktury ulegnie zmianie, będziesz musiał zaktualizować swoje wyrażenie regularne. Ponadto, wyrażenia regularne nie radzą sobie najlepiej z wyodrębnianiem danych z tabel.
  • Możesz użyć wizualnego systemu szablonów, w idealnym przypadku opartych o Dynamiczny OCR i Strefowy OCR. To bardziej zaawansowany sposób wydobywania danych z tekstu. Jest on bardziej odporny na zmiany niż wyrażenia regularne, ale też bardziej złożony w implementacji.
  • Możesz przekazać stronę do modelu wizyjnego wraz ze schematem i pozwolić mu na odczytanie dokumentu w sposób, w jaki robi to człowiek. Jest to podejście, które zmieniło się w ciągu ostatnich dwóch lat i poświęcono mu osobną sekcję poniżej.

Wydobądźmy dane z Twojej faktury za pomocą modułu Python do wyrażeń regularnych re. Oto przykładowy kod do wyodrębnienia numeru z Twojej faktury:

import re

# Wczytaj swoją fakturę
with open("invoice.txt", "r") as file_handle:
    invoice = file_handle.read()

# Wyodrębnij numer faktury
invoice_number = re.search(r"Invoice number: (\w+)", invoice).group(1)
print(invoice_number)

Nazwij ten skrypt extract.py i uruchom go, a otrzymasz numer faktury na standardowym wyjściu:

$ python extract.py

I otrzymasz coś w tym stylu:

INV-1234

Dlaczego twój parser faktur oparty na wyrażeniach regularnych się psuje

Wyrażenie regularne dopasowuje się do ciągu znaków. Faktura jest obrazem. Wszystko, co idzie nie tak, wynika z tego niedopasowania i psuje się w niewielkiej liczbie przewidywalnych przypadków:

  • Etykieta została przeniesiona. Twój wzorzec jest zakotwiczony w Invoice number:, a nowy szablon zawiera Invoice #, lub umieszcza wartość w następnym wierszu zamiast w tym samym.
  • extract_table zwraca None. Ekstraktory tabel szukają linii podziału (obramowań). Większość faktur od dostawców wyrównuje kolumny za pomocą spacji i nie rysuje w ogóle żadnych obramowań.
  • Tekst pojawia się w niewłaściwej kolejności. Układy dwukolumnowe i pływające bloki adresowe przeplatają się, gdy strona jest spłaszczana do pojedynczego stringu, więc wiersze Twoich pozycji docierają przetasowane.
  • Tabela rozciąga się na stronach. Wiersze od dwa do dziewięć znajdują się na pierwszej stronie, od dziesięć do czternaście na drugiej, a pomiędzy nimi powtarzają się nagłówki kolumn z dodaną sumą częściową, która udaje element tabeli.
  • Trzy liczby wyglądają jak kwota całkowita. Suma częściowa, suma całkowita, kwota do zapłaty oraz saldo na przeniesienie. Wybranie po prostu największej z nich będzie błędem na każdej fakturze zawierającej nadpłatę (credit).
  • Skan jest fotografią. OCR odczytuje zamazaną 8 jako 3 i nikt w dalszym procesie tego nie zauważa, ponieważ 3 to całkowicie poprawna cyfra.
  • Połączone komórki i wielowierszowe opisy. Opis jednego produktu przenosi się na trzy linijki, a Twoja logika podziału na wiersze zmienia go w trzy pozycje bez podanej ceny.

Żadna z tych rzeczy nie jest do naprawienia za pomocą lepszego wyrażenia regularnego. To wszystko są problemy z układem przebrane w szaty problemu z ciągiem znaków. Jest to moment, w którym większość ludzi albo zaczyna pisać po jednym szablonie dla każdego dostawcy, co powoduje nieskończony rozrost rozwiązania, albo zmienia podejście.

Podejście na 2026 rok - model wizyjny i schemat

Użyteczna zmiana polega na tym, że nie musisz już spłaszczać strony do tekstu przed wydobyciem z niej danych. Model wizyjny patrzy na wyrenderowaną fakturę, więc przeniesienie numeru faktury przez dostawcę przestaje być wydarzeniem. To, co mu dostarczasz to nie wzorzec, ale schemat, który zapisałeś na początku tego artykułu.

Ogranicz wynik tak, aby za każdym razem uzyskiwać te same klucze. Biblioteka Pydantic plus tryb wyników ustrukturyzowanych, jak na przykład OpenAI's structured outputs, zrobi to za Ciebie:

from typing import List
from pydantic import BaseModel

class LineItem(BaseModel):
    description: str
    quantity: float
    unit_price: float
    amount: float

class Invoice(BaseModel):
    vendor_name: str
    invoice_number: str
    invoice_date: str      # ISO 8601
    currency: str
    subtotal: float
    tax: float
    total: float
    line_items: List[LineItem]

# Renderuj stronę PDF do obrazu, wyślij do modelu wizyjnego,
# i wymagaj, aby odpowiedź pasowała do schematu Invoice.
# Model wypełnia pola. Nie może wymyślać ich struktury.

W rzeczywistości jest to rozwiązanie większości problemów z ekstrakcją i właśnie z tego powodu to podejście rozpowszechniło się tak szybko. Wprowadza ono również nowy tryb awarii, którego wyrażenia regularne nigdy nie miały: regex, który nie może znaleźć numeru faktury zwraca None, podczas gdy model, który nie może go znaleźć, czasami wymyśli wiarygodny numer. Zasada, która zapewnia Ci bezpieczeństwo, jest prosta. Model proponuje, a Twój kod weryfikuje.

Jeśli nie chcesz uruchamiać modelu samodzielnie, ta sama możliwość jest sprzedawana jako usługa zarządzana przez dostawców usług w chmurze: w usłudze Azure AI Document Intelligence i w ramach Amazon Textract's AnalyzeExpense, które zwracają pola nagłówka i pozycje na fakturze oddzielnie. O tym, jak podejście to różni się od parsowania opartego na regułach, pisaliśmy w tekście o parserach PDF wykorzystujących sztuczną inteligencję w porównaniu z parserami opartymi na regułach, a to, jak to wygląda w odniesieniu do faktur, znajdziesz w artykule na temat przetwarzania faktur przy użyciu AI z wizją.

Warstwa walidacji, którą musisz napisać samodzielnie

Niezależnie od tego, co wyprodukowało Twój JSON, te testy powinny znaleźć się w Twoim własnym kodzie, a nie zależeć od wskaźnika poziomu pewności (confidence score) wbudowanego w ekstraktor. Testy te są tanie, deterministyczne i wychwytują błędy, które kosztują pieniądze:

  1. subtotal + tax + shipping - discount składa się na total, z dokładnością do centa.
  2. Kwoty poszczególnych pozycji sumują się do wartości netto (subtotal). Jeżeli nie, to upuściłeś rząd albo dodałeś fałszywy.
  3. Każde quantity * unit_price musi być równe odpowiedniej dla nich kwocie amount.
  4. Data jest poprawnie formatowana i nie leży w przyszłości.
  5. Numer faktury powiązany z danym dostawcą nie został jeszcze opłacony. Podwójne płatności są najdroższym błędem przy rozliczeniach zobowiązań (AP).
  6. Nazwa dostawcy znajduje odzwierciedlenie w głównej bazie dostawców.
  7. Dane bankowe do zapłaty zgadzają się z tymi, które już posiadamy dla tego dostawcy. Jakakolwiek zmiana w tym przypadku będzie dotyczyła oszustwa, a nie danych z ekstrakcji.
  8. Waluta jest jedną z tych, w których faktycznie handlujesz.
  9. Numer zamówienia (PO) istnieje i jego ilości i ceny pasują, jeżeli uruchamiasz sprawdzanie dopasowania 2-kierunkowego lub 3-kierunkowego.
  10. Wszystkie wymagane pola są obecne i nie są puste.

Cokolwiek nie przejdzie testu, trafia do weryfikacji przez człowieka, a nie do ksiąg:

def validate(invoice):
    errors = []

    if abs(invoice.subtotal + invoice.tax - invoice.total) > 0.01:
        errors.append("totals_do_not_add_up")

    line_sum = sum(item.amount for item in invoice.line_items)
    if invoice.line_items and abs(line_sum - invoice.subtotal) > 0.01:
        errors.append("line_items_do_not_sum_to_subtotal")

    if not invoice.invoice_number:
        errors.append("missing_invoice_number")

    return errors

Dziesięć linijek arytmetyki wyłapie więcej prawdziwych problemów niż dowolna ilość dostrajania promptów.

Co z invoice2data i innymi bibliotekami?

Bibliotece invoice2data należy się uczciwa wzmianka, ponieważ jest to jeden z pierwszych wyników, na który natrafia wiele osób, i stanowi autentycznie solidne oprogramowanie. Jest to narzędzie z wiersza poleceń i biblioteka Pythona, która dopasowuje faktury na bazie szablonów YAML pisanych na dostawcę, gdzie logikę dopasowania przechowuje się w kontroli wersji zamiast głęboko zakopywać ją w kodzie programu. Mając tuzin dostawców, którzy nigdy nie zmieniają układu swoich faktur, program ten dobrze posłuży przez lata.

Ograniczenia leżą w designie, a nie w samej jakości. Jeden szablon na dostawcę oznacza, że nakład pracy utrzymaniowej wzrasta wraz z listą dostawców, i że szablony te mogą ulec uszkodzeniu ze wszystkich tych samych powodów, co opisane wcześniej. Gdzieś na granicy pomiędzy dwadzieścia a trzydzieści aktywnych formatów faktur, osoba utrzymująca szablony wykonuje więcej pracy niż człowiek, który do tej pory przepisywał te faktury z palca.

To samo odnosi się w zasadzie do wszystkich innych bibliotek. pdfplumber, PyMuPDF, pdftotext i pytesseract są doskonałe we własnej dziedzinie, tj. w pobieraniu poszczególnych liter i ich koordynatów ze strony. Żadna z nich nigdy nie była w zamyśle pomyślana tak, aby potrafić powiedzieć nam, który ułamek to ta finalna kwota na fakturze. Porównujemy jeszcze więcej podobnych narzędzi na liście zestawiającej najlepsze parsery PDF i najlepsze API do ekstrakcji danych.

Zbierz wyodrębnione dane

W Pythonie możesz iterować po plikach z fakturami w wybranym folderze i wyciągać z nich kluczowe dane. Załóżmy, że wyciągamy numer faktury oraz jej całkowitą kwotę, zapisując wyniki do pliku CSV:

import os
import re

import pdftotext

# Iteracja po wszystkich plikach PDF w folderze
for filename in os.listdir("invoices/"):
    if not filename.endswith(".pdf"):
        continue

    # Wczytaj fakturę
    with open("invoices/" + filename, "rb") as file_handle:
        pdf = pdftotext.PDF(file_handle)

    # Wydrukuj nagłówek kolumny CSV
    print("InvoiceNumber,TotalAmount")

    # Iteruj po wszystkich stronach
    for page in pdf:
        # Wyodrębnij numer faktury
        invoice_number = re.search(r"Invoice number: (\w+)", page).group(1)
        total_amount = re.search(r"Total amount: (\w+)", page).group(1)
        print(invoice_number, total_amount, sep=",")

Nazwij ten skrypt extract_to_csv.py i uruchom go, a na standardowym wyjściu uzyskasz numer faktury i kwotę całkowitą, które możesz przekierować do pliku CSV, by następnie otworzyć go ulubionym arkuszem kalkulacyjnym, np. Excel:

$ python extract_to_csv.py > invoices.csv

Folder z plikami CSV to moment, w którym kończy się większość skryptów do faktur, ale to także moment, w którym zaczyna się prawdziwa księgowość. Ktoś nadal musi je zaimportować, dopasować sprzedawców, poprawiać wiersze odrzucone przez system księgowy i ustalić, co zrobić z fakturą, która nie przeszła walidacji o 2 w nocy. Ta praca nie pojawia się w Twoim skrypcie i nie pojawia się na Twoim rachunku za tokeny.

Kiedy przestać budować własne rozwiązanie

Oto część, którą omija większość dostawców, więc my powiemy to pierwsi. Jeśli masz niewielką grupę stałych dostawców, inżyniera, który może pilnować pipeline'u, i nikt nie czeka na te dane, to napisz ten skrypt. Model wizyjny plus schemat plus te dziesięć linijek walidacji, o których wspomnieliśmy wyżej, pozwolą Ci osiągnąć bardzo wiele za niewielkie pieniądze, a dodatkowo dokładnie zrozumiesz każdą z jego części.

Rachunek za budowanie przyjdzie jednak z czasem, a nie na etapie ekstrakcji. Dotrze on dopiero w tych elementach oprogramowania, których nikt nie chce prototypować:

  • Kolejka wyjątków. Twój zespół ds. rozrachunków (AP) potrzebuje ekranu, na którym kliknięcie konkretnego pola na ekranie podświetla to samo miejsce w samej fakturze PDF, co pozwoliłoby pracownikowi skorygować taką usterkę w cztery, zamiast w czterdzieści sekund. Coś takiego to gotowy produkt, a nie jakiś tam skrypt.
  • Dopasowywanie dostawców. "ACME Ltd", "Acme Limited" oraz "ACME LTD." to dokładnie ten sam podmiot, za to księga rachunkowa nie zaakceptuje żadnych trzech, traktując je odrębnie.
  • Wykrywanie duplikatów. Dokładnie ta sama faktura potrafi przybyć we wtorek jako załącznik do emaila, a także zawędrować w postaci podsumowania PDF z rachunku z minionego piątku.
  • Maszyna stanu. Kolejki, ponowienia przy błędach, częściowe niepowodzenia działania, a także dokładna wiedza, które to właściwie spośród owych wczorajszych 300 nadesłanych faktur przeszły całość skutecznie bez żadnego zgłaszania błędów.
  • Ścieżka audytu. Co konkretnie w dokumencie zostało skrupulatnie ekstrahowane, co skorygował sam człowiek, kto następnie to wydał do puszczenia przez księgi, no i co ważniejsze: w którym dokładnie momencie. Pracownicy departamentu finansowego nieraz jeszcze zapytają o tego typu dane, w szczególności przy trakcie audytów firmy.
  • Wszystko, co następuje po wygenerowaniu formatu JSON. Konkretne przypisywania mapowanych wcześniej zmiennych bezpośrednio na zmienne akceptowalne do zapisu do głównego programu księgowego (GL), księgowanie z zamówieniami PO, a także korygowanie ręczne rekordów niedopuszczonych wstępnie automatycznie do importu.

Wyraźne sygnały wskazujące na to, że nadszedł czas na zakup gotowego narzędzia, zamiast na jego budowę, są następujące. Przekroczyłeś limit około dwudziestu do trzydziestu aktywnych układów dokumentów na swoich serwerach. Potrzebujesz pozycji z faktur (line items), a nie tylko głównych pól nagłówkowych. Spora część wszystkich przychodzących od firm papierów to po prostu zwykłe skany ich faktur. To Twój zespół AP (a nie sam w sobie dział inżynieryjny!) w rzeczywistości musi zajmować się systematycznym naprawianiem błędów z systemu po samych ekstraktach. Do tego dochodzą problemy polegające na opóźnianiu wypłat pieniędzy za sprawą niedziałającej autoryzacji z parserów, a sam programista w ogóle nie nadąża wypuszczać do systemu absolutnie żadnych użytecznych, z perspektywy samego biznesu, rzeczy po drodze.

Jak Parseur sobie z tym radzi

Parseur jest parserem dokumentów i obsługuje całą wspomnianą pętlę, a nie tylko ten pierwszy etap samej ekstrakcji. Dokumenty faktur przybywają w to miejsce odgórnie, prosto na specjalnie spersonalizowany, indywidualny dla każdej subskrypcji email adresowy, lub używając dedykowanego w pełni automatyzowanego interfejsu API, bądź wreszcie poprzez połączony synchronizowany stały kanał folderów w chmurze. Nasz potężny modelujący silnik Vision AI odczytuje automatycznie wszystkie wejściowe PDF-y, w tym wszystkie zeskanowane fizyczne wydruki bądź zdjęcia tychże plików, dodatkowo uzupełniając to analizowaniem tekstu dla dokumentów opartych na klasycznym zapisie elektronicznym. System samorzutnie oddaje pola odpowiednio nazwane z prawidłowym doborem przypisanych typów klas dla odpowiednich pozycji z wyodrębnianiem precyzyjnie poukładanych informacji wierszy i kolumn. Co więcej nie wymusza od użytkowników ręcznego dopisywania, dopasowywania czy nadpisywania logiki pisaniem oddzielnych autorskich szablonów dla kontrahentów modyfikujących formaty dla poszczególnych faktur.

To, co otrzymujesz z systemu, jest jednak zaledwie połową rozwiązania zaprezentowanego w naszym powyższym artykule. Wyodrębnione dane można w każdym z osobna fragmencie kontrolować, analizować czy nawet osobiście samodzielnie manualnie zmodyfikować, przez pracownika w specjalnym oknie z systemem błyskawicznie sprawdzając pozycje niewłaściwe przeliczone na całości przed wprowadzeniem zgłoszenia do jakiegokolwiek firmowego tiketu. Korekty z kolei automatycznie wpływają pozytywnie wstecznie na poprawę dla samej głównej pętli uczenia wyodrębniania. Skuteczne, poddane ostatecznej weryfikacji przez walidator rekordy wysyłane są bezpiecznie docelowo od razu prosto wszędzie tam, gdzie powinny one w pełni zautomatyzowane dotrzeć; to jest bezpośrednio dla integracji webhooks direct webhook integration, albo dalej z wykorzystaniem powszechnych łączników procesowych: Make, Zapier i Microsoft Power Automate. Można to oczywiście odebrać jako sam czysty plik gotowego wyniku dla API jako sam plik z kodem dla formatowania JSON.

Jeśli pragniesz natychmiast zobaczyć pełną udokumentowaną i ustandaryzowaną przez bazową naszą implementację bibliotekę pól automatycznie zaczerpywanych z wszystkich napotkanych przykładowych faktur w standardzie, sprawdź nasz szczegółowy przewodnik pod oknem na samej dedykowanej witrynie invoice OCR, co z pewnością posłuży dla zrozumienia pełnego formatowania pętli ujęcia całości w automatyzowanym formacie z naszego tekstu: invoice data capture.

Utwórz darmowe konto
Oszczędzaj czas i wysiłek z Parseur. Automatyzuj swoje dokumenty.

Podsumowanie

Wydobywanie danych z faktury PDF za pomocą Pythona to rozwiązany problem dla około stu faktur i problem nierozwiązany dla dziesięciu tysięcy. Kod nie jest tym, co zmienia się między tymi dwiema liczbami. To, co ulega zmianie, to to, na ile układów od różnych dostawców po cichu zapisujesz się w kwestii utrzymania i kto otrzyma alert na pager (czy powiadomienie z systemu), gdy jeden z nich zadecyduje przearanżować wygląd własnej faktury.

Napisz ten skrypt. Naprawdę warto zrobić to raz, chociażby po to, aby samemu dokładnie przekonać się o tym, z jakimi dokładnie to błędami natury systemowej i architektonicznej opisanych powyżej, po swojemu się skonfrontujesz w swoim systemie najpierw. Dopiero następnie postaraj się jak najbardziej autentycznie odpowiedzieć uczciwie sam przed sobą na samo najważniejsze i zarazem kluczowe zagadnienie i ustalić; co konkretnie zamierzasz przetestować przez ten dany zbliżający się półrocze testowania. Czy twoje poświęcenie nad pracą programisty nad swoim inżynieryjnym eksperymentem, przyniesie pozytywne realne zaowocowanie, na tej liście ósmego awaryjnego zawiadomienia. Jeżeli z Twojej pespektywy ta padająca opowiedź na końcu brzmiała „nie”, oprogramowanie Parseur dostarcza skuteczne rozwiązania w automatyzowaniu systemów firmowych na co dzień tego typu, z powodzeniem realizując te złożone trudne zdania w pełni niezawodnie w swoich usługach aż od pełnego 2016 roku i sprawnie całkowicie weźmie całą papierową biurokrację czy sterty folderowych baz firmowych po prostu wyjmując to obciążające wyzwanie od ręki, na zawsze i trwale wyręczając Cię w samej uciążliwej ekstrakcji w codziennej Twojej firmie.

Ostatnia aktualizacja

Rozpocznij

Zautomatyzuj ekstrakcję danych
z dokumentów już dziś

Załóż konto za darmo w kilka minut i zobacz, jak Parseur wpasowuje się w Twój proces.

Bez trenowania modeli AI
Działa od razu na Twoich dokumentach
Od prostego eksportu po pełne API

Często zadawane pytania

Pytania, które programiści zadają, gdy pierwszy skrypt do faktur już działa, a drugi dostawca zdążył go popsuć.

Odczytaj tekst z pliku PDF za pomocą biblioteki takiej jak pdfplumber lub pdftotext, a następnie wyodrębnij nazwane pola z tego tekstu. W przypadku PDF-ów stworzonych cyfrowo jest to zadanie dwuetapowe. W przypadku skanów najpierw potrzebujesz przejścia OCR, aby zamienić obraz w tekst. Część, która decyduje o tym, czy rozwiązanie sprawdzi się na produkcji, to nie samo odczytywanie, ale to, w jaki sposób przejdziesz od ściany tekstu do numeru faktury, daty, sprzedawcy i poszczególnych pozycji, gdy każdy dostawca układa je inaczej.

invoice2data to narzędzie wiersza poleceń open-source i biblioteka Pythona, która wyodrębnia pola z faktur za pomocą szablonów YAML, które piszesz dla każdego dostawcy. Jest to dobre rozwiązanie, gdy masz niewielką, stałą grupę dostawców i chcesz przechowywać logikę dopasowywania w systemie kontroli wersji, a nie w kodzie. Przestaje to być dobrym rozwiązaniem w momencie, gdy pisanie i utrzymywanie jednego szablonu na dostawcę kosztuje więcej niż ręczne przepisywanie, które miało zastąpić.

Potraktuj tabelę z pozycjami jako osobną ekstrakcję w stosunku do pól nagłówka i zrekonstruuj ją na stronach, zanim ją sparsujesz. Funkcja extract_table w pdfplumber działa na czystych tabelach z obramowaniem, a nie zwraca nic na tabelach bez obramowań, którymi jest większość faktur od dostawców. Niezawodnym wzorcem jest wykrycie granic kolumn raz na układ dostawcy, przeniesienie ich przez podziały stron, usunięcie powtarzających się wierszy nagłówka, a następnie sprawdzenie, czy wyodrębnione wiersze sumują się do sumy częściowej. Jeśli tak nie jest, oznacza to, że pominąłeś wiersz.

Za pomocą deterministycznych testów we własnym kodzie, nigdy nie polegając wyłącznie na poziomie pewności ekstraktora. Podstawowy zestaw opiera się na arytmetyce i tożsamości. Upewnij się, że suma częściowa plus podatek plus wysyłka minus rabat daje w rezultacie sumę całkowitą, że pozycje sumują się do sumy częściowej, że data jest poprawnie przetwarzana i nie dotyczy przyszłości, że numer faktury dla tego sprzedawcy nie został już opłacony, że sprzedawca istnieje w głównej bazie dostawców oraz że waluta jest tą, której się spodziewałeś. Wszystko, co nie przejdzie testów, powinno trafić do człowieka, a nie do ksiąg rachunkowych.

Wtedy, gdy ekstrakcja przestaje być trudną częścią. Wywołanie modelu wizyjnego jest tanie i szybkie w prototypowaniu, więc jeśli masz garstkę stałych dostawców i kogoś, kto może pilnować pipeline'u – buduj samodzielnie. Rachunek przyjdzie później, w logice kolejek i ponowień, na ekranie przeglądu wyjątków, którego potrzebuje Twój zespół ds. rozrachunków, w wykrywaniu duplikatów, dopasowywaniu dostawców, ścieżce audytu i integracji księgowej. Policz je, zanim zaczniesz porównywać ceny za stronę.

Tak, ale skan wymaga procesu OCR, zanim którakolwiek z bibliotek tekstowych będzie mogła cokolwiek zobaczyć. Sfotografowana lub zeskanowana faktura to po prostu obraz zawinięty w plik PDF, więc pdfplumber i pdftotext zwrócą z niego pusty ciąg znaków. Zazwyczaj stosowaną ścieżką open-source jest Tesseract za pośrednictwem powiązania pytesseract, po uprzednim wyprostowaniu i wyczyszczeniu obrazu. Nowoczesne modele wizyjne całkowicie pomijają ten krok i odczytują obraz bezpośrednio.

Nie ma jednej biblioteki do faktur, są tylko biblioteki PDF oraz Twoja własna logika. pdfplumber to zazwyczaj pierwszy wybór, ponieważ eksponuje słowa z ich współrzędnymi i posiada ekstraktor tabel. PyMuPDF jest szybszy przy dużych partiach dokumentów. pdftotext to najprostsze narzędzie, które działa, gdy potrzebujesz tylko surowego tekstu. pytesseract radzi sobie ze skanami. Każde z nich daje Ci tekst lub obramowania (boxy). Żadne z nich nie powie Ci jednak, która liczba to kwota całkowita.

Ponieważ wyrażenie regularne dopasowuje się do ciągu znaków, a faktura to obraz. Twój wzorzec jest zakotwiczony do etykiety, podziału wiersza lub pozycji kolumny, którą nowy szablon dostawcy właśnie przesunął. Faktury są na wpół ustrukturyzowanymi dokumentami wizualnymi, więc rzeczy, które powodują awarie, to problemy z układem, a nie problemy z ciągami znaków. Tabele, które dzielą się na stronach, powtarzające się nagłówki, połączone komórki oraz różnica między sumą częściową, sumą całkowitą a kwotą do zapłaty nie są do naprawienia za pomocą lepszego wyrażenia regularnego.

Tak, i obecnie jest to najkrótsza droga od pliku PDF do ustrukturyzowanego formatu JSON, ale tylko pod warunkiem obudowania tego schematem i walidatorem. Model wizyjny odczytuje fakturę w taki sam sposób, jak człowiek, więc zmiana układu przez dostawcę przestaje być wydarzeniem. Ogranicz wynik wyjściowy za pomocą schematu JSON, używając na przykład OpenAI structured outputs lub modelu Pydantic, aby za każdym razem otrzymywać te same klucze. Następnie sam sprawdź arytmetykę. Model, który nie może znaleźć numeru faktury, czasami wymyśli wiarygodny.

Dokładność zależy od pola i samego skanu, a nie od produktu, więc traktuj wszelkie pojedyncze, głośne procenty jako marketing. Wydrukowane sumy i numery faktur na czystym, cyfrowym pliku PDF wracają niemal idealnie odczytane. Te same pola na sfotografowanym, przekrzywionym skanie o niskim kontraście to miejsca, gdzie cyfry ulegają pomyleniu, a niewłaściwy znak kosztuje prawdziwe pieniądze. Liczba, którą warto mierzyć, to nie dokładność na poziomie znaków, ale to, ile faktur trafia do Twojego systemu księgowego bez ingerencji człowieka.

Przestajesz pisać cokolwiek pod konkretnego dostawcę. Każde podejście, które wymaga szablonu, zestawu wyrażeń regularnych czy mapy współrzędnych dla każdego z dostawców, rośnie liniowo wraz z listą kontrahentów i nie zmniejsza niczyjego nakładu pracy po przekroczeniu mniej więcej dwudziestu do trzydziestu aktywnych układów. Model, który odczytuje dokument wizualnie, jest z założenia niezależny od formatu, dlatego dryf układu przestaje być zdarzeniem wymagającym konserwacji. To, co nadal wymaga Twojej uwagi, to kolejka wyjątków, a to jest problem workflow, a nie samej ekstrakcji.

Zapisanie do CSV to raptem kilka linii kodu w Pythonie i jest to ta łatwiejsza połowa. Trudniejsza połowa to wprowadzenie danych do systemu, który opłaca rachunek, co oznacza mapowanie nazw pól na jego schemat, dopasowanie dostawców do istniejących rekordów, obsługę odrzuconych wierszy oraz ponawianie wywołań, które zawiodły. Zaplanuj tę połowę od samego początku, ponieważ folder z plikami CSV, które ktoś nadal musi importować ręcznie, nie zaoszczędził nikomu ani jednego popołudnia.