MetricSign
NL|ENStart free →
Data Observability8 min·

Power BI's PDF connector parseert je bestand één keer. Daarna breekt hij bij elke structuurwijziging.

De PDF connector in Power Query werkt prima tijdens development. Hij faalt bij scheduled refresh, op het moment dat niemand toekijkt terwijl de column mappings verdwijnen.

Read this article in English →

De PDF connector leidt structuur af, maar begrijpt hem niet

Power BI Desktop bevat een PDF connector op basis van IFilter-gebaseerde PDF-parsing in Power Query. Als je hem op een bestand richt en een tabel selecteert, roept de M engine Pdf.Tables() aan. Die scant de pagina op rechthoekige gebieden die op tabeldata lijken en retourneert één of meer tabellen met kolomnamen afgeleid van wat hij als headerrij herkent.

Dit werkt verrassend goed bij een enkel, netjes opgemaakt PDF. Het probleem is dat de connector het schema niet tussen refreshes onthoudt. Hij leidt de tabelindeling opnieuw af bij elke refresh. Als het leveranciersrapport van volgende maand een subtotaalrij toevoegt tussen de header en de eerste datarij, kan de connector het subtotaal interpreteren als de nieuwe header. Als een samengevoegde cel twee kolommen beslaat, verschuift het kolomaantal en breekt elke downstream-kolomreferentie in je DAX.

De M code die wordt gegenereerd op het moment dat je de verbinding legt, ziet er ongeveer zo uit:

let
    Source = Pdf.Tables(File.Contents("C:\\Reports\\vendor_q1.pdf")),
    Table1 = Source{[Id="Table1"]}[Data],
    Promoted = Table.PromoteHeaders(Table1, [PromoteAllScalars=true])
in
    Promoted

Die Table.PromoteHeaders-aanroep gaat ervan uit dat de eerste rij altijd de header is. Het is geen garantie. Het is een gok die de eerste keer toevallig klopte. Als de PDF-structuur afwijkt, worden de gepromote headers datawaardes en eindigen de echte headers als datarij. Je measures berekenen nog steeds. Ze berekenen op basis van rommel.

De connector heeft ook een harde beperking: hij kan geen PDFs verwerken die op afbeeldingen gebaseerde scans zijn in plaats van tekstdocumenten. Binnen Power Query draait geen OCR. De refresh slaagt met nul teruggegeven rijen en als je het aantal rijen niet controleert, valt het je niet op.

Scheduled refresh mislukt stilletjes als het bestandspad verandert

Tijdens development werk je met File.Contents("C:\\Reports\\..."), een lokaal pad. Scheduled refresh op de Power BI Service kan lokale paden niet bereiken. Je hebt een gateway-gekoppelde bestandsshare nodig of een cloudbestand (SharePoint, OneDrive for Business, Azure Blob Storage).

Hier maakt PDF-extractie de gebruikelijke file-connector-problemen erger. De On-premises Data Gateway ondersteunt de PDF connector, maar alleen als de machine waarop de gateway draait de juiste PDF IFilter heeft geregistreerd. Op Windows Server is dat doorgaans de Adobe PDF IFilter 64-bit of de ingebouwde Windows IFilter voor PDF die meekomt met Windows Server 2019 en later. Als de IFilter ontbreekt, logt de gateway een fout in de mashup engine container:

Microsoft.Mashup.Engine.Interface.ResourceAccessDeniedOrDoesNotExistException:
  The resource 'Pdf.Tables' is not supported in the current environment.

Deze fout verschijnt niet als duidelijke melding in de refresh history van de Power BI Service. Je ziet Data source error met een generieke aanbeveling om credentials te controleren. De werkelijke oorzaak, een ontbrekend systeemcomponent op de gateway-machine, vereist dat je de gateway-logs opent op C:\\Users\\PBIEgwService\\AppData\\Local\\Microsoft\\On-premises data gateway\\*.log en zoekt naar de Mashup engine uitzondering.

SharePoint-gehoste PDFs introduceren een ander failure mode. Als het bestand is uitgecheckt door een andere gebruiker of als de bibliotheek goedkeuringsworkflows afdwingt, ontvangt de connector een HTTP-statuscode 423 Locked. Power Query vertaalt dit naar DataSource.Error: The remote server returned an error: (423) Locked. De refresh mislukt maar de foutmelding vertelt je niet welk bestand of welke bibliotheek de oorzaak is. Als je dataset PDFs trekt uit meerdere bestanden verspreid over verschillende SharePoint-sites, vereist het isoleren van het vergrendelde bestand dat je tabellen één voor één uitschakelt en de refresh opnieuw uitvoert.

