Existe-t-il un module python pour convertir des fichiers PDF en texte? J'ai essayé un morceau de code trouvé dans Activestate qui utilise pypdf mais le texte généré n'avait pas d'espace entre et n'était d'aucune utilité.
Existe-t-il un module python pour convertir des fichiers PDF en texte? J'ai essayé un morceau de code trouvé dans Activestate qui utilise pypdf mais le texte généré n'avait pas d'espace entre et n'était d'aucune utilité.
Réponses:
Essayez PDFMiner . Il peut extraire le texte des fichiers PDF au format HTML, SGML ou "PDF balisé".
Le format PDF balisé semble être le plus propre, et supprimer les balises XML ne laisse que le texte nu.
Une version Python 3 est disponible sous:
Le package PDFMiner a changé depuis la publication de codeape .
MODIFIER (encore):
PDFMiner a été mis à jour à nouveau dans la version 20100213
Vous pouvez vérifier la version que vous avez installée avec les éléments suivants:
>>> import pdfminer
>>> pdfminer.__version__
'20100213'
Voici la version mise à jour (avec des commentaires sur ce que j'ai changé / ajouté):
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()
Modifier (encore une fois):
Voici une mise à jour pour la dernière version en pypi , 20100619p1
. En bref , j'ai remplacé LTTextItem
avec LTChar
et passé une instance de LAParams au constructeur de 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()
MODIFIER (encore une fois):
Mise à jour pour la version 20110515
(merci à 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
à LTChar
. unixuser.org/~euske/python/pdfminer/index.html#changes
20110515
par votre commentaire.
Étant donné qu'aucune de ces solutions ne prend en charge la dernière version de PDFMiner, j'ai écrit une solution simple qui retournera le texte d'un pdf à l'aide de PDFMiner. Cela fonctionnera pour ceux qui obtiennent des erreurs d'importation avecprocess_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])
Voir ci-dessous le code qui fonctionne pour 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
, outre les parenthèses évidentes après la print
commande, il faut remplacer la file
commande par open
et importer à StringIO
partir du packageio
Pdftotext Un programme open source (partie de Xpdf) que vous pourriez appeler depuis python (pas ce que vous avez demandé mais qui pourrait être utile). Je l'ai utilisé sans problème. Je pense que Google l'utilise dans Google Desktop.
-layout
possibilité de conserver le texte dans la même position que dans le PDF. Maintenant, si seulement je pouvais comprendre comment y diriger le contenu d'un PDF.
pdftotext
semble fonctionner très bien, mais il a besoin d'un deuxième argument qui est un trait d'union, si vous voulez voir les résultats sur stdout.
find . -iname "*.pdf" -exec pdftotext -enc UTF-8 -eol unix -raw {} \;
Par défaut, les fichiers générés prennent le nom d'origine avec l' .txt
extension.
pyPDF fonctionne bien (en supposant que vous travaillez avec des fichiers PDF bien formés). Si tout ce que vous voulez c'est le texte (avec des espaces), vous pouvez simplement faire:
import pyPdf
pdf = pyPdf.PdfFileReader(open(filename, "rb"))
for page in pdf.pages:
print page.extractText()
Vous pouvez également accéder facilement aux métadonnées, aux données d'image, etc.
Un commentaire dans les notes de code extractText:
Recherchez toutes les commandes de dessin de texte, dans l'ordre dans lequel elles sont fournies dans le flux de contenu, et extrayez le texte. Cela fonctionne bien pour certains fichiers PDF, mais mal pour d'autres, selon le générateur utilisé. Cela sera affiné à l'avenir. Ne vous fiez pas à l'ordre du texte sortant de cette fonction, car il changera si cette fonction est rendue plus sophistiquée.
Que ce soit ou non un problème dépend de ce que vous faites avec le texte (par exemple, si l'ordre n'a pas d'importance, ça va, ou si le générateur ajoute du texte au flux dans l'ordre où il sera affiché, ça va) . J'ai un code d'extraction pyPdf en utilisation quotidienne, sans aucun problème.
Vous pouvez également utiliser assez facilement pdfminer comme bibliothèque. Vous avez accès au modèle de contenu du pdf et pouvez créer votre propre extraction de texte. J'ai fait cela pour convertir le contenu pdf en texte séparé par des points-virgules, en utilisant le code ci-dessous.
La fonction trie simplement les objets de contenu TextItem selon leurs coordonnées y et x, et génère des éléments avec la même coordonnée y qu'une seule ligne de texte, en séparant les objets sur la même ligne avec ';' personnages.
En utilisant cette approche, j'ai pu extraire du texte à partir d'un pdf à partir duquel aucun autre outil n'a été en mesure d'extraire du contenu adapté à une analyse plus approfondie. D'autres outils que j'ai essayés incluent pdftotext, ps2ascii et l'outil en ligne pdftextonline.com.
pdfminer est un outil précieux pour le grattage de 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()
MISE À JOUR :
Le code ci-dessus est écrit contre une ancienne version de l'API, voir mon commentaire ci-dessous.
pdfminer
, non pdflib
). Je vous suggère de jeter un œil à la source de pdf2txt.py
la source PDFminer, le code ci-dessus a été inspiré par l'ancienne version de ce fichier.
slate
est un projet qui rend très simple l'utilisation de PDFMiner depuis une bibliothèque:
>>> with open('example.pdf') as f:
... doc = slate.PDF(f)
...
>>> doc
[..., ..., ...]
>>> doc[1]
'Text from page 2...'
J'avais besoin de convertir un PDF spécifique en texte brut dans un module python. J'ai utilisé PDFMiner 20110515, après avoir lu leur outil pdf2txt.py , j'ai écrit ce simple extrait:
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
Réutilisation du code pdf2txt.py fourni avec pdfminer; vous pouvez créer une fonction qui prendra un chemin vers le pdf; éventuellement, un type extérieur (txt | html | xml | tag) et opte comme la ligne de commande pdf2txt {'-o': '/path/to/outfile.txt' ...}. Par défaut, vous pouvez appeler:
convert_pdf(path)
Un fichier texte sera créé, un frère sur le système de fichiers au pdf d'origine.
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 m'a donné peut-être une ligne [page 1 sur 7 ...] sur chaque page d'un fichier pdf que j'ai essayé avec.
La meilleure réponse que j'ai jusqu'à présent est pdftoipe, ou le code c ++ est basé sur Xpdf.
voir ma question pour savoir à quoi ressemble la sortie de pdftoipe.
De plus, il existe PDFTextStream, une bibliothèque Java commerciale qui peut également être utilisée à partir de Python.
J'ai utilisé pdftohtml
avec l' -xml
argument, lire le résultat avec subprocess.Popen()
, qui vous donnera x coord, y coord, largeur, hauteur et police, de chaque extrait de texte dans le pdf. Je pense que c'est ce que 'evince' utilise probablement aussi parce que les mêmes messages d'erreur se répandent.
Si vous devez traiter des données en colonnes, cela devient un peu plus compliqué car vous devez inventer un algorithme qui convient à votre fichier pdf. Le problème est que les programmes qui créent des fichiers PDF ne présentent pas nécessairement le texte dans un format logique. Vous pouvez essayer des algorithmes de tri simples et cela fonctionne parfois, mais il peut y avoir de petits «retardataires» et «errants», des morceaux de texte qui ne sont pas mis dans l'ordre que vous pensiez qu'ils le feraient. Il faut donc faire preuve de créativité.
Il m'a fallu environ 5 heures pour en trouver un pour les fichiers PDF sur lesquels je travaillais. Mais cela fonctionne plutôt bien maintenant. Bonne chance.
J'ai trouvé cette solution aujourd'hui. Fonctionne très bien pour moi. Même le rendu des pages PDF en images PNG. http://www.swftools.org/gfx_tutorial.html