Um pouco sobre

Muitas vezes utiliza-se um sistema chamado ETL (Extract Transform Load), para podermos extrair as informações de lugares da internet, transformar a informação para colocar em um formato que desejamos e por fim carregar a informação em algum local de armazenamento e consulta.

Em um ETL, muitas vezes é necessário realizar a extração de dados de arquivos PDF, o que pode ser em certo sentido desafiador a depender do arquivo utilizado e da complexidade de organização da formatação interna do arquivo.

Qual caminho para extrair? Como funciona?

Extrair informações de um PDF é como se estivéssemos realizando “web scraping” para PDF, portanto o nosso código será dependente da estrutura do arquivo, o que faz com que tornar a informação desestruturada de um arquivo em informação estruturada se torne um grande desafio.

A estrutura do arquivo, como você já deve ter percebido, não tem um padrão. Isso faz com que o nível de dificuldade de extração seja muito variável, do mesmo modo que ocorre em extrações de páginas web.

Preparando o ambiente

Utilizaremos a biblioteca Fitz no Python para realizar a extração, primeiro vamos criar nosso ambiente virtual, verifique sua versão do Python:

pip install virtualenv
virtualenv --python=3.10 venv

Depois de criar o ambiente virtual, vamos ativá-lo:

source venv/bin/activate

Agora podemos instalar a biblioteca sem contaminar nosso sistema operacional:

pip install PyMuPDF

Baixe o arquivo PDF que utilizaremos nesta demonstração, será o arquivo da chamada do vestibular deste ano da UNESP

Exibindo todo o conteúdo do PDF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import fitz
import sys
import json

def abrir(arquivo):
    arquivo = open(arquivo, 'rb').read()
    arquivo = fitz.Document(stream=arquivo, filetype="pdf")

def extrair(arquivo):
    abrir(arquivo)

    informacao = {}
    curso = ""

    for page in arquivo:
        for lines in page.get_text('dict')['blocks']:
            for line in lines.get('lines', []):
                print(line)

if __name__ == "__main__":
    extrair(sys.argv[1])

Linha 1: importamos a biblioteca fitz

Linha 5: Criamos uma função que abre o arquivo especificado quando executamos o código

Linha 6: o arquivo é aberto no modo leitura e binário, pois não é um arquivo texto e o PDF pode ter outros arquivos dentro, como por exemplo imagens que mantêm o seu formato original.

Linha 7: O arquivo é aberto utilizando o fitz, determinamos a fonte do fluxo de informação e o tipo.

Linha 15: A variável arquivo contêm todas as páginas do arquivo e podemos iterar em cima dela utilizando o for

Linha 16: A variável pages nos dá a possibilidade de iterar nas linhas do arquivo (que não necessariamente são linhas, pois um arquivo PDF não é construído dessa forma) e podemos extrair o formato em dicionário. O que nos dá acesso a muitas informações. Mas assim, podemos ter acesso a todos os dados do PDF, mesmo o que não é texto e então veremos muita informação inicialmente incompreensível, como por exemplo imagens em hexadecimal.

Linha 18: simplesmente exibe a o conteúdo da página

Extraindo e finalizando

Um método efetivo, porém trabalhoso, pode ser utilizado para extrair PDFs, podemos analisar os tipos de fontes, o tamanho, dependendo até outras características apresentadas. Para isso, analise a estrutura a seguir:

{'spans': [{'size': 6.891113758087158, 'flags': 20, 'font': 'CIDFont+F2', 'color': 132100, 'ascender': 0.89111328125, 'descender': -0.21630859375, 'text': '-', 'origin': (736.7427978515625, 420.8341979980469), 'bbox': (736.7427978515625, 414.77301025390625, 739.0677490234375, 422.30548095703125)}], 'wmode': 0, 'dir': (1.0, 0.0), 'bbox': (736.7427978515625, 414.77301025390625, 739.0677490234375, 422.30548095703125)}

Analisando essas estruturas do arquivo, podemos ver o padrão dos títulos que iniciam os capítulos e adicioná-los em um vetor, em outro vetor, adicionamos todas as estruturas e fazemos uma recombinação entre ambos.

No caso, o arquivo que estamos utilizando é algo muito mais simples e sua extração pode ser feita da seguinte forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import fitz
import sys
import json

def abrir(arquivo):
    arquivo = open(arquivo, 'rb').read()
    arquivo = fitz.Document(stream=arquivo, filetype="pdf")
    
def extrair(arquivo):
    abrir(arquivo)

    informacao = {}
    curso = ""

    for page in arquivo:
        for lines in page.get_text('dict')['blocks']:
            for line in lines.get('lines', []):
                if 'integral' in line['spans'][0]['text'] or 'diurno' in line['spans'][0]['text'] or 'matutino' in line['spans'][0]['text'] or 'noturno' in line['spans'][0]['text']:
                    curso = line['spans'][0]['text'].replace(" ", "-")
                    if curso not in informacao.keys():
                        informacao[curso] = []
                if line['spans'][0]['text'].isupper() and len(list(informacao.keys())):
                    informacao[curso].append(line['spans'][0]['text'].replace('*', ''))

    resultado = open(sys.argv[1].split('.')[0]+".json", "w")
    json.dump(informacao, resultado)
    resultado.close()


if __name__ == "__main__":
    extrair(sys.argv[1])

O que mudou do código anterior para esse foi apenas a inserção de uma ideia que possibilita analisarmos o conteúdo do dicionário que representa um elemento no PDF e caso o texto seja em CAIXA ALTA, que é o que nos interessa para extrair os nomes, podemos montar uma estrutura de dicionário e armazenar em um vetor, depois disso teremos todos os nomes das pessoas que passaram no vestibular da UNESP no ano de 2023 classificado em um formato JSON com o nome do curso, possibilitando pesquisar na estrutura posteriormente.

Linha 18: Adicionado condição que identifica na linha as palavras integral, noturno e diurno, que sempre aparecem nos nomes dos cursos

Linha 19: Nome da identificação do trecho que determina algum curso, muda um pouco por conta de algum identificador interno

Linha 20: Verifica se o curso já foi adicionado no vetor

Linha 21: Caso não tenha sido adicionado ao vetor, cria um novo espaço

Linha 22: Algo que especifica a característica de um nome de uma pessoa na lista do vestibular é que todas as letras são em caixa alta

Linha 25 a 27: Salvo os dados novos no arquivo

Conclusão

A extração de dados de arquivos PDF podem ser pouco triviais, este arquivo de exemplo tem poucos problemas, mas podemos ter situações em que textos “fantasmas”, textos quebrados em várias estruturas de dicionários diferentes, organizações de informações em tabelas, imagens e todo tipo de dificuldade imaginada. Algo que seria um exemplo de arquivo interessante de se extrair informações de forma perfeita são os diários oficiais de município, que além de não serem padronizados, podem ser literalmente um formato qualquer dentro do PDF.