¿Cómo extraer texto de un archivo PDF?

Estoy tratando de extraer el texto incluido en este archivo PDF usando Python .

Estoy usando el módulo PyPDF2 y tengo el siguiente script:

 import PyPDF2 pdf_file = open('sample.pdf') read_pdf = PyPDF2.PdfFileReader(pdf_file) number_of_pages = read_pdf.getNumPages() page = read_pdf.getPage(0) page_content = page.extractText() print page_content 

Cuando ejecuto el código, obtengo la siguiente salida, que es diferente de la que se incluye en el documento PDF:

 !"#$%#$%&%$&'()*%+,-%./01'*23%4 5'%1$#26%3/%7/))/8%&)/26%8#3"%3"*%313/9#&) % 

¿Cómo puedo extraer el texto tal como está en el documento PDF?

Estaba buscando una solución simple para Python 3.x y windows. Parece que no hay soporte por parte de textract , lo cual es desafortunado, pero si está buscando una solución sencilla para el paquete tika de windows / python 3, realmente sencillo para leer archivos PDF.

 from tika import parser raw = parser.from_file('sample.pdf') print(raw['content']) 

Utilice textract.

Es compatible con muchos tipos de archivos, incluyendo archivos PDF

 import textract text = textract.process("path/to/file.extension") 

Mira este código:

 import PyPDF2 pdf_file = open('sample.pdf', 'rb') read_pdf = PyPDF2.PdfFileReader(pdf_file) number_of_pages = read_pdf.getNumPages() page = read_pdf.getPage(0) page_content = page.extractText() print page_content.encode('utf-8') 

La salida es:

 !"#$%#$%&%$&'()*%+,-%./01'*23%4 5'%1$#26%3/%7/))/8%&)/26%8#3"%3"*%313/9#&) % 

Usar el mismo código para leer un pdf de 201308FCR.pdf . La salida es normal.

Su documentación explica por qué:

 def extractText(self): """ Locate all text drawing commands, in the order they are provided in the content stream, and extract the text. This works well for some PDF files, but poorly for others, depending on the generator used. This will be refined in the future. Do not rely on the order of text coming out of this function, as it will change if this function is made more sophisticated. :return: a unicode string object. """ 

Después de probar textract (que parecía tener demasiadas dependencias) y pypdf2 (que no podía extraer el texto de los pdfs que probé) y tika (que era demasiado lento) terminé usando pdftotext de pdftotext (como ya se sugirió en otra respuesta) y acaba de llamar directamente al binario desde python (es posible que necesite adaptar la ruta al pdftotext):

 import os, subprocess SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) args = ["/usr/local/bin/pdftotext", '-enc', 'UTF-8', "{}/my-pdf.pdf".format(SCRIPT_DIR), '-'] res = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output = res.stdout.decode('utf-8') 

Hay pdftotext que hace básicamente lo mismo pero esto asume pdftotext en / usr / local / bin mientras que estoy usando esto en AWS lambda y quise usarlo desde el directorio actual.

Por cierto: para usar esto en lambda, debe colocar el binario y la dependencia en libstdc++.so En su función lambda. Yo personalmente necesitaba comstackr xpdf. Como las instrucciones para esto explotarían esta respuesta, las puse en mi blog personal .

Es posible que desee utilizar xPDF de eficacia probada y herramientas derivadas para extraer texto, ya que pyPDF2 parece tener varios problemas con la extracción de texto.

La respuesta larga es que hay muchas variaciones sobre cómo se codifica un texto dentro de PDF y que puede requerir descifrar la propia cadena de PDF, luego puede ser necesario mapear con CMAP, luego puede ser necesario analizar la distancia entre palabras y letras, etc.

En caso de que el PDF esté dañado (es decir, que muestre el texto correcto, pero al copiarlo da basura) y realmente necesite extraerlo, puede considerar convertir PDF en imagen (usando ImageMagik ) y luego usar Tesseract para obtener texto de la imagen. utilizando OCR.

El siguiente código es una solución a la pregunta en Python 3 . Antes de ejecutar el código, asegúrese de haber instalado la biblioteca PyPDF2 en su entorno. Si no está instalado, abra el símbolo del sistema y ejecute el siguiente comando:

 pip3 install PyPDF2 

Código de la solución:

 import PyPDF2 pdfFileObject = open('sample.pdf', 'rb') pdfReader = PyPDF2.PdfFileReader(pdfFileObject) count = pdfReader.numPages for i in range(count): page = pdfReader.getPage(i) print(page.extractText()) 

El pdf de varias páginas se puede extraer como texto en un solo tramo en lugar de dar un número de página individual como argumento usando el código de abajo

 import PyPDF2 import collections pdf_file = open('samples.pdf', 'rb') read_pdf = PyPDF2.PdfFileReader(pdf_file) number_of_pages = read_pdf.getNumPages() c = collections.Counter(range(number_of_pages)) for i in c: page = read_pdf.getPage(i) page_content = page.extractText() print page_content.encode('utf-8') 

Puede utilizar PDFtoText https://github.com/jalan/pdftotext

PDF a texto mantiene la sangría de formato de texto, no importa si tiene tablas.

