Czy istnieje moduł Pythona do konwersji plików PDF na tekst? Wypróbowałem jeden fragment kodu znaleziony w Activestate, który używa pypdf, ale w wygenerowanym tekście nie było spacji i nie było pożytku.
Czy istnieje moduł Pythona do konwersji plików PDF na tekst? Wypróbowałem jeden fragment kodu znaleziony w Activestate, który używa pypdf, ale w wygenerowanym tekście nie było spacji i nie było pożytku.
Odpowiedzi:
Wypróbuj PDFMiner . Może wyodrębniać tekst z plików PDF w formacie HTML, SGML lub „Tagged PDF”.
Format Tagged PDF wydaje się być najczystszy, a usunięcie tagów XML pozostawia tylko czysty tekst.
Wersja Python 3 jest dostępna pod:
PDFMiner pakiet zmieniła się od codeape pisał.
EDYCJA (ponownie):
PDFMiner został ponownie zaktualizowany w wersji 20100213
Możesz sprawdzić zainstalowaną wersję za pomocą:
>>> import pdfminer
>>> pdfminer.__version__
'20100213'
Oto zaktualizowana wersja (z komentarzami do tego, co zmieniłem / dodałem):
def pdf_to_csv(filename):
from cStringIO import StringIO #<-- added so you can copy/paste this to try it
from pdfminer.converter import LTTextItem, TextConverter
from pdfminer.pdfparser import PDFDocument, PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
class CsvConverter(TextConverter):
def __init__(self, *args, **kwargs):
TextConverter.__init__(self, *args, **kwargs)
def end_page(self, i):
from collections import defaultdict
lines = defaultdict(lambda : {})
for child in self.cur_item.objs:
if isinstance(child, LTTextItem):
(_,_,x,y) = child.bbox #<-- changed
line = lines[int(-y)]
line[x] = child.text.encode(self.codec) #<-- changed
for y in sorted(lines.keys()):
line = lines[y]
self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
self.outfp.write("\n")
# ... the following part of the code is a remix of the
# convert() function in the pdfminer/tools/pdf2text module
rsrc = PDFResourceManager()
outfp = StringIO()
device = CsvConverter(rsrc, outfp, codec="utf-8") #<-- changed
# becuase my test documents are utf-8 (note: utf-8 is the default codec)
doc = PDFDocument()
fp = open(filename, 'rb')
parser = PDFParser(fp) #<-- changed
parser.set_document(doc) #<-- added
doc.set_parser(parser) #<-- added
doc.initialize('')
interpreter = PDFPageInterpreter(rsrc, device)
for i, page in enumerate(doc.get_pages()):
outfp.write("START PAGE %d\n" % i)
interpreter.process_page(page)
outfp.write("END PAGE %d\n" % i)
device.close()
fp.close()
return outfp.getvalue()
Edytuj (jeszcze raz):
Tutaj jest aktualizacja do najnowszej wersji w PyPI , 20100619p1
. W skrócie Wymieniłem LTTextItem
z LTChar
i przeszedł wystąpienie LAParams do konstruktora CsvConverter.
def pdf_to_csv(filename):
from cStringIO import StringIO
from pdfminer.converter import LTChar, TextConverter #<-- changed
from pdfminer.layout import LAParams
from pdfminer.pdfparser import PDFDocument, PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
class CsvConverter(TextConverter):
def __init__(self, *args, **kwargs):
TextConverter.__init__(self, *args, **kwargs)
def end_page(self, i):
from collections import defaultdict
lines = defaultdict(lambda : {})
for child in self.cur_item.objs:
if isinstance(child, LTChar): #<-- changed
(_,_,x,y) = child.bbox
line = lines[int(-y)]
line[x] = child.text.encode(self.codec)
for y in sorted(lines.keys()):
line = lines[y]
self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
self.outfp.write("\n")
# ... the following part of the code is a remix of the
# convert() function in the pdfminer/tools/pdf2text module
rsrc = PDFResourceManager()
outfp = StringIO()
device = CsvConverter(rsrc, outfp, codec="utf-8", laparams=LAParams()) #<-- changed
# becuase my test documents are utf-8 (note: utf-8 is the default codec)
doc = PDFDocument()
fp = open(filename, 'rb')
parser = PDFParser(fp)
parser.set_document(doc)
doc.set_parser(parser)
doc.initialize('')
interpreter = PDFPageInterpreter(rsrc, device)
for i, page in enumerate(doc.get_pages()):
outfp.write("START PAGE %d\n" % i)
if page is not None:
interpreter.process_page(page)
outfp.write("END PAGE %d\n" % i)
device.close()
fp.close()
return outfp.getvalue()
EDYCJA (jeszcze raz):
Zaktualizowano do wersji 20110515
(dzięki Oeufcoque Penteano!):
def pdf_to_csv(filename):
from cStringIO import StringIO
from pdfminer.converter import LTChar, TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfparser import PDFDocument, PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
class CsvConverter(TextConverter):
def __init__(self, *args, **kwargs):
TextConverter.__init__(self, *args, **kwargs)
def end_page(self, i):
from collections import defaultdict
lines = defaultdict(lambda : {})
for child in self.cur_item._objs: #<-- changed
if isinstance(child, LTChar):
(_,_,x,y) = child.bbox
line = lines[int(-y)]
line[x] = child._text.encode(self.codec) #<-- changed
for y in sorted(lines.keys()):
line = lines[y]
self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
self.outfp.write("\n")
# ... the following part of the code is a remix of the
# convert() function in the pdfminer/tools/pdf2text module
rsrc = PDFResourceManager()
outfp = StringIO()
device = CsvConverter(rsrc, outfp, codec="utf-8", laparams=LAParams())
# becuase my test documents are utf-8 (note: utf-8 is the default codec)
doc = PDFDocument()
fp = open(filename, 'rb')
parser = PDFParser(fp)
parser.set_document(doc)
doc.set_parser(parser)
doc.initialize('')
interpreter = PDFPageInterpreter(rsrc, device)
for i, page in enumerate(doc.get_pages()):
outfp.write("START PAGE %d\n" % i)
if page is not None:
interpreter.process_page(page)
outfp.write("END PAGE %d\n" % i)
device.close()
fp.close()
return outfp.getvalue()
LTTextItem
się LTChar
. unixuser.org/~euske/python/pdfminer/index.html#changes
20110515
dla twojego komentarza.
Ponieważ żadne z tych rozwiązań nie obsługuje najnowszej wersji PDFMiner, napisałem proste rozwiązanie, które zwróci tekst pdf za pomocą PDFMiner. Działa to dla tych, którzy mają błędy importowaniaprocess_pdf
import sys
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter
from pdfminer.layout import LAParams
from cStringIO import StringIO
def pdfparser(data):
fp = file(data, 'rb')
rsrcmgr = PDFResourceManager()
retstr = StringIO()
codec = 'utf-8'
laparams = LAParams()
device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
# Create a PDF interpreter object.
interpreter = PDFPageInterpreter(rsrcmgr, device)
# Process each page contained in the document.
for page in PDFPage.get_pages(fp):
interpreter.process_page(page)
data = retstr.getvalue()
print data
if __name__ == '__main__':
pdfparser(sys.argv[1])
Zobacz poniżej kod, który działa dla Python 3:
import sys
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter
from pdfminer.layout import LAParams
import io
def pdfparser(data):
fp = open(data, 'rb')
rsrcmgr = PDFResourceManager()
retstr = io.StringIO()
codec = 'utf-8'
laparams = LAParams()
device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
# Create a PDF interpreter object.
interpreter = PDFPageInterpreter(rsrcmgr, device)
# Process each page contained in the document.
for page in PDFPage.get_pages(fp):
interpreter.process_page(page)
data = retstr.getvalue()
print(data)
if __name__ == '__main__':
pdfparser(sys.argv[1])
python3
oprócz oczywistych nawiasów po print
poleceniu, należy zastąpić file
polecenie open
i zaimportować StringIO
z pakietuio
Pdftotext Program typu open source (część Xpdf), który możesz wywołać z pythona (nie to, o co prosiłeś, ale może być przydatny). Użyłem go bez problemów. Myślę, że Google używa go w Google Desktop.
-layout
opcją utrzymania tekstu w tej samej pozycji, co w pliku PDF. Teraz gdybym tylko mógł wymyślić, jak umieścić w nim zawartość pliku PDF.
pdftotext
wydaje się działać bardzo dobrze, ale potrzebuje drugiego argumentu, który jest łącznikiem, jeśli chcesz zobaczyć wyniki na standardowym wyjściu.
find . -iname "*.pdf" -exec pdftotext -enc UTF-8 -eol unix -raw {} \;
Domyślnie wygenerowane pliki przyjmują oryginalną nazwę z .txt
rozszerzeniem.
pyPDF działa dobrze (zakładając, że pracujesz z dobrze sformatowanymi plikami PDF). Jeśli wszystko, czego chcesz, to tekst (ze spacjami), możesz po prostu:
import pyPdf
pdf = pyPdf.PdfFileReader(open(filename, "rb"))
for page in pdf.pages:
print page.extractText()
Możesz także łatwo uzyskać dostęp do metadanych, danych obrazu i tak dalej.
Komentarz w uwagach do kodu extractText:
Znajdź wszystkie polecenia rysowania tekstu w kolejności, w jakiej znajdują się w strumieniu treści, i wyodrębnij tekst. Działa to dobrze dla niektórych plików PDF, ale źle dla innych, w zależności od użytego generatora. Zostanie to poprawione w przyszłości. Nie polegaj na kolejności tekstu wychodzącego z tej funkcji, ponieważ zmieni się ona, jeśli ta funkcja stanie się bardziej zaawansowana.
To, czy jest to problem, zależy od tego, co robisz z tekstem (np. Jeśli kolejność nie ma znaczenia, jest w porządku, lub jeśli generator dodaje tekst do strumienia w kolejności, w jakiej będzie wyświetlany, jest w porządku) . Mam kod ekstrakcji pyPdf do codziennego użytku, bez żadnych problemów.
Możesz również dość łatwo użyć pdfminer jako biblioteki. Masz dostęp do modelu treści pdf i możesz stworzyć własną ekstrakcję tekstu. Zrobiłem to, aby przekonwertować zawartość pdf na tekst oddzielony średnikiem, używając poniższego kodu.
Funkcja po prostu sortuje obiekty treści TextItem według ich współrzędnych yi x, i wysyła elementy o tej samej współrzędnej y jak jedna linia tekstu, oddzielając obiekty w tej samej linii za pomocą „;” postacie.
Dzięki takiemu podejściu udało mi się wyodrębnić tekst z pliku pdf, z którego żadne inne narzędzie nie było w stanie wyodrębnić treści odpowiedniej do dalszej analizy. Inne narzędzia, które wypróbowałem, to pdftotext, ps2ascii i narzędzie online pdftextonline.com.
pdfminer to nieocenione narzędzie do skrobania pdf.
def pdf_to_csv(filename):
from pdflib.page import TextItem, TextConverter
from pdflib.pdfparser import PDFDocument, PDFParser
from pdflib.pdfinterp import PDFResourceManager, PDFPageInterpreter
class CsvConverter(TextConverter):
def __init__(self, *args, **kwargs):
TextConverter.__init__(self, *args, **kwargs)
def end_page(self, i):
from collections import defaultdict
lines = defaultdict(lambda : {})
for child in self.cur_item.objs:
if isinstance(child, TextItem):
(_,_,x,y) = child.bbox
line = lines[int(-y)]
line[x] = child.text
for y in sorted(lines.keys()):
line = lines[y]
self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
self.outfp.write("\n")
# ... the following part of the code is a remix of the
# convert() function in the pdfminer/tools/pdf2text module
rsrc = PDFResourceManager()
outfp = StringIO()
device = CsvConverter(rsrc, outfp, "ascii")
doc = PDFDocument()
fp = open(filename, 'rb')
parser = PDFParser(doc, fp)
doc.initialize('')
interpreter = PDFPageInterpreter(rsrc, device)
for i, page in enumerate(doc.get_pages()):
outfp.write("START PAGE %d\n" % i)
interpreter.process_page(page)
outfp.write("END PAGE %d\n" % i)
device.close()
fp.close()
return outfp.getvalue()
AKTUALIZACJA :
Powyższy kod jest napisany przeciwko starej wersji interfejsu API, zobacz mój komentarz poniżej.
pdfminer
nie jest pdflib
). Sugeruję zajrzeć do źródła pdf2txt.py
w źródle PDFminer, powyższy kod został zainspirowany starą wersją tego pliku.
slate
to projekt, który bardzo ułatwia korzystanie z PDFMiner z biblioteki:
>>> with open('example.pdf') as f:
... doc = slate.PDF(f)
...
>>> doc
[..., ..., ...]
>>> doc[1]
'Text from page 2...'
Musiałem przekonwertować określony plik PDF na zwykły tekst w module Pythona. Użyłem PDFMiner 20110515, po przeczytaniu ich narzędzia pdf2txt.py napisałem ten prosty fragment:
from cStringIO import StringIO
from pdfminer.pdfinterp import PDFResourceManager, process_pdf
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
def to_txt(pdf_path):
input_ = file(pdf_path, 'rb')
output = StringIO()
manager = PDFResourceManager()
converter = TextConverter(manager, output, laparams=LAParams())
process_pdf(manager, converter, input_)
return output.getvalue()
C:\Python27\Scripts\pdfminer\tools\pdf2txt.py
Zmiana przeznaczenia kodu pdf2txt.py, który jest dołączony do pdfminer; możesz stworzyć funkcję, która podąży ścieżką do pliku pdf; opcjonalnie typ wyjściowy (txt | html | xml | tag) i wybiera polecenie takie jak linia poleceń pdf2txt {'-o': '/path/to/outfile.txt' ...}. Domyślnie możesz zadzwonić:
convert_pdf(path)
Zostanie utworzony plik tekstowy, rodzeństwo w systemie plików do oryginalnego pliku pdf.
def convert_pdf(path, outtype='txt', opts={}):
import sys
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter, process_pdf
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter, TagExtractor
from pdfminer.layout import LAParams
from pdfminer.pdfparser import PDFDocument, PDFParser
from pdfminer.pdfdevice import PDFDevice
from pdfminer.cmapdb import CMapDB
outfile = path[:-3] + outtype
outdir = '/'.join(path.split('/')[:-1])
debug = 0
# input option
password = ''
pagenos = set()
maxpages = 0
# output option
codec = 'utf-8'
pageno = 1
scale = 1
showpageno = True
laparams = LAParams()
for (k, v) in opts:
if k == '-d': debug += 1
elif k == '-p': pagenos.update( int(x)-1 for x in v.split(',') )
elif k == '-m': maxpages = int(v)
elif k == '-P': password = v
elif k == '-o': outfile = v
elif k == '-n': laparams = None
elif k == '-A': laparams.all_texts = True
elif k == '-D': laparams.writing_mode = v
elif k == '-M': laparams.char_margin = float(v)
elif k == '-L': laparams.line_margin = float(v)
elif k == '-W': laparams.word_margin = float(v)
elif k == '-O': outdir = v
elif k == '-t': outtype = v
elif k == '-c': codec = v
elif k == '-s': scale = float(v)
#
CMapDB.debug = debug
PDFResourceManager.debug = debug
PDFDocument.debug = debug
PDFParser.debug = debug
PDFPageInterpreter.debug = debug
PDFDevice.debug = debug
#
rsrcmgr = PDFResourceManager()
if not outtype:
outtype = 'txt'
if outfile:
if outfile.endswith('.htm') or outfile.endswith('.html'):
outtype = 'html'
elif outfile.endswith('.xml'):
outtype = 'xml'
elif outfile.endswith('.tag'):
outtype = 'tag'
if outfile:
outfp = file(outfile, 'w')
else:
outfp = sys.stdout
if outtype == 'txt':
device = TextConverter(rsrcmgr, outfp, codec=codec, laparams=laparams)
elif outtype == 'xml':
device = XMLConverter(rsrcmgr, outfp, codec=codec, laparams=laparams, outdir=outdir)
elif outtype == 'html':
device = HTMLConverter(rsrcmgr, outfp, codec=codec, scale=scale, laparams=laparams, outdir=outdir)
elif outtype == 'tag':
device = TagExtractor(rsrcmgr, outfp, codec=codec)
else:
return usage()
fp = file(path, 'rb')
process_pdf(rsrcmgr, device, fp, pagenos, maxpages=maxpages, password=password)
fp.close()
device.close()
outfp.close()
return
PDFminer dał mi może jedną linię [strona 1 z 7 ...] na każdej stronie pliku pdf, z którym próbowałem.
Najlepsza odpowiedź, jaką do tej pory mam, to pdftoipe lub kod c ++ oparty na Xpdf.
zobacz moje pytanie, jak wygląda wyjście pdftoipe.
Dodatkowo istnieje PDFTextStream, który jest komercyjną biblioteką Java, z której można także korzystać w Pythonie.
Użyłem pdftohtml
z -xml
argumentem, przeczytaj wynik za pomocą subprocess.Popen()
, który da ci x koordyn, y koordyn, szerokość, wysokość i czcionkę każdego fragmentu tekstu w pdf. Myślę, że tego właśnie używa „evince”, ponieważ wyrzucają te same komunikaty o błędach.
Jeśli potrzebujesz przetworzyć dane kolumnowe, staje się to nieco bardziej skomplikowane, ponieważ musisz wynaleźć algorytm, który pasuje do twojego pliku pdf. Problem polega na tym, że programy tworzące pliki PDF niekoniecznie układają tekst w jakimkolwiek logicznym formacie. Możesz wypróbować proste algorytmy sortowania i to działa czasami, ale mogą być małe „marudery” i „błąki”, fragmenty tekstu, które nie są ułożone w kolejności, w jakiej się spodziewałeś. Musisz więc być kreatywny.
Zajęło mi około 5 godzin, aby znaleźć jeden dla pdf, nad którym pracowałem. Ale teraz działa całkiem dobrze. Powodzenia.
Znalazłem to rozwiązanie dzisiaj. Działa świetnie dla mnie. Nawet renderowanie stron PDF na obrazy PNG. http://www.swftools.org/gfx_tutorial.html