matthiasbp
Goto Top

Pdf in Excel Bestellnummern auslesen

Guten Abend,

ich bin vorhin ueber Google auf den Beitrag PDF auslesen und per VBA in Excel schreiben gekommen. In dem steht, dass man mit einer Datei pdftotext.exe und einem Makro Daten auslesen kann.
Die Datei habe ich nur unter einem xpdreader gefunden, aber nicht direkt. Das Makro hahe ich wie beschrieben rueber kopiert und den Text geaendert, bekomme aber eine Fehlermeldung auf dem Firmenlatop.

Ist es moeglich auf dem Weg ca. 500 pdf's mit teilweise 90 Seiten auszulesen? Ich habe schon ein altes Makro, dass mir vor Jahren jemand geschrieben hat versucht und Makros gefunden, die pdf's in Excel umwandeln koennen sollen, aber nichts hat funktioniert. Mein Desktop meldet staendig, dass eine dll fehlt, da muss ich vermutlich Excel reparieren. Mit Powerquery habe ich es auch nicht hinbekommen, weil ich nichts gefunden habe wie ich mehr als eine Seite laden kann. Bei dem Makro, dass ich rueberkopiert und probiert habe kam eine Fehlermeldung, hier For i = 1 To colPFiles.Count
WSHShell.Run strCMDLine & """" & colPFiles.Item(i) & """", 0, True

Unten habe ich als Bilder zwei Seiten aus einer pdf angehaengt, dass ich nicht weiss, wie man die pdf einhaengen kann.
Es soll 'nur' die Ordernummer ausgelesen werden. Pro pdf koennen das mehr als 200 sein. Geht das mit dem tool?

Beste Gruesse
Matthias

Content-ID: 4289509530

Url: https://administrator.de/contentid/4289509530

Ausgedruckt am: 18.11.2024 um 19:11 Uhr

aqui
aqui 20.09.2023 um 09:36:22 Uhr
Goto Top
colinardo
Lösung colinardo 20.09.2023, aktualisiert am 24.10.2023 um 13:01:03 Uhr
Goto Top
Servus.
das oben genannte Tutorial hat @aqui ja schon genannt. Dein gewünschter Text lässt sich aber auch schön abgrenzen und per Regular Expressions auslesen.

Sowas macht man heute eher gleich über die Powershell als mit VBA. Hierfür kannst du folgende Powershell-Funktion nehmen:
Wichtiger Hinweis: Da ich hier natürlich keines deiner PDFs im Original vorliegen habe musste ich den Regular Expression-Pattern anhand deiner Bilder "raten" es kann also sein das er nicht ganz passt, in dem Fall kannst du mir aber gerne eine (geschwärzte) Datei zur Verfügung stellen => PN , dann kann ich dir bei der Definition des Pattern behilflich sein. Des weiteren muss das PDF maschinenlesbaren Text enthalten, das Skript macht keine OCR!