PyPDF2 en algunos casos ignora los espacios en blanco y hace que el texto resultante sea un desastre, pero yo uso PyMuPDF y estoy muy satisfecho de que pueda usar este enlace para obtener más información

Estoy agregando código para lograr esto: está funcionando bien para mí:

 # This works in python 3 # required python packages # tabula-py==1.0.0 # PyPDF2==1.26.0 # Pillow==4.0.0 # pdfminer.six==20170720 import os import shutil import warnings from io import StringIO import requests import tabula from PIL import Image from PyPDF2 import PdfFileWriter, PdfFileReader from pdfminer.converter import TextConverter from pdfminer.layout import LAParams from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter from pdfminer.pdfpage import PDFPage warnings.filterwarnings("ignore") def download_file(url): local_filename = url.split('/')[-1] local_filename = local_filename.replace("%20", "_") r = requests.get(url, stream=True) print(r) with open(local_filename, 'wb') as f: shutil.copyfileobj(r.raw, f) return local_filename class PDFExtractor(): def __init__(self, url): self.url = url # Downloading File in local def break_pdf(self, filename, start_page=-1, end_page=-1): pdf_reader = PdfFileReader(open(filename, "rb")) # Reading each pdf one by one total_pages = pdf_reader.numPages if start_page == -1: start_page = 0 elif start_page < 1 or start_page > total_pages: return "Start Page Selection Is Wrong" else: start_page = start_page - 1 if end_page == -1: end_page = total_pages elif end_page < 1 or end_page > total_pages - 1: return "End Page Selection Is Wrong" else: end_page = end_page for i in range(start_page, end_page): output = PdfFileWriter() output.addPage(pdf_reader.getPage(i)) with open(str(i + 1) + "_" + filename, "wb") as outputStream: output.write(outputStream) def extract_text_algo_1(self, file): pdf_reader = PdfFileReader(open(file, 'rb')) # creating a page object pageObj = pdf_reader.getPage(0) # extracting extract_text from page text = pageObj.extractText() text = text.replace("\n", "").replace("\t", "") return text def extract_text_algo_2(self, file): pdfResourceManager = PDFResourceManager() retstr = StringIO() la_params = LAParams() device = TextConverter(pdfResourceManager, retstr, codec='utf-8', laparams=la_params) fp = open(file, 'rb') interpreter = PDFPageInterpreter(pdfResourceManager, device) password = "" max_pages = 0 caching = True page_num = set() for page in PDFPage.get_pages(fp, page_num, maxpages=max_pages, password=password, caching=caching, check_extractable=True): interpreter.process_page(page) text = retstr.getvalue() text = text.replace("\t", "").replace("\n", "") fp.close() device.close() retstr.close() return text def extract_text(self, file): text1 = self.extract_text_algo_1(file) text2 = self.extract_text_algo_2(file) if len(text2) > len(str(text1)): return text2 else: return text1 def extarct_table(self, file): # Read pdf into DataFrame try: df = tabula.read_pdf(file, output_format="csv") except: print("Error Reading Table") return print("\nPrinting Table Content: \n", df) print("\nDone Printing Table Content\n") def tiff_header_for_CCITT(self, width, height, img_size, CCITT_group=4): tiff_header_struct = '<' + '2s' + 'h' + 'l' + 'h' + 'hhll' * 8 + 'h' return struct.pack(tiff_header_struct, b'II', # Byte order indication: Little indian 42, # Version number (always 42) 8, # Offset to first IFD 8, # Number of tags in IFD 256, 4, 1, width, # ImageWidth, LONG, 1, width 257, 4, 1, height, # ImageLength, LONG, 1, lenght 258, 3, 1, 1, # BitsPerSample, SHORT, 1, 1 259, 3, 1, CCITT_group, # Compression, SHORT, 1, 4 = CCITT Group 4 fax encoding 262, 3, 1, 0, # Threshholding, SHORT, 1, 0 = WhiteIsZero 273, 4, 1, struct.calcsize(tiff_header_struct), # StripOffsets, LONG, 1, len of header 278, 4, 1, height, # RowsPerStrip, LONG, 1, lenght 279, 4, 1, img_size, # StripByteCounts, LONG, 1, size of extract_image 0 # last IFD ) def extract_image(self, filename): number = 1 pdf_reader = PdfFileReader(open(filename, 'rb')) for i in range(0, pdf_reader.numPages): page = pdf_reader.getPage(i) try: xObject = page['/Resources']['/XObject'].getObject() except: print("No XObject Found") return for obj in xObject: try: if xObject[obj]['/Subtype'] == '/Image': size = (xObject[obj]['/Width'], xObject[obj]['/Height']) data = xObject[obj]._data if xObject[obj]['/ColorSpace'] == '/DeviceRGB': mode = "RGB" else: mode = "P" image_name = filename.split(".")[0] + str(number) print(xObject[obj]['/Filter']) if xObject[obj]['/Filter'] == '/FlateDecode': data = xObject[obj].getData() img = Image.frombytes(mode, size, data) img.save(image_name + "_Flate.png") # save_to_s3(imagename + "_Flate.png") print("Image_Saved") number += 1 elif xObject[obj]['/Filter'] == '/DCTDecode': img = open(image_name + "_DCT.jpg", "wb") img.write(data) # save_to_s3(imagename + "_DCT.jpg") img.close() number += 1 elif xObject[obj]['/Filter'] == '/JPXDecode': img = open(image_name + "_JPX.jp2", "wb") img.write(data) # save_to_s3(imagename + "_JPX.jp2") img.close() number += 1 elif xObject[obj]['/Filter'] == '/CCITTFaxDecode': if xObject[obj]['/DecodeParms']['/K'] == -1: CCITT_group = 4 else: CCITT_group = 3 width = xObject[obj]['/Width'] height = xObject[obj]['/Height'] data = xObject[obj]._data # sorry, getData() does not work for CCITTFaxDecode img_size = len(data) tiff_header = self.tiff_header_for_CCITT(width, height, img_size, CCITT_group) img_name = image_name + '_CCITT.tiff' with open(img_name, 'wb') as img_file: img_file.write(tiff_header + data) # save_to_s3(img_name) number += 1 except: continue return number def read_pages(self, start_page=-1, end_page=-1): # Downloading file locally downloaded_file = download_file(self.url) print(downloaded_file) # breaking PDF into number of pages in diff pdf files self.break_pdf(downloaded_file, start_page, end_page) # creating a pdf reader object pdf_reader = PdfFileReader(open(downloaded_file, 'rb')) # Reading each pdf one by one total_pages = pdf_reader.numPages if start_page == -1: start_page = 0 elif start_page < 1 or start_page > total_pages: return "Start Page Selection Is Wrong" else: start_page = start_page - 1 if end_page == -1: end_page = total_pages elif end_page < 1 or end_page > total_pages - 1: return "End Page Selection Is Wrong" else: end_page = end_page for i in range(start_page, end_page): # creating a page based filename file = str(i + 1) + "_" + downloaded_file print("\nStarting to Read Page: ", i + 1, "\n -----------===-------------") file_text = self.extract_text(file) print(file_text) self.extract_image(file) self.extarct_table(file) os.remove(file) print("Stopped Reading Page: ", i + 1, "\n -----------===-------------") os.remove(downloaded_file) # I have tested on these 3 pdf files # url = "http://s3.amazonaws.com/NLP_Project/Original_Documents/Healthcare-January-2017.pdf" url = "http://s3.amazonaws.com/NLP_Project/Original_Documents/Sample_Test.pdf" # url = "http://s3.amazonaws.com/NLP_Project/Original_Documents/Sazerac_FS_2017_06_30%20Annual.pdf" # creating the instance of class pdf_extractor = PDFExtractor(url) # Getting desired data out pdf_extractor.read_pages(15, 23) 

