arquivos especiais - pdf, csv


Arquivos especiais - pdf e csv

PDF

A sigla PDF vem de Portable Document Format e significa um formato de arquivo usado para apresentar documentos com a mesma aparência, independente do computador, hardware ou sistema operacional.
Un arquivo PDF inclue textos, gráficos, imagens, tabelas, fontes de caracteres e tudo mais necessário para garantir a representação do documento.
Originalmente um formato proprietário da empresa Adobe, desde 2008 virou um standard ISO e Adobe cedeu seu uso sem royalties para desenvolvedores de software em geral.
O formato PDF é bastante complexo e sua manipulação não é tarefa trivial. Uma descrição do formato na Wikipedia pode ser vista aqui.
Talvez porisso não existe nenhum pacote de software aberto realmente bom para manipular arquivos PDF, afora o próprio software da Adobe, é claro, mas este não é livre nem gratuito. Um pacote aberto recomendado é o Poppler) mas este é escrito na linguagem C++, mais usada por desenvolvedores profissionais.

De modo geral para lêr arquivos PDF e extrair deles o seu texto o melhor é usar o Adobe Reader para abrir o documento PDF e usar a menu File\Save as other...\text para obter o texto perfeitamente extraido.
Entretanto este procedimento é manual e em certas ocasiões pode ser inviável. Por exemplo, podemos estar procurando determinadas palavras no texto em dezenas de milhares de arquivos. Abri-los manualmente um por um é inviável. Em tais casos podemos usar módulos para manipulação de arquivos PDF feitos para o Python. Existem diversos módulos em Python para extrair texto, cada qual com seus trade-offs.
Nesta lição vamos seguir nosso livro referência e instalar o módulo PyPDF2 em nosso sistema. Se você ainda não tem este módulo em seu computador, abra a janela de comando ou o Windows PowerShell e digite
>>pip install PyPDF2
O instalador pip vai buscar o software (usualmente no GitHub) baixar o pacote e proceder sua instalação em Anaconda 3\Lib\site-packages\PyPDF2.

Extraindo texto de um arquivo PDF