PDF-naar-Power BI pipeline architectuur PDF arriveert in Blob Storage of SharePoint Event trigger start ADF pipeline of Azure Function Python parser extraheert tabellen en valideert schema Schema geldig? Schrijf naar staging tabel (Parquet of SQL) Schema ongeldig? Stuur alert en stop pipeline Power BI scheduled refresh laadt van staging tabel MetricSign bewaakt refresh-resultaat en latency
PDF-naar-Power BI pipeline architectuur

Python- en R-scripts parsen beter maar breken het gateway-contract

Veel teams laten de native PDF connector los en stappen over op Python-gebaseerde extractie met libraries als tabula-py, camelot-py of pdfplumber. Deze libraries bieden aanzienlijk meer controle over tabeldetectie: je kunt het paginanummer, de coördinaten van het begrenzingsvak en de parsingstrategie (lattice vs. stream) opgeven. Binnen Power BI Desktop werkt dit via de Python script datasource:

import pandas as pd
import tabula

tables = tabula.read_pdf(
    r"C:\\Reports\\vendor_q1.pdf",
    pages="all",
    lattice=True
)
df = pd.concat(tables, ignore_index=True)

Het probleem doet zich voor bij scheduled refresh. Python- en R-script datasources worden niet ondersteund door de On-premises Data Gateway. Microsoft documenteert deze beperking expliciet: gateway refresh is niet beschikbaar voor datasets die Python of R als datasource gebruiken. Je dataset refresht handmatig vanuit Desktop maar wordt nooit op schema vernieuwd via de Service, tenzij je de pipeline herstructureert.

De oplossing is de Python-parsing volledig buiten Power BI te verplaatsen. Voer de extractie uit in een Azure Data Factory pipeline activiteit, een Databricks notebook of een geplande Azure Function. Schrijf de geparsede output naar een staging-tabel in een SQL-database of een Parquet-bestand in Azure Data Lake Storage Gen2. Richt Power BI dan op de schone, tabellarische output en niet op de ruwe PDF.

Deze architectuurverschuiving lost twee problemen tegelijk op. Ten eerste wordt het schema gevalideerd in de parsingstap. Als de PDF-structuur verandert en tabula een DataFrame retourneert met onverwachte kolommen, kun je een uitzondering genereren en de pipeline stoppen voordat slechte data de dataset bereikt. Ten tweede maakt de Power BI dataset nu verbinding met een standaard datasource die de gateway probleemloos verwerkt.

De afweging is operationele complexiteit. Je hebt nu een pipeline-component die onafhankelijk gemonitord moet worden. De PDF kan te laat arriveren, het parsen kan falen op een nieuwe indeling of de staging-tabel wordt mogelijk niet gevuld voordat de Power BI refresh start.

Schema drift tussen PDF-versies vergiftigt measures zonder fouten te geven

Het gevaarlijkste failure mode bij PDF-extractie is niet een mislukte refresh, maar een succesvolle die structureel verkeerde data laadt. Stel je een maandrapport voor waarbij de leverancier een nieuwe kolom toevoegt tussen "Net Amount" en "Tax Amount". De PDF connector leidt de tabel opnieuw af en kent nieuwe kolomnamen toe. Als de nieuwe kolom "Discount Amount" is, kan de oude kolom "Tax Amount" nu worden toegewezen aan wat Power Query Column6 noemt in plaats van de gepromote headernaam.

Je DAX measure SUM('VendorData'[Tax Amount]) wordt nog steeds uitgevoerd. Hij telt op welke kolom die header ook draagt. Als de header-promotie is verschoven, telt hij nu mogelijk de kortingswaardes op, of de kolom bevat nulls van een verkeerd uitgelijnd parse. Er verschijnt geen fout. De visual rendert. Het getal klopt niet.

Om je hiertegen te beschermen, voeg je expliciete schema-validatiestappen toe in je M query:

let
    Source = Pdf.Tables(File.Contents(filePath)),
    Table1 = Source{[Id="Table1"]}[Data],
    Promoted = Table.PromoteHeaders(Table1),
    ExpectedColumns = {"Vendor", "Net Amount", "Tax Amount", "Total"},
    ActualColumns = Table.ColumnNames(Promoted),
    SchemaValid = List.ContainsAll(ActualColumns, ExpectedColumns),
    Validated = if SchemaValid then Promoted
               else error Error.Record("Schema Mismatch",
                    "Expected columns missing: " &
                    Text.Combine(List.Difference(ExpectedColumns, ActualColumns), ", "))
in
    Validated