Puede descargar tika-app-xxx.jar (más reciente) desde Aquí .

Luego coloque este archivo .jar en la misma carpeta de su archivo de script de Python.

Luego inserte el siguiente código en el script:

 import os import os.path tika_dir=os.path.join(os.path.dirname(__file__),'.jar') def extract_pdf(source_pdf:str,target_txt:str): os.system('java -jar '+tika_dir+' -t {} > {}'.format(source_pdf,target_txt)) 

La ventaja de este método:

menos dependencia Un solo archivo .jar es más fácil de administrar que un paquete de Python.

Soporte multiformato. La posición source_pdf puede ser el directorio de cualquier tipo de documento. (.doc, .html, .odt, etc.)

A hoy. tika-app.jar siempre se libera antes que la versión relevante del paquete tika python.

estable. Es mucho más estable y bien mantenido (Powered by Apache) que PyPDF.

desventaja:

Un jre sin cabeza es necesario.

Aquí está el código más simple para extraer texto.

código:

 # importing required modules import PyPDF2 # creating a pdf file object pdfFileObj = open('filename.pdf', 'rb') # creating a pdf reader object pdfReader = PyPDF2.PdfFileReader(pdfFileObj) # printing number of pages in pdf file print(pdfReader.numPages) # creating a page object pageObj = pdfReader.getPage(5) # extracting text from page print(pageObj.extractText()) # closing the pdf file object pdfFileObj.close() 

He probado muchos convertidores de PDF de Python, Tika es el mejor.

 from tika import parser raw = parser.from_file("///Users/Documents/Textos/Texto1.pdf") raw = str(raw) safe_text = raw.encode('utf-8', errors='ignore') safe_text = str(safe_text).replace("\n", "").replace("\\", "") print('--- safe text ---' ) print( safe_text ) 

PyPDF2 funciona, pero los resultados pueden variar. Estoy viendo resultados bastante inconsistentes de su extracción de resultados.

 reader=PyPDF2.pdf.PdfFileReader(self._path) eachPageText=[] for i in range(0,reader.getNumPages()): pageText=reader.getPage(i).extractText() print(pageText) eachPageText.append(pageText) 

pdftotext es el mejor y el más simple! pdftotext también se reserva la estructura también.

Probé con PyPDF2, PDFMiner y algunos otros, pero ninguno dio un resultado satisfactorio.