Neste exemplo vamos extrair o texto de um artigo científico sobre Redes Neurais, que pode ser baixado aqui.
O arquivo pdf escolhido 'NN.pdf' é um arquivo pdf "fácil" para extração de texto, contendo essencialmente texto, fórmulas matemáticas e algumas poucas figuras. Arquivos podem ser mais "dificeis" quando contém muitas tabelas formatadas, imagens, etc.
É bom avisar aqui que extrair texto de arquivos PDF nem sempre dá muito certo (a menos que use um pacote como o Adobe...). Surpresas desagradáveis podem acontecer.
Vamos ao exemplo. Após baixar o arquivo NN.pdf coloque-o no mesmo diretório onde está este notebook Jupyter.
Abrindo o arquivo com o Adobe Reader deve aparecer um artigo assim:
alt text
Vamos tentar extrair este texto com um script Python usando o módulo PyPDF2. A documentação deste módulo pode ser encontrada aqui.
In [1]:
# vamos importar o módulo essencial
import PyPDF2
O módulo PyPDF2 possue diversos objetos úteis, alguns descritos em nosso livro referência. Para extrair texto usaremos a classe PyPDF2.PdfFileReader. Esta classe recebe como argumento um objeto File (nosso já conhecido) obtido abrindo o arquivo pdf para leitura em modo binário e retorna um objeto que representa o documento encontrado dentro do arquivo pdf, se tudo foi bem.
In [4]:
# cria um objeto da classe PdfFileReader que representa o documento no arquivo pdf
f = open('NN.pdf', 'rb')
pdf = PyPDF2.PdfFileReader(f)
PdfReadWarning: Xref table not zero-indexed. ID numbers for objects will be corrected. [pdf.py:1736]
O aviso ameaçador só indica que na criação do objeto pdf alguma anomalia do documento foi encontrada. Isto não afeta nossa extração do texto.
In [5]:
# vejamos quantas páginas foram encontradas
pdf.getNumPages()
Out[5]:
15
Foram lidas 15 páginas compondo o documento. Podemos conferir este número abrindo o arquivo NN.pdf no Adobe e contando as páginas. São 15 mesmo.
Para extrair o texto de cada página usaremos o método getPage(i).extractText() onde i é o indice da página desejada.
Vejamos a página índice 0.
In [8]:
# extrai o texto da página indice 0
spg0 = pdf.getPage(0).extractText()
type(spg0)
Out[8]:
str
O texto da página 0 retornou como um string, na variável spg0. Vejamos um pedaço dele.
In [10]:
# tamanho do texto na página 0
len(spg0)
Out[10]:
5346
In [12]:
spg0[:1000]
Out[12]:
'240 IEEE TRANSACTIONS ON NEURAL NETWORKS, VOL. 5, NO. 2, MARCH 1994 Recurrent Neural Networks and \nRobust Time \nSeries Prediction \nJerome T. Connor, R. Douglas Martin, Member, IEEE, and L. E. Atlas, Member IEEE Abstract-We propose a robust learning algorithm and apply it to recurrent neural \nnetworks. This algorithm \nis based on filtering \noutliers from the data and \nthen estimating \nparameters from the filtered data. The \nfiltering removes outliers from both \nthe target \nfunction and the inputs of the neural network. The filtering \nis soff in that some outliers are neither completely rejected nor accepted. To show the need for robust recurrent \nnetworks, we compare the predictive ability \nof least squares estimated recurrent net- works on synthetic data and on the Puget Power Electric Demand \ntime series. \nThese investigations result \nin a \nclass of recurrent neural networks, \nNARMA(p, q), which show \nadvantages over \nfeedforward neural networks for time series with \na moving average '
In [13]:
# vamos colocar o texto numa lista de linhas separadas por \n
pg0 = spg0.split('\n')
print(pg0[:15])
['240 IEEE TRANSACTIONS ON NEURAL NETWORKS, VOL. 5, NO. 2, MARCH 1994 Recurrent Neural Networks and ', 'Robust Time ', 'Series Prediction ', 'Jerome T. Connor, R. Douglas Martin, Member, IEEE, and L. E. Atlas, Member IEEE Abstract-We propose a robust learning algorithm and apply it to recurrent neural ', 'networks. This algorithm ', 'is based on filtering ', 'outliers from the data and ', 'then estimating ', 'parameters from the filtered data. The ', 'filtering removes outliers from both ', 'the target ', 'function and the inputs of the neural network. The filtering ', 'is soff in that some outliers are neither completely rejected nor accepted. To show the need for robust recurrent ', 'networks, we compare the predictive ability ', 'of least squares estimated recurrent net- works on synthetic data and on the Puget Power Electric Demand ']
In [14]:
# após extrair texto não esqueça de fechar o arquivo
f.close()
Sucesso! O texto extraido pode ser comparado com o artigo original e está razoavelmente compatível. Algumas coisas que não sairam bem foram as fórmulas matemáticas, que estão numa linguagem chamada PostScript dentro do documento e claro, figuras não são texto logo não foram extraidas.
Isto encerra este primeiro contato com extração de texto de arquivos PDF. O procedimento acima pode ser encapsulado num módulo Python cujo código está aqui:
import PyPDF2
"""Lê documento PDF e retorna texto extraido"""

# esta função recebe o caminho do arquivo pdf e retorna seu texto extraido num string
def getPDFContent(path):
    content = ""

    # abre o arquivo pdf e cria um objeto reader
    f = open(path, "rb")
    pdf = PyPDF2.PdfFileReader(f)       # objeto que representa o documento

    # Itera pelas páginas do documento
    for i in range(0, pdf.getNumPages()):        
        # Extrai texto da pagina e apenda no string content
        content += pdf.getPage(i).extractText() + "\n"

    # fecha o arquivo
    f.close()
    return content


# programa principal, executado ao ativar o módulo
print('Extrai texto de arquivo PDF.')
print(os.getcwd())
print(os.listdir())
print('----------------------------')
print('Entre nome ou caminho de arquivo PDF:')

arq = input()
if not arq.endswith('.pdf'):
    arq = arq + '.pdf'

