Wyodrębnianie tekstu z pliku PDF za pomocą PDFMiner w Pythonie?

89

Szukam dokumentacji lub przykładów, jak wyodrębnić tekst z pliku PDF za pomocą PDFMiner z Pythonem.

Wygląda na to, że PDFMiner zaktualizował swoje API, a wszystkie odpowiednie przykłady, które znalazłem, zawierają przestarzały kod (klasy i metody uległy zmianie). Biblioteki, które znalazłem, które ułatwiają wyodrębnianie tekstu z pliku PDF, używają starej składni PDFMiner, więc nie jestem pewien, jak to zrobić.

W tej chwili patrzę tylko na kod źródłowy, aby zobaczyć, czy mogę to rozgryźć.

DuckPuncher
źródło
1
Zajrzyj na stackoverflow.com/help/how-to-ask i stackoverflow.com/help/mcve i zaktualizuj swoją odpowiedź, aby była w lepszym formacie i była zgodna z wytycznymi.
Parker
Której dystrybucji Pythona używasz, 2.7.x czy 3.xx? Należy zauważyć, że autor wyraźnie wyszczególnił, że PDFminernie działa z Pythonem 3.xx To może być powód, dla którego otrzymujesz importbłędy. Powinieneś użyć pdfminer3kjeśli tak, ponieważ jest to stały import Pythona 3 wspomnianej biblioteki.
NullDev
@Nanashi, przepraszam, zapomniałem dodać moją wersję Pythona. Jest to 2,7, więc to nie jest problem. Przeglądałem kod źródłowy i wygląda na to, że niektóre rzeczy zmienili, dlatego import się psuje. Nie mogę znaleźć żadnej dokumentacji do PDFMiner lub po prostu nad tym pracowałbym :(
DuckPuncher
Właśnie dosłownie zainstalowałem PDFminerz GitHub i importuje się dobrze. Czy możesz wysłać swój kod i zamieścić również pełne śledzenie błędów?
NullDev
@Nanashi, Jak powiedziałem w moim pierwotnym pytaniu, biblioteki, które opierają się na PDFMiner, psują się przed zakończeniem importu wraz z każdym przykładem, który mogę znaleźć. To nie jest problem PDFMiner. To ja szukam dokumentacji lub przykładu korzystania z PDFMiner. Wszystko, co mogę znaleźć, używa starej składni PDFMiner. Poszedłem dalej i zredagowałem moje pytanie dla jasności. Myślę, że sprawiłem, że było to bardziej zagmatwane, niż powinno. Przepraszam za to.
DuckPuncher

Odpowiedzi:

184

Oto działający przykład wyodrębniania tekstu z pliku PDF przy użyciu aktualnej wersji PDFMiner (wrzesień 2016 r.)

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from io import StringIO

def convert_pdf_to_txt(path):
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    fp = open(path, 'rb')
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    password = ""
    maxpages = 0
    caching = True
    pagenos=set()

    for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages, password=password,caching=caching, check_extractable=True):
        interpreter.process_page(page)

    text = retstr.getvalue()

    fp.close()
    device.close()
    retstr.close()
    return text

Struktura PDFMinera zmieniła się niedawno, więc powinno to działać w przypadku wyodrębniania tekstu z plików PDF.

Edycja : nadal działa od 7 czerwca 2018 r. Zweryfikowano w Pythonie w wersji 3.x

Edycja: Rozwiązanie działa z Pythonem 3.7 od 3 października 2019 r. Korzystałem z biblioteki Python pdfminer.six, wydanej w listopadzie 2018 r.