Anzugeben in den Variablen im Kopf des Skriptes sind der Ordner mit den PDF-Dateien, und die Zieldatei in der die extrahierten Nummer landen.
# Ordner mit den auszulesenden PDF Dateien
$SOURCEFOLDER = "D:\Orders"  
# Ausgabedatei mit den Nummern
$OUTFILE = "D:\ordernummern.csv"  
# Extrahierungs-Muster
$REGEXPATTERN = '(?is)(?<=Order\s+)\d{7} ?[\d-]{8}\b'  
# ---------------------------------------
# function to get pdf page content
function Get-PDFContent {
    param(
        # absolute pdf file path
        [Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][Alias('Fullname')][string[]]$path,  
        # array of page indices from wich to extract page content
        [Parameter(mandatory=$false)][int[]]$page = -1,
        # optional regular expression pattern for string extraction
        [Parameter(mandatory=$false)][string]$pattern,
        # optional regex subgroup definition
        [Parameter(mandatory=$false)][int]$patterngroup = 0
    )
    begin {
        if (!('iTextSharp.text.pdf.PdfReader' -as [type])){  
	        # Funktion zum Laden des iTextSharp Assemblies
            function Load-iTextLibrary {
                if($psscriptroot -ne ''){  
                    $localpath = join-path $psscriptroot 'itextsharp.dll'  
                }else{
                    $localpath = join-path $env:TEMP 'itextsharp.dll'  
                }
                $zip = $null;$tmp = ''  
                try{
                    if(!(Test-Path $localpath)){
                        Add-Type -A System.IO.Compression.FileSystem
                        $tmp = "$env:TEMP\$([IO.Path]::GetRandomFileName())"  
                        write-host "Downloading and extracting required 'iTextSharp.dll' ... " -F Green -NoNewline  

                        (New-Object System.Net.WebClient).DownloadFile('https://www.nuget.org/api/v2/package/iTextSharp/5.5.13.1', $tmp)  
                        $zip = [System.IO.Compression.ZipFile]::OpenRead($tmp)
                        $zip.Entries | ?{$_.Fullname -eq 'lib/itextsharp.dll'} | %{  
                            [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_,$localpath)
                        }
                        write-host "OK" -F Green   
                    }
                    if (Get-Item $localpath -Stream zone.identifier -ea SilentlyContinue){
                        Unblock-File -Path $localpath
                    }
                    Add-Type -Path $localpath
                }catch{
                    throw "Error: $($_.Exception.Message)"  
                }finally{
                    if ($zip){$zip.Dispose()}
                    if ($tmp -ne ''){del $tmp -Force -EA SilentlyContinue}  
                }
            }
            # iText Library laden
            Load-iTextLibrary
            # enable reading protected documents
            [iTextSharp.text.pdf.PdfReader]::unethicalreading = $true
        }
    }
    process{
        foreach($file in $path){
            $reader = $null
            try{
                $reader = New-Object iTextSharp.text.pdf.PdfReader $file
                if ($page -eq -1){
                    # Get content of all pages
                    $content = 1..($reader.NumberOfPages) | %{
                        [iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($reader,$_)
                    }
                }else{
                    # Get content of specific pages
                    $content = $page | ?{$_ -in (1..$reader.NumberOfPages)} | %{
                        [iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($reader,$_)
                    }
                }
                if($pattern -ne ''){  
                    [regex]::matches([string]$content,$pattern) | %{$_.Groups[$patterngroup].Value}
                }else{
                    $content
                }
            }catch{
                write-error "ERROR processing file '$file' : $($_.Exception.Message)"  
            }finally{
                if ($reader){$reader.Dispose()}
            }
        }
    }
}
# Extrahiere Daten aus allen gefundenen PDF-Dateien und schreibe das Ergebnis von Eindeutigen Werten inklusive Dateiname als CSV in die Ausgabedatei
$result = foreach($file in Get-ChildItem $SOURCEFOLDER -File -Filter *.pdf){
    Get-PDFContent $file.Fullname -pattern $REGEXPATTERN | select @{n='Filename';e={$file.Name}},@{n='Ordernumber';e={$_ -replace '\s'}} -Unique  
}
if ($result){
    $result | export-csv -LiteralPath $OUTFILE -Delimiter ";" -NoType -Encoding UTF8  
}else{
    write-host "Keine passenden Daten für Export gefunden!" -F Yellow  
}

Grüße Uwe
MatthiasBP
MatthiasBP 20.09.2023 um 13:33:59 Uhr
Goto Top
Hallo Uwe,

vielen Dank. Habe dir eine PN geschickt, allerdings konnte ich keine pdf anhaengen.

VG
Matthias
colinardo
colinardo 21.09.2023 aktualisiert um 10:18:18 Uhr
Goto Top
Code wurde auf die tatsächlichen Daten die per PN geliefert wurden angepasst. #edit# Die zusätzliche Ausgabe des Dateinamens wurde noch hinzugefügt!

Wenns das dann war, den Beitrag bitte noch auf gelöst setzen. Merci.
MatthiasBP
MatthiasBP 21.09.2023 um 11:03:33 Uhr
Goto Top
Super, danke dir, Uwe. Muss nur noch rausfinden wie die Ausfuehrung geht.
colinardo
colinardo 21.09.2023 aktualisiert um 11:15:52 Uhr
Goto Top
Powershell Leitfaden für Anfänger


back-to-topAnleitung: Wie starte ich Powershell-Scripte
  • Zuerst speichert man den Code in einer Textdatei mit der Endung .ps1 mit UTF-8 Encoding.
  • Wenn man zum ersten mal Powershell-Scripte ausführt, musst man einmalig vorher noch das Ausführen von Scripten im User-Account freischalten. Dazu öffnet man eineadministrative Powershell-Konsole und gibt dort den Befehl Set-ExecutionPolicy RemoteSigned -Force ein. Um diese Policy für alle User auf dem Rechner zu setzen muss man diesen Befehl in einer Powershell-Konsole mit Admin-Rechten starten. Noch ein Hinweis für 64-Bit-Systeme: Hier sollte sowohl für die 32bit und 64Bit Variante der Powershell die Policy in einer Admin-Konsole gesetzt werden: Set-ExecutionPolicy RemoteSigned -Force; start-job { Set-ExecutionPolicy RemoteSigned -Force } -RunAs32
  • Jetzt kann das Powershell-Script wie weiter unten erläutert in einer Powershell-Konsole oder aus einer CMD-Fenster heraus ausgeführt werden. Wer lieber mit der Maus arbeitet macht einen Rechtsklick auf die Script-Datei und wählt: Mit Powershell ausführen.
  • Alternativ lässt sich ein Script auch ohne das globale Ändern der ExecutionPolicy ausführen indem man die Policy als Parameter auf der Kommandozeile mitgibt:
powershell.exe -ExecutionPolicy ByPass -File "C:\Pfad\Script.ps1"
back-to-topStarten eines Scriptes in einer Powershell-Konsole
Immer den kompletten Pfad zum Script angeben, und wenn er Leerzeichen beinhaltet in Anführungszeichen einschließen:
"C:\Pfad\script.ps1"
Liegt das Script im selben Verzeichnis in dem man sich gerade befindet, kann man es auch so abkürzen:
.\script.ps1
back-to-topStarten von PS-Scripten aus Batch und Kommandozeilen heraus:
Hier gibt es unterschiedliche Methoden, je nach Anforderungen gibt es hier einige Besonderheiten vor allem bei Leerzeichen in Pfaden zu beachten!
Der einfachste Aufruf sieht hier so aus:
powershell.exe -File "C:\Pfad\Script.ps1"
Wenn man dem Script Parameter übergeben möchte:
powershell.exe -File "C:\Pfad\Script.ps1" "Parameter 1" "Parameter 2"
Wenn man "benannte" Parameter übergeben möchte (die einfachen Hochkommas um den Scriptpfad werden benötigt wenn er Leerzeichen beinhaltet):
powershell.exe -command "&'C:\Pfad\Script.ps1' -par1 'Wert1' -par2 'Wert2'"
Man kann auch mehrere Scripte hintereinander ausführen lassen:
powershell.exe -command "&'C:\Pfad\Script1.ps1';&'C:\Pfad\Script2.ps1'"
Weitere Parameter zeigt einem ein powershell -? in einer Konsole an.
back-to-topStarten von PS-Scripten in der Aufgabenplanung (Taskplaner)
In der jeweiligen Aktion unter "Programm/Script" trägt man powershell.exe ein und unter "Argumente hinzufügen (optional)" trägt man wie oben geschrieben alles was hinter powershell.exe kommt ein - also z.B. -File "C:\Pfad\Script.ps1" "Parameter 1" "Parameter 2"
MatthiasBP
MatthiasBP 21.09.2023 um 11:17:40 Uhr
Goto Top
Danke schoen. Hier ist ein Fehler hochgekommen, aber nicht mit deiner Anleitung, sondern direkt in der Powershell auf einem anderen Laptop: Werde deine Anleitung ausprobieren. Vielleicht geht's damit.
colinardo
colinardo 21.09.2023 aktualisiert um 11:37:53 Uhr
Goto Top
Rechtsklick auf die DLL > Eigenschaften und das Mark-of-the-web entfernen sollte hier helfen sofern noch vorhanden. Eventuelle Security Tools temporär abschalten und an obige Anleitung halten.
Wenn die Skriptdatei selbst von einem anderen Rechner kopiert wurde bitte auch per rechtsklick > Eigenschaften prüfen ob das Skript mit einem Mark Of The Web versehen ist und wenn ja entfernen!
MatthiasBP
MatthiasBP 21.09.2023 um 11:39:42 Uhr
Goto Top
Sehe ich in keinem der Tabs.
colinardo
colinardo 21.09.2023 aktualisiert um 11:44:51 Uhr
Goto Top
Dann ist die OK, das erscheint wenn, ganz unten auf dem ersten Tab. Prüfe auch die Skriptdatei (*.ps1) selbst auf das MOTW wenn du sie von einem anderen Rechner übertragen hast.
Das sind die typischen Powershell Anfänger-Ruckler face-wink
MatthiasBP
MatthiasBP 21.09.2023 aktualisiert um 15:42:19 Uhr
Goto Top
Wo muesste ich da schauen im Skript?
Habe hier gekuckt:
if(!(Test-Path $localpath)){
Add-Type -A System.IO.Compression.FileSystem
$tmp = "$env:TEMP\$([IO.Path]::GetRandomFileName())"
write-host "Downloading and extracting required 'iTextSharp.dll' ... " -F Green -NoNewline

(New-Object System.Net.WebClient).DownloadFile('https://www.nuget.org/api/v2/package/iTextSharp/5.5.13.1', $tmp)
$zip = [System.IO.Compression.ZipFile]::OpenRead($tmp)
$zip.Entries | ?{$_.Fullname -eq 'lib/itextsharp.dll'} | %{
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_,$localpath)
Wenn ich den Pfad ohne die Nummer eingebe wird eine Datei runtergeladen mit 5.5.13.3. Soll ich die vielleicht probieren?
colinardo
colinardo 21.09.2023 aktualisiert um 13:53:53 Uhr
Goto Top
Wo muesste ich da schauen im Skript?
Wenn ich den Pfad ohne die Nummer eingebe wird eine Datei runtergeladen mit 5.5.13.3. Soll ich die vielleicht probieren?
Nein, Nein, Nein nirgendwo, du muss hier rein garnichts anpassen das läuft out of the box mit der Version! Die neueren DLL-Versionen haben zusätzlich weitere Abhängigkeiten mit denen geht das obige Skript in der Form nicht!

Ich meinte das Markt of the Web in den Skript-Datei-Eigenschaften nicht der Inhalt.

Works as designed, ansonsten grätscht dir sehr wahrscheinlich deine verwendete Security Suite dazwischen und du musst die DLL als Ausnahme definieren!
MatthiasBP
MatthiasBP 21.09.2023 um 13:25:35 Uhr
Goto Top
Ok, mit der Testdatei hat's jetzt funktioniert. Mit den anderen noch nicht, kommen ein paar kryptische Zeichen, siehe unten. Habe die Dateien eben in denselben Ordner und es kommt wieder nur die Testdatei raus, allerdings nur 5 Bestellungen und nicht 19.
colinardo
colinardo 21.09.2023, aktualisiert am 22.09.2023 um 07:45:55 Uhr
Goto Top
allerdings nur 5 Bestellungen und nicht 19.
Du sagst ja kein Wort welche Zeichen die Nummern enthalten dürfen, ich war davon ausgegangen das nur Zahlen und Bindestrich enthalten sein dürfen, die "xxx" habe ich als Schwärzung deinerseits interpretiert. Des weiteren gibt es nach 7 Ziffern ein Leerzeichen im Fließtext so das ich immer 7 Zeichen vor dem Leerzeichen erwarte. Du kannst den Regex ja selbst an deine Bedürfnisse anpassen, musst dich halt etwas damit beschäftigen: Regular Expressions Tutorial

Zur Info: Doppelte Nummern in der selben Datei werden ausgefiltert und immer nur eine davon beibehalten, auch darüber verlierst du kein Wort und ich muss raten!

Mit den anderen noch nicht, kommen ein paar kryptische Zeichen, siehe unten.
Da kommt vor wenn es keinen extrahierten Inhalt aus der Datei gibt, dass habe ich noch korrigiert.

Viel Erfolg
Grüße Uwe
MatthiasBP
MatthiasBP 21.09.2023 aktualisiert um 15:44:18 Uhr
Goto Top
Tut mir leid, das war mir nicht klar. Ich bin davon ausgegangen, dass das was nach der Order kommt das auszulesende ist, weil ich es im Makro gesehen hatte und dachte es funktioniert genauso. Da stand 25 Zeichen bzw. die hatte ich auch eingetragen. Danke fuer den Link, schaue ich mir an.

Das stimmt die XXX sind die Schwaerzungen. Alles von den Nullen bis zum -001 ist die Bestellnummer. Das hat es auch richtig ausgelesen. 11 Stellen vor und drei nach dem Bindestrich. Habe allerdings auch mit Nullen ersetzt.

Ok, vielen Dank fuer deine Hilfe.
Gruss
Matthias