stx = getPDFContent(arq)

txt = stx.split('\n')

print('Texto extraido (primeiros 100 caracteres):')
print('-------------------------------------------')
print()
print(txt[:101])
print()
print('-------------------------------------------')

CSV

Arquivos CSV são arquivos texto cujo conteúdo representa uma planilha Excel. O conteúdo do arquivo CSV são linhas de valores separados por vírgulas. Por exemplo:
123, 4, 56, 7, 89 0.1, 3.5, 1, 420, 0
O conteúdo do arquivo CSV é interpretado da seguinte maneira:
  • cada linha do arquivo CSV representa uma linha da planilha Excel
  • cada elemento entre virgulas representa o valor de uma célula da planilha, naquela linha e na coluna respectiva.
Por ser um mero arquivo texto formatado na convenção acima a tendência é manipulas arquivos CSV como textos mesmo, usando técnicas ordinárias de manipulação de strings. De fato na prática isto é possível mas existem razões para usar um módulo apropriado para manipular CSV.
Por exemplo, as vêzes um valor dentro de uma célula pode conter uma vírgula. Como saber ser esta é uma separação de células ou somente um caracter dentro de um valor?
O módulo csv do Python esconde todas estas nuances traiçoeiras atrás de uma interface simples e robusta, que deve sempre ser usada. Manipule diretamente um arquivo CSV somente se você sabe bem o que está fazendo.
A documentação oficial deste módulo pode ser vista aqui.

Classes do módulo CSV

Essencialmente são quatro classes importantes, das quais obtemos objetos correspondentes para manipulação de CSV:
  • csv.reader(csvfile)
  • csv.writer(csvfile)
  • csv.DictReader(csvfile)
  • csv.DictWriter(csvfile, fieldnames)
Outras classes tem aplicação especial e podem ser vistas na documentação.
Para esta lição vamos usar um arquivo CSV que pode ser baixado aqui.
Baixe-o agora antes de continuar. Após baixar o arquivo coloque-o no mesmo diretório deste notebook.

Usando os objetos csv.reader e csv.writer

Vejamos exemplo de leitura de arquivo CSV usando um objeto csv.reader. Este objeto é iterável, tal como por exemplo uma lista. A cada iteração usando o objeto obtemos uma nova linha do arquivo CSV que está sendo lido.
A sequência de passos é:
  1. abrir o arquivo CSV com open() no modo texto para leitura
  2. criar um objeto csv.reader a partir do objeto File obtido no passo 1
  3. iterar sobre as linhas contidas no objeto csv.reader
In [20]:
# vamos importar o módulo csv
import csv

# Criamos um objeto reader para lêr cada linha do arquivo
arq = 'dep1.csv'

# abrimos o arquivo como texto e iteramos por suas linhas, imprimindo cada uma
linhas = []                 
with open(arq) as csvfile:
    # cria objeto reader
    rdr = csv.reader(csvfile)
    # agora iteramos sobre as linhas do arquivo
    for linha in rdr:
        print(', '.join(linha))
        linhas.append([x for x in linha if not x == ''])
    
