scraping - Módulo de Python para convertir PDF a texto
read pdf python (13)
Además, hay PDFTextStream que es una biblioteca comercial de Java que también se puede usar desde Python.
¿Cuáles son los mejores módulos de Python para convertir archivos PDF en texto?
Como ninguna de estas soluciones es compatible con la última versión de PDFMiner, escribí una solución simple que devolverá el texto de un pdf utilizando PDFMiner. Esto funcionará para aquellos que obtienen errores de importación con process_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])
Vea a continuación el código que funciona para 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])
El paquete de http://www.unixuser.org/~euske/python/pdfminer/index.html ha cambiado desde que se publicó el codeape .
EDITAR (otra vez):
PDFMiner se ha actualizado de nuevo en la versión 20100213
Puede verificar la versión que ha instalado con lo siguiente:
>>> import pdfminer
>>> pdfminer.__version__
''20100213''
Aquí está la versión actualizada (con comentarios sobre lo que cambié / agregué):
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()
Editar (una vez más):
Aquí hay una actualización para la última versión en pypi , 20100619p1
. En resumen, reemplacé LTTextItem
con LTChar
y pasé una instancia de LAParams al constructor 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()
EDITAR (una vez más):
Actualizado para la versión 20110515
(¡gracias a 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()
Encontré esa solución hoy. Funciona muy bien para mí. Incluso renderizando páginas PDF a imágenes PNG. http://www.swftools.org/gfx_tutorial.html
He usado pdftohtml con el argumento ''-xml'', leí el resultado con subprocess.Popen (), que le dará x coorden, y coordenada, ancho, alto y fuente, de cada ''fragmento de texto'' en el pdf. Creo que esto es lo que probablemente usa ''evidencia'' también porque se emiten los mismos mensajes de error.
Si necesita procesar datos en columnas, se vuelve un poco más complicado ya que tiene que inventar un algoritmo que se adapte a su archivo pdf. El problema es que los programas que hacen archivos PDF no necesariamente disponen el texto en un formato lógico. Puedes probar algoritmos de clasificación simples y funciona a veces, pero puede haber pequeños ''stragglers'' y ''strays'', piezas de texto que no se ponen en el orden que pensabas que serían ... así que tienes que ser creativo.
Me tomó cerca de 5 horas encontrar uno para los archivos PDF en los que estaba trabajando. Pero funciona bastante bien ahora. Buena suerte.
Necesitaba convertir un PDF específico a texto plano dentro de un módulo de python. Usé http://www.unixuser.org/~euske/python/pdfminer/index.html 20110515, después de leer su herramienta pdf2txt.py , escribí este simple fragmento de pdf2txt.py :
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()
PDFminer me dio quizás una línea [página 1 de 7 ...] en cada página de un archivo pdf que probé con él.
La mejor respuesta que tengo hasta ahora es pdftoipe, o el código c ++ está basado en Xpdf.
Vea mi pregunta para ver cómo se ve la salida de pdftoipe.
Pruebe PDFMiner. Puede extraer texto de archivos PDF en formato HTML, SGML o "Tagged PDF".
http://www.unixuser.org/~euske/python/pdfminer/index.html
El formato PDF etiquetado parece ser el más limpio, y eliminar las etiquetas XML deja solo el texto.
Una versión de Python 3 está disponible en:
Reutilizando el código pdf2txt.py que viene con pdfminer; puedes hacer una función que tome un camino hacia el pdf; opcionalmente, un outtype (txt | html | xml | tag) y opta como la línea de comandos pdf2txt {''-o'': ''/path/to/outfile.txt'' ...}. Por defecto, puedes llamar a:
convert_pdf(path)
Se creará un archivo de texto, un hermano en el sistema de archivos al pdf original.
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
También puedes usar fácilmente pdfminer como una biblioteca. Tiene acceso al modelo de contenido del pdf y puede crear su propia extracción de texto. Hice esto para convertir el contenido del pdf en un texto separado por punto y coma, usando el siguiente código.
La función simplemente ordena los objetos de contenido TextItem de acuerdo con sus coordenadas yyx, y genera elementos con la misma coordenada y como una línea de texto, separando los objetos en la misma línea con '';'' caracteres.
Usando este enfoque, pude extraer texto de un pdf del cual ninguna otra herramienta pudo extraer contenido adecuado para analizarlo más. Otras herramientas que probé incluyen pdftotext, ps2ascii y la herramienta en línea pdftextonline.com.
pdfminer es una herramienta invaluable para el raspado 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()
ACTUALIZACIÓN :
El código anterior está escrito en contra de una versión anterior de la API, vea mi comentario a continuación.
Pdftotext Un programa de código abierto (parte de Xpdf) al que puedes llamar desde python (no es lo que pediste pero podría ser útil). Lo he usado sin problemas. Creo que Google lo usa en google desktop.
pyPDF funciona bien (suponiendo que está trabajando con archivos PDF bien formados). Si todo lo que quieres es el texto (con espacios), puedes hacerlo:
import pyPdf
pdf = pyPdf.PdfFileReader(open(filename, "rb"))
for page in pdf.pages:
print page.extractText()
También puede obtener fácilmente acceso a los metadatos, datos de imágenes, etc.
Un comentario en las notas del código extractText:
Localice todos los comandos de dibujo de texto, en el orden en que se proporcionan en la secuencia de contenido, y extraiga el texto. Esto funciona bien para algunos archivos PDF, pero no para otros, dependiendo del generador utilizado. Esto será refinado en el futuro. No confíe en el orden del texto que sale de esta función, ya que cambiará si esta función se hace más sofisticada.
Si esto es un problema o no, depende de lo que esté haciendo con el texto (por ejemplo, si el orden no importa, está bien, o si el generador agrega texto a la secuencia en el orden en que se mostrará, está bien) . Tengo el código de extracción pyPdf en uso diario, sin ningún problema.
slate
es un proyecto que simplifica el uso de PDFMiner desde una biblioteca:
>>> with open(''example.pdf'') as f:
... doc = slate.PDF(f)
...
>>> doc
[..., ..., ...]
>>> doc[1]
''Text from page 2...''