DuckPuncher
źródło
2
działa dobrze, ale jak sobie radzić ze spacjami na przykład w nazwach? przypuszczam, że mam plik PDF zawierający 4 kolumny, w których mam imię i nazwisko w jednej kolumnie, teraz jest analizowany z imieniem w jednym wierszu i nazwiskiem w jednym wierszu, oto przykład docdro.id/rRyef3x
Deusdeorum
2
Obecnie pojawia się błąd importu z tym kodem: ImportError: Brak modułu o nazwie „pdfminer.pdfpage”
Jeffrey Swan
1
Dzięki, że działa na Pythonie w wersji 2.7.12 i na ubuntu 16.04, chociaż lepiej byłoby załadować dokument pdf z kodowaniem utf-8, ponieważ mój przykładowy plik PDF ma pewien problem z kodowaniem, więc spróbuj tego po zakodowaniu za pomocą utf-8 i rozwiąż problem problem ... import sys reload(sys) sys.setdefaultencoding('utf-8')
sib10
2
@DuckPuncher, czy nadal działa? Musiałem zmienić na file(path, 'rb')`open (path, 'rb'), aby mój działał.
wyciągnięto
2
Nadal pracuje dla użytkowników Python3.7. Zainstalowany pakiet pdfminer.six == 20181108. Jak dotąd najlepsze rozwiązanie dla mojego przypadku i porównałem wiele rozwiązań.
aze45sq6d
30

świetna odpowiedź od DuckPuncher, dla Python3 upewnij się, że zainstalowałeś pdfminer2 i wykonaj:

import io

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage


def convert_pdf_to_txt(path):
    rsrcmgr = PDFResourceManager()
    retstr = io.StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    fp = open(path, 'rb')
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    password = ""
    maxpages = 0
    caching = True
    pagenos = set()

    for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages,
                                  password=password,
                                  caching=caching,
                                  check_extractable=True):
        interpreter.process_page(page)



    fp.close()
    device.close()
    text = retstr.getvalue()
    retstr.close()
    return text
juan Isaza
źródło
1
To nie działa dla mnie: ModuleNotFoundError: Brak modułu o nazwie „pdfminer.pdfpage”. Używam Pythona 3.6
Atti
@Atti, na wszelki wypadek upewnij się, że masz zainstalowany pdfminer2, ponieważ jest inny pakiet pdfminer (nienawidzę tego). Działa dla wersji pdfminer2 == 20151206 podczas zamrażania pip3.
Juan Isaza
5
dzięki, w końcu działało, zainstalowałem pdfminer.six z conda forge
Atti
8
W przypadku Pythona 3 zalecanym pakietem jest pdfminer.six
Mike Driscoll
Czy to wciąż aktualne. Otrzymuję tę samą ImportError:wiadomość
14

Działa to w maju 2020 roku przy użyciu PDFminer six w Python3.

Instalowanie pakietu

$ pip install pdfminer.six

Importowanie pakietu

from pdfminer.high_level import extract_text

Korzystanie z pliku PDF zapisanego na dysku

text = extract_text('report.pdf')

Lub alternatywnie:

with open('report.pdf','rb') as f:
    text = extract_text(f)

Korzystanie z PDF już w pamięci

Jeśli plik PDF jest już w pamięci, na przykład jeśli został pobrany z Internetu za pomocą biblioteki żądań, można go przekonwertować na strumień za pomocą iobiblioteki:

import io

response = requests.get(url)
text = extract_text(io.BytesIO(response.content))

Wydajność i niezawodność w porównaniu z PyPDF2

PDFminer.six działa bardziej niezawodnie niż PyPDF2 (który nie działa w przypadku niektórych typów plików PDF), w szczególności PDF w wersji 1.7

Jednak ekstrakcja tekstu za pomocą PDFminer.six jest znacznie wolniejsza niż PyPDF2 o współczynnik 6.

Zsynchronizowałem wyodrębnianie tekstu z timeit15-calowym MBP (2018), synchronizując tylko funkcję ekstrakcji (bez otwierania pliku itp.) Z 10-stronicowym plikiem PDF i otrzymałem następujące wyniki:

PDFminer.six: 2.88 sec
PyPDF2:       0.45 sec

pdfminer.six ma również ogromne rozmiary, wymagając pycryptodome, który wymaga zainstalowania GCC i innych rzeczy, wypychając minimalny obraz dockera instalacyjnego w Alpine Linux z 80 MB do 350 MB. PyPDF2 nie ma zauważalnego wpływu na pamięć.

Cornelius Roemer
źródło
To podejście mogło się zepsuć od ostatniej aktualizacji. Obecnie ImportError: cannot import name 'open_filename' from 'pdfminer.utils'pojawia się błąd podczas uruchamianiafrom pdfminer.high_level import extract_text
Dalsze czytanie
1
Aktualizacja: naprawiłem to, tworząc nowy venv i ponownie instalując pdfminer.six. Wydaje mi się, że jeden z innych pakietów pdf, które wypróbowałem wcześniej, w jakiś sposób przeszkadzał.
Dalsze czytanie
11

Pełne ujawnienie, jestem jednym z opiekunów pdfminer.six.

Obecnie istnieje wiele interfejsów API do wyodrębniania tekstu z pliku PDF, w zależności od potrzeb. W tle wszystkie te interfejsy API używają tej samej logiki do analizowania i analizowania układu.

(We wszystkich przykładach przyjęto, że plik PDF nosi nazwę przykład.pdf )

Wiersz poleceń

Jeśli chcesz wyodrębnić tekst tylko raz, możesz użyć narzędzia wiersza poleceń pdf2txt.py:

$ pdf2txt.py example.pdf

Interfejs API wysokiego poziomu

Jeśli chcesz wyodrębnić tekst za pomocą Pythona, możesz użyć interfejsu API wysokiego poziomu. To podejście jest najlepszym rozwiązaniem, jeśli chcesz programowo wyodrębnić tekst z wielu plików PDF.

from pdfminer.high_level import extract_text

text = extract_text('example.pdf')

Komponowalne API

Istnieje również komponowalny interfejs API, który zapewnia dużą elastyczność w obsłudze wynikowych obiektów. Na przykład możesz zaimplementować własny algorytm układu, używając tego. Ta metoda jest sugerowana w innych odpowiedziach, ale poleciłbym ją tylko wtedy, gdy musisz dostosować sposób zachowania pdfminer.six.

from io import StringIO

from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfparser import PDFParser

output_string = StringIO()
with open('example.pdf', 'rb') as in_file:
    parser = PDFParser(in_file)
    doc = PDFDocument(parser)
    rsrcmgr = PDFResourceManager()
    device = TextConverter(rsrcmgr, output_string, laparams=LAParams())
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    for page in PDFPage.create_pages(doc):
        interpreter.process_page(page)

print(output_string.getvalue())
Pieter
źródło
0

ten kod jest testowany z pdfminer dla Pythona 3 (pdfminer-20191125)

from pdfminer.layout import LAParams
from pdfminer.converter import PDFPageAggregator
from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.pdfinterp import PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.layout import LTTextBoxHorizontal

def parsedocument(document):
    # convert all horizontal text into a lines list (one entry per line)
    # document is a file stream
    lines = []
    rsrcmgr = PDFResourceManager()
    laparams = LAParams()
    device = PDFPageAggregator(rsrcmgr, laparams=laparams)
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    for page in PDFPage.get_pages(document):
            interpreter.process_page(page)
            layout = device.get_result()
            for element in layout:
                if isinstance(element, LTTextBoxHorizontal):
                    lines.extend(element.get_text().splitlines())
    return lines
Brault Gilbert
źródło
Mam pliki PDF, które mogę przekonwertować za pomocą narzędzia Nitro Pro. Kiedy próbuję przekonwertować ten sam plik PDF za pomocą zamieszczonego tutaj kodu, otrzymuję wynik sugerujący, że wystąpił błąd uprawnień. Oto wynik: ('z kolekcji SAGE Social Science. Wszelkie prawa zastrzeżone. \ N \ n \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c ')
b00kgrrl
Co masz na myśli przez strumień plików?
Vincent
@Vincent z otwartym (plik, „rb”) jako strumień: [...]
Rodrigo Formighieri
czy uda ci się uzyskać ten plik jako tabelę / pandy idealnie? groupe-psa.com/en/publication/monthly-world-sales-march-2020
Nono London