Extraindo texto de um arquivo PDF usando PDFMiner em python?

87

Estou procurando documentação ou exemplos sobre como extrair texto de um arquivo PDF usando PDFMiner com Python.

Parece que o PDFMiner atualizou sua API e todos os exemplos relevantes que encontrei contêm código desatualizado (classes e métodos foram alterados). As bibliotecas que descobri que facilitam a tarefa de extrair texto de um arquivo PDF estão usando a sintaxe antiga do PDFMiner, então não tenho certeza de como fazer isso.

Do jeito que está, estou apenas olhando o código-fonte para ver se consigo descobrir.

DuckPuncher
fonte
1
Verifique stackoverflow.com/help/how-to-ask e stackoverflow.com/help/mcve e atualize sua resposta para que fique em um formato melhor e se alinhe às diretrizes.
Parker
Qual distribuição de Python você está usando, 2.7.x ou 3.xx? Deve-se notar que o autor detalhou explicitamente que PDFminernão funciona com Python 3.xx Esse pode ser o motivo de você estar recebendo importerros. Você deve usar pdfminer3knesse caso, pois é a importação Python 3 permanente da referida biblioteca.
NullDev
@Nanashi, desculpe, esqueci de adicionar minha versão Python. É 2.7, então esse não é o problema. Estive olhando o código-fonte e parece que eles reestruturaram algumas coisas, é por isso que as importações estão quebrando. Não consigo encontrar nenhuma documentação para o PDFMiner ou estaria trabalhando nisso :(
DuckPuncher
Acabei de instalar literalmente PDFminerdo GitHub e importa muito bem. Você pode gentilmente postar seu código e também o rastreamento completo do erro?
NullDev
@Nanashi, Como eu disse na minha pergunta original, as bibliotecas que dependem do PDFMiner quebram antes de terminar as importações junto com qualquer exemplo que eu possa encontrar. Este não é um problema do PDFMiner. Estou procurando por documentação ou um exemplo de como usar o PDFMiner. Tudo que posso encontrar está usando uma sintaxe antiga para PDFMiner. Fui em frente e editei minha pergunta para maior clareza. Acho que deixei tudo mais confuso do que precisava ser. Me desculpe por isso.
DuckPuncher de

Respostas:

181

Aqui está um exemplo prático de extração de texto de um arquivo PDF usando a versão atual do PDFMiner (setembro de 2016)

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from io import StringIO

def convert_pdf_to_txt(path):
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    fp = open(path, 'rb')
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    password = ""
    maxpages = 0
    caching = True
    pagenos=set()

    for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages, password=password,caching=caching, check_extractable=True):
        interpreter.process_page(page)

    text = retstr.getvalue()

    fp.close()
    device.close()
    retstr.close()
    return text

A estrutura do PDFMiner mudou recentemente, então isso deve funcionar para extrair texto dos arquivos PDF.

Edit : Ainda trabalhando em 7 de junho de 2018. Verificado na versão Python 3.x

Edit: A solução funciona com Python 3.7 em 3 de outubro de 2019. Usei a biblioteca Python pdfminer.six, lançada em novembro de 2018.

