Rechnungsdatenextraktion in Echtzeit mit PHP und AWS Textract
AWS Textract kann Rechnungs- und Belegfelder für eine PHP-Anwendung extrahieren. Beginnen Sie mit einer begrenzten lokalen CLI und ergänzen Sie bei Bedarf asynchrone Jobs sowie eine dauerhafte Queue-Verarbeitung. OCR liefert Kandidatenwerte, keine geprüften Buchhaltungsdatensätze: Prüfen Sie Konfidenz, Summen, Währungen und den Bedarf an manueller Prüfung, bevor Sie Ergebnisse weiterverwenden.
Warum die Rechnungsdatenextraktion automatisieren?
Automatisierung kann wiederholtes Abtippen reduzieren und Prüf-Queues leichter abarbeitbar machen. Die Verbesserung hängt von der Dokumentqualität und Ihrem Workflow ab; dieser Leitfaden belegt keine prozentuale Reduzierung manueller Arbeit und garantiert keine Echtzeit-Latenz.
AWS Textract und das PHP SDK v3 einrichten
Verwenden Sie eine gepflegte Version von PHP 8.x mit Composer und den vom AWS SDK benötigten Erweiterungen, darunter XML und einen funktionierenden HTTPS-Transport. Installieren Sie SDK v3 und committen Sie das erzeugte Lockfile:
composer require aws/aws-sdk-php:^3
Aktivieren Sie die erforderlichen AWS-Dienste und Berechtigungen in einer ausgewählten Region. Bevorzugen Sie IAM-Rollen für Workloads und die Standard-Credential-Provider-Chain des SDK gegenüber langlebigen Schlüsseln im Quellcode. Tatsächliche Textract-Anfragen können Kosten verursachen. Die folgenden Beispiele stellen weder Konten, Queues, Buckets noch IAM-Richtlinien für Sie bereit.
Die API AnalyzeExpense verstehen
AnalyzeExpense ist synchron. StartExpenseAnalysis startet einen asynchronen Job, dessen abgeschlossene
Ergebnisse mit GetExpenseAnalysis abgerufen werden. Verwechseln Sie die beiden Ergebnis-Workflows nicht.
Diese CLI akzeptiert bewusst nur PNG- oder JPEG-Dateien bis 5 MiB, ein konservatives Anwendungslimit für den Byte-Upload-Pfad. PDF/TIFF, mehrseitige Eingaben und S3-gestützte Verarbeitung erfordern eine eigene Validierung anhand der aktuellen Dokumentkontingente und des API-Vertrags. Die synchrone Verarbeitung von PDF/TIFF ist auf eine Seite begrenzt. Prüfen Sie die Limits des gewählten Vorgangs, statt anzunehmen, dass jeder Eingabepfad dieselben Nutzdaten akzeptiert.
Eine synchrone Rechnungs-CLI erstellen
Speichern Sie dies als invoice.php. Das Skript verarbeitet ein lokales Bild und gibt strukturiertes
JSON aus. Es ist kein öffentlicher Upload-Endpunkt: Eine Web-Integration benötigt zusätzlich
Authentifizierung, Autorisierung, Upload-Validierung, CSRF-Schutz, wo relevant, Rate Limits und
bereinigte HTTP-Antworten.
<?php
if (PHP_SAPI !== 'cli') {
http_response_code(404);
exit;
}
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/invoice_helpers.php';
use Aws\Textract\TextractClient;
if ($argc !== 2) {
fwrite(STDERR, "Usage: php invoice.php <invoice.png|invoice.jpg>\n");
exit(1);
}
try {
$path = $argv[1];
if (!is_file($path) || !is_readable($path)) {
throw new RuntimeException('Unreadable input');
}
$bytes = file_get_contents($path, false, null, 0, 5 * 1024 * 1024 + 1);
if ($bytes === false || $bytes === '' || strlen($bytes) > 5 * 1024 * 1024) {
throw new RuntimeException('Input exceeds the application limit');
}
$mime = (new finfo(FILEINFO_MIME_TYPE))->buffer($bytes);
if (!in_array($mime, ['image/png', 'image/jpeg'], true)) {
throw new RuntimeException('Use PNG or JPEG');
}
$textract = new TextractClient([
'region' => getenv('AWS_REGION') ?: 'us-east-1',
'version' => '2018-06-27',
'http' => ['connect_timeout' => 5, 'timeout' => 30],
'retries' => 2,
]);
$result = $textract->analyzeExpense(['Document' => ['Bytes' => $bytes]]);
echo json_encode(parseExpense($result->toArray()), JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR), "\n";
} catch (Throwable $error) {
fwrite(STDERR, "Invoice analysis failed. Check the image, size, credentials, region, and permissions.\n");
exit(1);
}
Verwenden Sie php invoice.php invoice.png, nachdem Sie den untenstehenden Helfer ergänzt haben. Das SDK serialisiert
die rohen Bildbytes; codieren Sie sie nicht erneut mit base64. Die JSON-Ausgabe kann sensible
Rechnungsdaten enthalten, senden Sie sie daher nicht an öffentliche Logs. Ergebnisse des AWS SDK sind
Objekte: Rufen Sie toArray() auf, bevor Sie sie an einen Helfer übergeben, der explizit ein Array
akzeptiert.
Asynchrone Verarbeitung mit SNS + SQS implementieren
Die folgenden Integrationsfragmente gehören in Ihren authentifizierten Job-Dienst. Sie setzen einen konfigurierten S3-Client, einen Textract-Client, einen Bucket, eine geprüfte lokale Eingabe, ein SNS-Topic und eine Publishing-Rolle voraus. Speichern Sie Eigentümerschaft und Job-Status dauerhaft in Ihrer Anwendung; dies sind keine eigenständigen HTTP-Handler.
1 · Das Dokument in S3 hochladen
Verwenden Sie einen von der Anwendung generierten Schlüssel und schließen Sie den Quell-Stream auch dann, wenn der Upload fehlschlägt:
$s3Key = 'invoices/' . bin2hex(random_bytes(16)) . '.pdf';
$stream = fopen($localPath, 'rb');
if ($stream === false) {
throw new RuntimeException('Cannot open validated invoice');
}
try {
$s3->putObject(['Bucket' => $bucket, 'Key' => $s3Key, 'Body' => $stream]);
} finally {
fclose($stream);
}
Validieren Sie Größe, Seitenzahl, Verschlüsselung und Typ der PDF-Datei vor dieser Phase. Der Bucket muss sich in der Textract-Region befinden, mit Zugriffs- und Verschlüsselungsberechtigungen, die zu Ihrem Workload passen.
2 · StartExpenseAnalysis anstoßen
Erstellen und speichern Sie einen Anwendungs-Job und dessen Idempotenz-Token dauerhaft, bevor Sie Textract aufrufen. Verwenden Sie dieses Token mit denselben Parametern erneut, wenn Sie eine fehlgeschlagene Antwort wiederholen:
$start = $textract->startExpenseAnalysis([
'DocumentLocation' => ['S3Object' => ['Bucket' => $bucket, 'Name' => $s3Key]],
'NotificationChannel' => ['SNSTopicArn' => $snsTopicArn, 'RoleArn' => $roleArn],
'ClientRequestToken' => $persistedRequestToken,
]);
$jobId = $start->get('JobId');
if (!is_string($jobId) || $jobId === '') {
throw new RuntimeException('Textract returned no job ID');
}
Speichern Sie die zurückgegebene Job-ID zusammen mit Eigentümer, S3-Schlüssel, Region und ausstehendem Status dauerhaft. Gleichen Sie einen Retry mit diesem Datensatz ab, damit eine verlorene Antwort keine unabhängige doppelte Arbeit erzeugt.
3 · Den Benachrichtigungskanal konfigurieren
Erstellen Sie das SNS-Topic und das SQS-Abonnement in der passenden Region. Setzen Sie
RawMessageDelivery=true für den nachfolgenden Decoder. Beschränken Sie die Queue-Richtlinie auf das erwartete
SNS-Topic und Konto und geben Sie der Publishing-Rolle von Textract die eng begrenzten Berechtigungen
und die Trust-Richtlinie, die AWS verlangt. Eine Rolle mit sns:Publish allein ist nicht die
vollständige Konfiguration.
4 · Einen Long-Poll-Worker ausführen
Fügen Sie diese Funktion zu invoice_helpers.php hinzu. Sie führt ein einzelnes Long Polling durch. Ein
Supervisor kann sie wiederholt mit einem konfigurierten Aws\Sqs\SqsClient, einer Queue-URL und Ihrem
dauerhaften Event-Handler aufrufen.
function consumeExpenseMessages(Aws\Sqs\SqsClient $sqs, string $queueUrl, callable $persistEvent): int
{
$response = $sqs->receiveMessage([
'QueueUrl' => $queueUrl, 'MaxNumberOfMessages' => 10, 'WaitTimeSeconds' => 20,
]);
$failures = 0;
foreach ($response['Messages'] ?? [] as $message) {
try {
// Raw SNS delivery contains the Textract event directly, not a Message envelope.
$event = json_decode($message['Body'], true, 512, JSON_THROW_ON_ERROR);
if (!is_array($event) || ($event['API'] ?? null) !== 'StartExpenseAnalysis'
|| !is_string($event['JobId'] ?? null) || $event['JobId'] === ''
|| !in_array($event['Status'] ?? null, ['SUCCEEDED', 'FAILED', 'PARTIAL_SUCCESS'], true)) {
throw new RuntimeException('Invalid Textract event');
}
$persistEvent($event);
$sqs->deleteMessage([
'QueueUrl' => $queueUrl, 'ReceiptHandle' => $message['ReceiptHandle'],
]);
} catch (Throwable $error) {
$failures++;
error_log('Invoice event processing failed; message was not acknowledged');
}
}
return $failures;
}
Der Callback ist eine erforderliche Anwendungsgrenze. Er muss den bekannten Job nachschlagen, Eigentümerschaft und erwartete Quellinformationen verifizieren, Ergebnisse abrufen, sofern angemessen, und eine dauerhafte, idempotente Statusänderung festschreiben, bevor er zurückkehrt. Bei fehlgeschlagener Persistierung muss er eine Exception werfen. Ein leerer Callback würde Nachrichten bestätigen und Arbeit verlieren. Fehlgeschlagene oder unvollständige Textract-Jobs benötigen einen dauerhaften Prüf- bzw. Fehlerstatus, keinen erfolgreichen Rechnungsdatensatz.
Setzen Sie das HTTP-Timeout des SDK länger als das 20-Sekunden-Polling. Verwenden Sie ein Visibility Timeout, das Verarbeitung und Persistierung abdeckt, verlängern Sie es bei längerer Arbeit und konfigurieren Sie eine Dead-Letter-Queue. Doppelte Nachrichten und ein Bestätigungsfehler nach einem erfolgreichen Commit müssen sich gefahrlos erneut abspielen lassen.
Rechnungsdaten extrahieren und strukturieren
Speichern Sie diesen Helfer in invoice_helpers.php und beginnen Sie die Datei mit <?php. Er bewahrt
wiederholte Feldtypen als separate Einträge und behält die Konfidenz bei, statt Werte in einer nach
Typ geschlüsselten Map stillschweigend zu überschreiben:
function parseExpense(array $result): array
{
return array_map(function (array $document): array {
$fields = static fn (array $field): array => [
'type' => $field['Type']['Text'] ?? null,
'text' => $field['ValueDetection']['Text'] ?? null,
'confidence' => $field['ValueDetection']['Confidence'] ?? null,
];
$groups = [];
foreach ($document['LineItemGroups'] ?? [] as $group) {
$rows = [];
foreach ($group['LineItems'] ?? [] as $row) {
$rows[] = array_map($fields, $row['LineItemExpenseFields'] ?? []);
}
$groups[] = ['index' => $group['LineItemGroupIndex'] ?? null, 'rows' => $rows];
}
return [
'expense_index' => $document['ExpenseIndex'] ?? null,
'summary' => array_map($fields, $document['SummaryFields'] ?? []),
'line_item_groups' => $groups,
];
}, $result['ExpenseDocuments'] ?? []);
}
Beispielhafter Antwortausschnitt
[
{
"expense_index": 1,
"summary": [
{ "type": "VENDOR_NAME", "text": "Example Supplies", "confidence": 98.5 },
{ "type": "TOTAL", "text": "125.00", "confidence": 97.2 }
],
"line_item_groups": []
}
]
Dies ist eine illustrative Projektion, nicht die vollständige AWS-Antwort. Bewahren Sie die Ursprungsantworten gemäß einer geeigneten Datenrichtlinie auf, falls eine spätere Prüfung Geometrie, Währung, Seitenverweise oder Labels benötigt.
Ein schlankes Dashboard erstellen
Zeigen Sie ausstehende, abgeschlossene, fehlgeschlagene und prüfungsbedürftige Status an. Escapen Sie OCR-Text beim Rendern und schützen Sie Tabellenkalkulations-Exporte vor CSV-Formel-Injection. Rechnungssummen sind nicht vertrauenswürdiger Text: Parsen Sie Währungen und Dezimalkonventionen explizit, gleichen Sie Positionen ab und verlangen Sie eine angemessene Freigabe vor einer ERP-Änderung oder Zahlung.
Fehlerbehandlung und Retry-Strategien
| Ebene | Erforderliches Verhalten |
|---|---|
| SDK | Retries und Anfrage-Timeouts begrenzen; Konfigurationsfehler von wiederholbaren Fehlern unterscheiden |
| Queue | Erst nach dauerhafter Verarbeitung bestätigen; Duplikate tolerieren |
| Persistenz | Job-Identität und idempotente Schreibvorgänge erzwingen |
| Teilergebnisse | Warnungen bewahren und zur Prüfung weiterleiten, statt vollständigen Erfolg zu melden |
| Logs | Rechnungstext, Zugangsdaten, Provider-Payloads und rohe Stacktraces aus öffentlichen Logs heraushalten |
Performance-Optimierung und Kostenüberlegungen
Wählen Sie synchrone oder asynchrone Verarbeitung anhand der Dokumentlimits und der Anforderungen an die Antwortzeit. Überwachen Sie tatsächliche Anfrageraten und Verarbeitungslatenzen, statt sich auf feste Standard-Kontingentwerte zu verlassen. Konfigurieren Sie Speicher-Aufbewahrungs- und Zugriffsrichtlinien für sensible Rechnungen. Begrenzen Sie Retries und prüfen Sie Abrechnungsmetriken auf unerwartete doppelte Arbeit.
Bewährte Verfahren für die Sicherheit
Authentifizieren Sie Uploads, autorisieren Sie jeden Job und jedes Ergebnis, begrenzen Sie Größen und Formate und nutzen Sie privaten Speicher. Beschränken Sie SNS/SQS- und Verschlüsselungsschlüssel-Richtlinien auf die beteiligten Ressourcen. Stellen Sie den Textract-Client und den Event-Callback nicht als nicht authentifizierten öffentlichen Endpunkt bereit. Dienstfehler sollten zu bereinigten Anwendungsstatus werden, nicht zu rohen Provider-Antworten.
Spickzettel zur Fehlerbehebung
| Symptom | Prüfungen |
|---|---|
| Zugriff verweigert | Berechtigungen des Aufrufers, Trust der Publishing-Rolle, SNS/SQS-Richtlinien und KMS-Zugriff |
| Keine Events | Topic-Abonnement, Queue-Richtlinie, Region und Raw-Delivery-Einstellung |
| Leere oder unsichere Felder | Lesbarkeit der Quelle, Modellgrenzen und konfidenzbasierte Prüfung |
| Wiederholte Jobs | Persistenz des Idempotenz-Tokens und Umgang mit Queue-Replays |
Mehrseitige Rechnungen und Batch-Verarbeitung handhaben
Rufen Sie nach Erfolg alle Ergebnisseiten ab. Dieser Generator lässt NextToken bei der ersten
Anfrage weg und weist ein wiederholtes Token oder einen nicht erfolgreichen Job-Status zurück. Fügen
Sie ihn derselben Helferdatei hinzu:
function expensePages(Aws\Textract\TextractClient $textract, string $jobId): Generator
{
$request = ['JobId' => $jobId, 'MaxResults' => 20];
$seen = [];
do {
$result = $textract->getExpenseAnalysis($request);
if (($result['JobStatus'] ?? null) !== 'SUCCEEDED') {
throw new RuntimeException('Expense analysis is not fully successful');
}
yield $result->toArray();
$token = $result['NextToken'] ?? null;
if ($token === null) break;
if (!is_string($token) || $token === '' || isset($seen[$token])) {
throw new RuntimeException('Invalid expense pagination token');
}
$seen[$token] = true;
$request['NextToken'] = $token;
} while (true);
}
Speichern Sie Seitenergebnisse dauerhaft mit ihrer Job-Identität, ExpenseIndex und den Indizes der
Positionsgruppen. Nehmen Sie nicht an, dass jede Paginierungsantwort eine eigene Rechnung ist, und
markieren Sie einen Job nicht als abgeschlossen, bevor der Generator fertig ist. Schlägt eine spätere
Seite fehl, setzen Sie den Retry an einem dauerhaften Checkpoint an oder ersetzen Sie
zwischengespeicherte Ergebnisse idempotent; zuvor gelieferte Seiten sind kein Beleg für einen
vollständigen Job.
Referenzen
Nächste Schritte
Bauen Sie einen verifizierten Prüf-Workflow um die extrahierten Felder herum auf, bevor Sie nachgelagerte Geschäftsaktionen automatisieren. Für verwaltete Vorverarbeitung und Dokumentkonvertierung lohnt ein Blick auf den Dienst für Dokumentenverarbeitung von Transloadit.
