textract scraping read python pdf text-extraction pdf-scraping

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()



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.



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...''