DuckPuncher
fonte
2
funciona bem, mas, como posso lidar com espaços em nomes de exemplo? suponha que eu tenha um pdf que contém 4 colunas onde tenho primeiro e último nome em uma coluna, agora ele é analisado com o nome em uma linha e o sobrenome em uma linha, aqui está um exemplo docdro.id/rRyef3x
Deusdeorum
2
Atualmente recebendo um erro de importação com este código: ImportError: Nenhum módulo chamado 'pdfminer.pdfpage'
Jeffrey Swan
1
Obrigado, ele funciona no python v2.7.12 e no ubuntu 16.04, embora seja melhor carregar o documento pdf com a codificação utf-8, porque meu pdf de amostra tem alguns problemas de codificação, então tente isso depois de codificar com utf-8 e resolver o edição ... import sys reload(sys) sys.setdefaultencoding('utf-8')
sib10
2
@DuckPuncher, ainda está funcionando agora? Tive que mudar o file(path, 'rb')para `open (path, 'rb') para fazer o meu funcionar.
esticado em
2
Ainda trabalhando para usuários Python3.7. Pdfminer.six instalado == pacote 20181108. A melhor solução até agora para o meu caso e comparei várias soluções.
aze45sq6d
29

excelente resposta do DuckPuncher, para Python3 certifique-se de instalar o pdfminer2 e fazer:

import io

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage


def convert_pdf_to_txt(path):
    rsrcmgr = PDFResourceManager()
    retstr = io.StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    fp = open(path, 'rb')
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    password = ""
    maxpages = 0
    caching = True
    pagenos = set()

    for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages,
                                  password=password,
                                  caching=caching,
                                  check_extractable=True):
        interpreter.process_page(page)



    fp.close()
    device.close()
    text = retstr.getvalue()
    retstr.close()
    return text
Juan Isaza
fonte
1
Não funciona para mim: ModuleNotFoundError: Nenhum módulo chamado 'pdfminer.pdfpage' estou usando o python 3.6
Ato
@Atti, por precaução, certifique-se de ter o pdfminer2 instalado, pois existe outro pacote pdfminer (eu odeio isso). Ele funciona para a versão pdfminer2 == 20151206 ao fazer o congelamento do pip3.
Juan Isaza
5
obrigado, finalmente consegui fazer funcionar, instalei o pdfminer.six do conda forge
Atti
8
Para Python 3, pdfminer.six é o pacote recomendado - github.com/pdfminer/pdfminer.six
Mike Driscoll
Isso ainda é atual? Estou recebendo a mesma ImportError:mensagem
12

Isso funcionará em maio de 2020 usando PDFminer six em Python3.

Instalando o pacote

$ pip install pdfminer.six

Importando o pacote

from pdfminer.high_level import extract_text

Usando um PDF salvo no disco

text = extract_text('report.pdf')

Ou alternativamente:

with open('report.pdf','rb') as f:
    text = extract_text(open('report.pdf','rb'))

Usando PDF já na memória

Se o PDF já estiver na memória, por exemplo, se recuperado da web com a biblioteca de solicitações, ele pode ser convertido em um fluxo usando a iobiblioteca:

import io

response = requests.get(url)
text = extract_text(io.BytesIO(response.content))

Desempenho e confiabilidade em comparação com PyPDF2

PDFminer.six funciona de forma mais confiável do que PyPDF2 (que falha com certos tipos de PDFs), em particular o PDF versão 1.7

No entanto, a extração de texto com PDFminer.six é significativamente mais lenta do que PyPDF2 por um fator de 6.

timeitCronometrei a extração de texto com um MBP de 15 "(2018), cronometrando apenas a função de extração (sem abertura de arquivo etc.) com um PDF de 10 páginas e obtive os seguintes resultados:

PDFminer.six: 2.88 sec
PyPDF2:       0.45 sec

O pdfminer.six também ocupa uma grande área de cobertura, exigindo pycryptodome, que precisa do GCC e de outras coisas instaladas, enviando uma imagem docker de instalação mínima no Alpine Linux de 80 MB a 350 MB. PyPDF2 não tem impacto de armazenamento perceptível.

Cornelius Roemer
fonte
9

Divulgação completa, sou um dos mantenedores do pdfminer.six.

Hoje em dia, existem várias APIs para extrair texto de um PDF, dependendo de suas necessidades. Nos bastidores, todas essas APIs usam a mesma lógica para analisar e analisar o layout.

Linha de comando

Se você deseja extrair o texto apenas uma vez, pode usar a ferramenta de linha de comando pdf2txt.py:

$ pdf2txt.py example.pdf

API de alto nível

Se você deseja extrair texto com Python, pode usar a API de alto nível. Essa abordagem é a solução ideal se você deseja extrair texto programaticamente de muitos PDFs.

from pdfminer.high_level import extract_text

text = extract_text('samples/simple1.pdf')

API composable

Também há uma API composable que oferece muita flexibilidade no manuseio dos objetos resultantes. Por exemplo, você pode implementar seu próprio algoritmo de layout usando isso. Esse método é sugerido nas outras respostas, mas eu só recomendaria quando você precisar personalizar a maneira como o pdfminer.six se comporta.

from io import StringIO

from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfparser import PDFParser

output_string = StringIO()
with open('samples/simple1.pdf', 'rb') as in_file:
    parser = PDFParser(in_file)
    doc = PDFDocument(parser)
    rsrcmgr = PDFResourceManager()
    device = TextConverter(rsrcmgr, output_string, laparams=LAParams())
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    for page in PDFPage.create_pages(doc):
        interpreter.process_page(page)

print(output_string.getvalue())
Pieter
fonte
0

este código é testado com pdfminer para python 3 (pdfminer-20191125)

from pdfminer.layout import LAParams
from pdfminer.converter import PDFPageAggregator
from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.pdfinterp import PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.layout import LTTextBoxHorizontal

def parsedocument(document):
    # convert all horizontal text into a lines list (one entry per line)
    # document is a file stream
    lines = []
    rsrcmgr = PDFResourceManager()
    laparams = LAParams()
    device = PDFPageAggregator(rsrcmgr, laparams=laparams)
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    for page in PDFPage.get_pages(document):
            interpreter.process_page(page)
            layout = device.get_result()
            for element in layout:
                if isinstance(element, LTTextBoxHorizontal):
                    lines.extend(element.get_text().splitlines())
    return lines
Brault Gilbert
fonte
Tenho arquivos PDF que posso converter usando a ferramenta Nitro Pro. Quando tento converter o mesmo PDF usando o código postado aqui, porém, obtenho uma saída que sugere que há um erro de permissão. Aqui está a saída: ('das coleções de ciências sociais SAGE. Todos os direitos reservados. \ N \ n \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c \ x0c ')
b00kgrrl
O que você quer dizer com fluxo de arquivo?
Vincent,
@Vincent com open (file, 'rb') como stream: [...]
Rodrigo Formighieri
você consegue obter este arquivo como uma mesa / pandas idealmente? groupe-psa.com/en/publication/monthly-world-sales-march-2020
Nono Londres