web-dev-qa-db-fra.com

Comment extraire le texte d'un fichier PDF?

J'essaie d'extraire le texte inclus dans le fichier this PDF à l'aide de Python.

J'utilise le module PyPDF2 et j'ai le script suivant:

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

Lorsque j'exécute le code, le résultat suivant est différent de celui inclus dans le document PDF:

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

Comment puis-je extraire le texte tel quel dans le document PDF?

119
Simplicity

Je cherchais une solution simple à utiliser pour python 3.x et windows. Il ne semble pas y avoir de support de textract , ce qui est regrettable, mais si vous recherchez une solution simple pour Windows/Python 3, utilisez le paquet tika , vraiment tout droit. transmettre pour la lecture de fichiers PDF

from tika import parser

raw = parser.from_file('sample.pdf')
print(raw['content'])
99
DJK

Utilisez textract.

Il supporte de nombreux types de fichiers, y compris les PDF

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

Regardez ce code:

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 sortie est:

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

Utiliser le même code pour lire un pdf à partir de 201308FCR.pdf . La sortie est normale.

Sa documentation explique pourquoi:

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.
    """
40
Quinn

Après avoir essayé textract (qui semblait avoir trop de dépendances) et pypdf2 (qui ne pouvait pas extraire le texte des fichiers PDF avec lesquels j'ai testé) et tika (qui était trop lent), j'ai fini par utiliser pdftotext à partir de xpdf (comme déjà suggéré dans une autre réponse) et juste appelé le binaire de python directement (vous devrez peut-être adapter le chemin d'accès à 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')

Il y a pdftotext qui fait fondamentalement la même chose, mais cela suppose pdftotext dans/usr/local/bin alors que je l'utilise dans AWS lambda et que je voulais l'utiliser à partir du répertoire en cours.

Btw: Pour utiliser ceci sur lambda, vous devez mettre le binaire et la dépendance sur libstdc++.so dans votre fonction lambda. J'ai personnellement besoin de compiler xpdf. Comme des instructions à ce sujet exploseraient cette réponse, je les ai mises sur mon blog personnel .

20
hansaplast

Vous voudrez peut-être utiliser le temps prouvé xPDF et des outils dérivés pour extraire le texte, contrairement à ce que semble avoir fait pyPDF2 divers problèmes avec l'extraction du texte.

La réponse longue est qu’il existe de nombreuses variations dans la manière dont un texte est codé à l'intérieur de PDF et qu'il peut nécessiter le décodage de la chaîne PDF elle-même, puis besoin d'analyser la distance entre les mots et les lettres, etc.

Si le PDF est endommagé (c’est-à-dire si vous affichez le texte correct mais que vous le copiez), vous devez vraiment extraire le texte. Vous pouvez alors envisager de convertir PDF en image ( en utilisant ImageMagik ) puis en utilisant Tesseract pour obtenir du texte à partir d'une image en utilisant l'OCR.

10
Eugene

Le code ci-dessous est une solution à la question de Python. Avant d'exécuter le code, assurez-vous d'avoir installé la bibliothèque PyPDF2 dans votre environnement. S'il n'est pas installé, ouvrez la commande Invite et exécutez la commande suivante:

pip3 install PyPDF2

Code de solution:

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

Le pdf de plusieurs pages peut être extrait sous forme de texte en un seul passage au lieu de donner un numéro de page individuel comme argument en utilisant le code ci-dessous

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')
4
Yogi

Vous pouvez utiliser PDFtoText https://github.com/jalan/pdftotext

PDF to text conserve l'indentation du format texte, peu importe si vous avez des tableaux.

3
Máxima Alekz

J'ai essayé plusieurs convertisseurs Python PDF, Tika est ce qu'il y a de mieux.

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 )
2
erfelipe

Voici le code le plus simple pour extraire du texte

code:

# 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()
2
Infinity

pdftotext est le meilleur et le plus simple! pdftotext réserve également la structure.

J'ai essayé PyPDF2, PDFMiner et quelques autres, mais aucun d'entre eux n'a donné de résultat satisfaisant.

2
Dharam

J'ai trouvé une solution ici PDFLayoutTextStripper

C'est bien parce que vous pouvez conserver la mise en page du PDF original .

C'est écrit en Java mais j'ai ajouté une passerelle pour supporter Python.

Exemple de code:

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

Exemple de sortie de PDFLayoutTextStripper : enter image description here

Vous pouvez voir plus de détails ici Stripper with Python

2
Tho

PyPDF2, dans certains cas, ignore les espaces et rend le texte du résultat un désordre, mais j'utilise PyMuPDF et je suis vraiment satisfait de pouvoir utiliser ceci lien pour plus d'informations.

2
ehsaneha

Vous pouvez télécharger tika-app-xxx.jar (dernier) à partir de Ici .

Placez ensuite ce fichier .jar dans le même dossier que votre fichier de script python.

puis insérez le code suivant dans le 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))

L'avantage de cette méthode:

moins de dépendance. Un seul fichier .jar est plus facile à gérer qu’un package python.

support multi-format. La position source_pdf peut être le répertoire de tout type de document. (.doc, .html, .odt, etc.)

à jour. tika-app.jar publie toujours une version antérieure à la version appropriée du paquet tika python.

stable. Il est beaucoup plus stable et bien entretenu (Powered by Apache) que PyPDF.

désavantage:

Un jre-sans tête est nécessaire.

0
pah8J

PyPDF2 fonctionne, mais les résultats peuvent varier. Je vois des résultats assez contradictoires de son extraction de résultats.

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

J'ajoute du code pour accomplir ceci: Cela fonctionne très bien pour moi:

# 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)
0
Ritesh Shanker

Si vous l'essayez sous Anaconda sous Windows, PyPDF2 pourrait ne pas gérer certains fichiers PDF avec une structure non standard ou des caractères unicode. Je vous recommande d’utiliser le code suivant si vous devez ouvrir et lire beaucoup de fichiers pdf - le texte de tous les fichiers pdf du dossier avec le chemin relatif .//pdfs// sera stocké dans la liste 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)
0
Gibron