Nome Parlamentar, Partido, UF, Titular/Suplente/Efetivado, , , , , , , , , , , , , , 
ABEL MESQUITA JR., PMB, RR, T, , , , , , , , , , , , , , 
ADALBERTO CAVALCANTI, PMB, PE, T, , , , , , , , , , , , , , 
ADELMO CARNEIRO LEÃO, PT, MG, S, , , , , , , , , , , , , , 
ADELSON BARRETO, PTB, SE, T, , , , , , , , , , , , , , 
ADEMIR CAMILO, PROS, MG, S, , , , , , , , , , , , , , 
ADILTON SACHETTI, PSB, MT, T, , , , , , , , , , , , , , 
AELTON FREITAS, PR, MG, T, , , , , , , , , , , , , , 
AFONSO FLORENCE, PT, BA, T, , , , , , , , , , , , , , 
AFONSO HAMM, PP, RS, T, , , , , , , , , , , , , , 
Como vemos a primeira linha é de nomes de colunas, e as demais contém os dados. Note que só as primeiras 4 colunas tem informação, as demais são vazias mas fazem parte do documento.
Vejamos agora um exemplo de criação de um arquivo CSV.
Para escrever num arquivo CSV usamos um objeto writer, que é gerado da classe csv.writer().
In [21]:
# lista de linhas no arquivo com colunas vazias omitidas
linhas
Out[21]:
[['Nome Parlamentar', 'Partido', 'UF', 'Titular/Suplente/Efetivado'],
 ['ABEL MESQUITA JR.', 'PMB', 'RR', 'T'],
 ['ADALBERTO CAVALCANTI', 'PMB', 'PE', 'T'],
 ['ADELMO CARNEIRO LEÃO', 'PT', 'MG', 'S'],
 ['ADELSON BARRETO', 'PTB', 'SE', 'T'],
 ['ADEMIR CAMILO', 'PROS', 'MG', 'S'],
 ['ADILTON SACHETTI', 'PSB', 'MT', 'T'],
 ['AELTON FREITAS', 'PR', 'MG', 'T'],
 ['AFONSO FLORENCE', 'PT', 'BA', 'T'],
 ['AFONSO HAMM', 'PP', 'RS', 'T']]
In [23]:
arq1 = 'exemplo.csv'
with open(arq1, 'w') as csvfile:
    wrt = csv.writer(csvfile)      # cria o objeto writer
    # itera sobre as linhas da lista escrevendo cada uma
    for l in linhas:
        wrt.writerow(l)
Deve aparecer um arquivo exemplo.csv no diretório corrente. Abra-o com o Notepad e verifique que os dados foram escritos corretamente. Inclusive a linha de cabeçalhos (nomes de colunas).

Usando os objetos csv.DictReader e csv.DictWriter

Estes objetos são úteis quando nossos dados estão organizados em formato tipo dicionário, onde a chave de cada item é o nome de uma coluna e o valor do item é o valor da célula na coluna, para uma dada linha da planilha.
Por exemplo a última linha no exemplo anterior seria lida da seguinte forma:
{'Nome Parlamentar':'AFONSO HAMM', 'Partido':'PP', 'UF':'RS', 'Titular/Suplente/Efetivado':'T'}
Ou seja, cada linha é lida como um objeto dicionário dict.
In [24]:
# vamos ler o arquivo exemplo.csv
with open('exemplo.csv') as csvfile:
    # cria objeto DictReader
    rd = csv.DictReader(csvfile)
    #itera sobre as linhas do objeto
    for row in rd:
        # imprimimos somente o nome e o partido
        print(row['Nome Parlamentar'], row['Partido'])
ABEL MESQUITA JR. PMB
ADALBERTO CAVALCANTI PMB
ADELMO CARNEIRO LEÃO PT
ADELSON BARRETO PTB
ADEMIR CAMILO PROS
ADILTON SACHETTI PSB
AELTON FREITAS PR
AFONSO FLORENCE PT
AFONSO HAMM PP
Para escrever usando csv.DictWriter o processo é parecido:
In [26]:
# criar arquivo csv com partido e UF somente
with open('partidos.csv', 'w') as f:
    # cria o objeto DictWriter: é obrigatório dar nomes das colunas
    nomes = ['Partido', 'UF']
    wrt = csv.DictWriter(f, fieldnames=nomes)
    
    # escreve os cabeçalhos
    wrt.writeheader()
    
    # itera sobre a lista escrevendo linhas
    for linha in linhas[1:]:
        wrt.writerow({'Partido':linha[1], 'UF':linha[2]})
Deve ter aparecido um arquivo partidos.csv no diretório corrente. Podemos ver seu conteúdo de forma rápida assim:
In [29]:
with open('partidos.csv') as f:
    l = f.read().split('\n')
l = [x for x in l if x != '']
print(l)
['Partido,UF', 'PMB,RR', 'PMB,PE', 'PT,MG', 'PTB,SE', 'PROS,MG', 'PSB,MT', 'PR,MG', 'PT,BA', 'PP,RS']
Isto encerra esta lição.

No comments:

Post a Comment