python - scraping - ¿Cómo extraer texto de un archivo PDF?
python search text in pdf (17)
Aquí está el código más simple para extraer texto
código:
from py4j.java_gateway import JavaGateway
gw = JavaGateway()
result = gw.entry_point.strip(''samples/bus.pdf'')
# result is a dict of {
# ''success'': ''true'' or ''false'',
# ''payload'': pdf file content if ''success'' is ''true''
# ''error'': error message if ''success'' is ''false''
# }
print result[''payload'']
Estoy tratando de extraer el texto incluido en
this
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 el siguiente resultado que es diferente del incluido 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?
Después de probar textract (que parecía tener demasiadas dependencias) y pypdf2 (que no podía extraer texto de los archivos PDF que probé) y tika (que era demasiado lento) terminé usando
pdftotext
de
pdftotext
(como ya se sugirió en otra respuesta) y solo llamé al binario desde python directamente (puede que necesite adaptar la ruta a 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 supone pdftotext en / usr / local / bin, mientras que estoy usando esto en AWS lambda y quería usarlo desde el directorio actual.
Por cierto: para usar esto en lambda necesita poner el binario y la dependencia de
libstdc++.so
en su función lambda.
Yo personalmente necesitaba compilar xpdf.
Como las instrucciones para esto explotarían esta respuesta, las puse
en mi blog personal
.
El PDF de varias páginas se puede extraer como texto en una sola extensión en lugar de proporcionar un número de página individual como argumento usando el código siguiente
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'')
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 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())
Encontré una solución aquí PDFLayoutTextStripper
Es bueno porque puede mantener el diseño del PDF original .
Está escrito en Java, pero he agregado una puerta de enlace para admitir Python.
Código de muestra:
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 )
Salida de muestra de PDFLayoutTextStripper :
Puedes ver más detalles aquí Stripper con Python
Es posible que desee utilizar xPDF probado con el tiempo y herramientas derivadas para extraer texto, ya que pyPDF2 parece tener varios problemas con la extracción de texto todavía.
La respuesta larga es que hay muchas variaciones sobre cómo se codifica un texto dentro de PDF y que puede requerir decodificar la cadena de PDF, luego puede necesitar mapear con CMAP, luego puede necesitar 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 extraer texto, puede considerar convertir el PDF a imagen (usando ImageMagik ) y luego usar Tesseract para obtener texto de la imagen usando OCR.
Estaba buscando una solución simple para Python 3.xy Windows. Parece que no hay soporte de textract , lo cual es desafortunado, pero si está buscando una solución simple para Windows / Python 3, consulte el paquete tika , realmente sencillo para leer archivos PDF
from tika import parser
raw = parser.from_file(''sample.pdf'')
print(raw[''content''])
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)
He probado muchos convertidores PDF de Python, Tika es el mejor.
# 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()
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'')
El resultado es:
!"#$%#$%&%$&''()*%+,-%./01''*23%4
5''%1$#26%3/%7/))/8%&)/26%8#3"%3"*%313/9#&)
%
Usando el mismo código para leer un pdf de 201308FCR.pdf . La salida es normal.
Su documentation 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.
"""
Puede descargar tika-app-xxx.jar (más reciente) desde Here .
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__),''<tika-app-xxx>.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. El archivo .jar único 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 publica antes de la versión relevante del paquete tika python.
estable. Es mucho más estable y está bien mantenido (Desarrollado por Apache) que PyPDF.
desventaja:
Un jre-headless es necesario.
Puede usar PDFtoText pdftotext
PDF to text mantiene la sangría del 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 uso PyMuPDF y estoy realmente satisfecho de que pueda usar este link para obtener más información
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)
Si lo prueba en Anaconda en Windows, PyPDF2 podría no manejar algunos de los archivos PDF con estructura no estándar o caracteres unicode.
Recomiendo usar el siguiente código si necesita abrir y leer muchos archivos pdf: el texto de todos los archivos pdf en la carpeta con la ruta relativa
.//pdfs//
se almacenará en la lista
pdf_text_list
.
from tika import parser
import glob
def read_pdf(filename):
text = parser.from_file(filename)
return(text)
all_files = glob.glob(".//pdfs//*.pdf")
pdf_text_list=[]
for i,file in enumerate(all_files):
text=read_pdf(file)
pdf_text_list.append(text[''content''])
print(pdf_text_list)
Usa textract.
Admite muchos tipos de archivos, incluidos archivos PDF
import textract
text = textract.process("path/to/file.extension")
pdftotext es el mejor y más simple! pdftotext también se reserva la estructura también.
Probé PyPDF2, PDFMiner y algunos otros, pero ninguno de ellos dio un resultado satisfactorio.