Dit dwingt de refresh te mislukken met een beschrijvende fout als kolommen afwijken. Een mislukte refresh is veel beter dan een geslaagde refresh met beschadigde data: de mislukte refresh valt op, de beschadigde data staat dagenlang in een dashboard totdat iemand de cijfers in kwestie stelt tijdens een maandagochtendoverleg.

Voor datasets waarbij de PDF-bron regelmatig van structuur wisselt, overweeg je om het aantal rijen en kolommen bij te houden als checksum tijdens elke refresh en die te loggen naar een aparte tracking-tabel. Een plotselinge daling van 500 naar 12 rijen is een betrouwbaar signaal dat de parser de tabelgrenzen verkeerd heeft geïdentificeerd.

Verplaats parsing upstream en monitor het refresh-resultaat downstream

De betrouwbare architectuur voor PDF-naar-Power BI pipelines scheidt drie aandachtsgebieden: bestandsacquisitie, structureel parsen en dataset refresh. Elk kan onafhankelijk falen en elk heeft zijn eigen monitoring nodig.

Bestandsacquisitie houdt in dat je de PDF naar een locatie brengt die je pipeline kan bereiken. Of dat nu een SharePoint documentbibliotheek, een Azure Blob-container of een SFTP-drop is, bouw een check in die bevestigt dat het bestand is aangekomen met een niet-nul bestandsgrootte voordat het parsen start. Azure Data Factory ondersteunt event-gebaseerde triggers op Blob Storage die afgaan zodra een nieuw bestand binnenkomt. Voor SharePoint kan een Logic App of Power Automate flow een bibliotheekmap bewaken en het bestand kopiëren naar Blob Storage.

Structureel parsen draait in een rekenomgeving die je zelf beheert: een Databricks notebook, een ADF mapping data flow of een Azure Function die pdfplumber uitvoert. Deze stap moet het output-schema valideren, minimale rijaaantallen controleren en schrijven naar een staging-gebied in een formaat dat Power BI native aankan: Parquet, Delta Lake of een SQL-tabel.

Dataset refresh is de laatste stap. Met de geparsede data al in een schoon tabellarisch formaat wordt de Power BI refresh een standaard import- of DirectQuery-operatie. De gateway ondersteunt het, scheduled refresh werkt en incremental refresh policies kunnen worden toegepast als de staging-tabel is gepartitioneerd op datum.

MetricSign monitort deze laatste stap. Als een Power BI dataset refresh mislukt of slaagt maar te laat aankomt ten opzichte van het schema, geeft MetricSign een refresh_delayed-signaal af met de exacte foutcontext van de Power BI Service API. Voor datasets met PDF als bron, waarbij het echte risico schema drift is in plaats van een directe failure, sluit de combinatie van M-level schema-validatie uit de vorige sectie met MetricSign's refresh monitoring de kloof: de validatie dwingt een zichtbare mislukking af en MetricSign zorgt dat die mislukking de juiste persoon bereikt binnen minuten en niet binnen dagen.

Veelgestelde vragen

Kan Power BI's PDF connector gescande (op afbeeldingen gebaseerde) PDFs verwerken?+
Nee. De PDF connector in Power Query vertrouwt op tekstextractie via IFilter, niet op OCR. Als de PDF gescande afbeeldingen van tekst bevat, retourneert Pdf.Tables() nul rijen zonder een fout. Je moet OCR (bijvoorbeeld Azure AI Document Intelligence of Tesseract) upstream uitvoeren en de geëxtraheerde tekst als CSV of databasetabel aan Power BI aanbieden.
Waarom mislukt de refresh van mijn PDF-gebaseerde dataset op de Power BI Service maar werkt hij wel in Desktop?+
Drie veel voorkomende oorzaken: (1) De On-premises Data Gateway-machine mist de vereiste PDF IFilter, waardoor Pdf.Tables niet wordt ondersteund in die omgeving. (2) De dataset gebruikt een Python- of R-script voor PDF-parsing, wat de gateway niet ondersteunt voor scheduled refresh. (3) Het bestandspad verwijst naar een lokaal station dat de cloudservice niet kan bereiken. Controleer de gateway-logs onder het PBIEgwService gebruikersprofiel op de specifieke Mashup engine uitzondering.
Hoe voorkom ik stille datacorruptie als de PDF-indeling van maand tot maand verandert?+
Voeg een schema-validatiestap toe in je M query die controleert of alle verwachte kolomnamen aanwezig zijn na Table.PromoteHeaders. Als er ontbreken, gooi een fout met Error.Record() die de ontbrekende kolommen opsomt. Dit zet stille corruptie om in een zichtbare refresh failure die je monitoring kan oppikken.

Gerelateerde integraties

Gerelateerde artikelen