PDF e OCR (atualizado!)

Post atualizado! A versão original do post está aqui!

Já precisou extrair dados de arquivos pdf? Bom, eu já. Eu trabalho com jurimetria e preciso extrair dados de diários de justiça, petições, sentenças, então já viu né…

A primeira pergunta que você precisa fazer antes de ler um pdf é: o arquivo é digital ou digitalizado?

  • Se for digital, significa que ele pode ser transcrito diretamente para vários formatos: texto, html, xml e até mesmo data.frames diretamente.

Vamos usar esse exemplo de PDF digital

Se estiver no desktop, é possível ver o documento abaixo:

  • Se for digitalizado, você precisará passar um algoritmo de OCR (Optical Character Recognition) para extrair os dados. Provavelmente seu output nesse caso será sempre texto.

Vamos usar esse exemplo de PDF digitalizado

Se estiver no desktop, é possível ver o documento abaixo:

Obs: é possível que seu arquivo seja digitalizado, mas já com uma OCR passada no próprio arquivo. Nesse caso, você pode tratar o documento como digital.

Os créditos dos pacotes abaixo vão todos para o Jeroen Ooms, um dos maiores autores de pacotes da comunidade R nos últimos dez anos. Sou fã desse cara!

Pacote {pdftools} para PDFs digitais

Para instalar o {pdftools} no Windows e no Mac, basta rodar

install.packages("pdftools")

Para instalar no Linux, siga as instruções desse link.

PDF para texto

library(tidyverse)
library(pdftools)
pdf <- 'caminho/para/pdf_digital.pdf'
txt <- pdf_text(pdf)

# imprimindo só os 500 primeiros caracteres da primeira página
cat(str_trunc(txt[1], 500))
## TJ/SP - Comarca de São Paulo
## Movimento Judiciário
## 
## Referência: Janeiro de 2011
## Foro: ADAMANTINA
## Unidade: 02 CUMULATIVA
## Planilha: CIVEL
## 
## 
## Dados da Unidade
## 1. Total de feitos em andamento                                            2756
## 2. Precatórias                                                                6
## 3. Processos
##   3.1 Processos cíveis                                                     2078
##     3.1.1 De Conhecimento                                                  1111
##   3.1.2 De...

PDF para HTML ou XML

Muitas vezes queremos pegar estruturas no texto que dependem da posição dos elementos. Por exemplo, o texto em um PDF pode estar dividido em várias colunas. Para isso, o ideal seria transformar o arquivo em dados semi-estruturados como HTML ou XML, que separam os elementos do conteúdo do PDF em tags.

Infelizmente, o pdftools não transforma em HTML nem em XML. Para soltar um HTML, vamos montar uma função que chama pdftohtml do poppler por command line.

pdf_html <- function(pdf) {
  infos <- pdf_info(pdf)              # pega infos do pdf
  html <- tempfile(fileext = '.html') # cria arquivo temporário

  # monta comando a ser executado.
  # no windows, você pode instalar o Poppler por aqui:
  # https://blog.alivate.com.au/poppler-windows/
  command <- sprintf(
    'pdftohtml -f 1 -l %s -q -i -s -noframes %s %s',
    infos$pages,
    normalizePath(pdf),
    html
  )

  system(command)                     # roda comando e salva
  txt <- readr::read_file(html)       # lê arquivo salvo
  file.remove(html)                   # remove arquivo temporário
  txt
}

Você pode brincar com o HTML usando o pacote xml2:

library(xml2)
html <- pdf_html(pdf)
html |>
  read_html() |>
  xml_find_all('//div') |>
  head()
## {xml_nodeset (6)}
## [1] <div id="page1-div" style="position:relative;width:1263px;height:892px;"> ...
## [2] <div id="page2-div" style="position:relative;width:1263px;height:892px;"> ...
## [3] <div id="page3-div" style="position:relative;width:1263px;height:892px;"> ...
## [4] <div id="page4-div" style="position:relative;width:1263px;height:892px;"> ...
## [5] <div id="page5-div" style="position:relative;width:1263px;height:892px;"> ...
## [6] <div id="page6-div" style="position:relative;width:1263px;height:892px;"> ...

PDF para tabelas

Use o {tabulizer}! Apesar de depender do polêmico {rJava} (que é um pacote chato de instalar e configurar) o {tabulizer} é capaz de extrair os dados diretamente para tabelas, de forma simples e intuitiva.

Para instalar o {tabulizer}, siga as instruções dessa página. Já adianto que pode não ser uma tarefa fácil, principalmente por conta do {rJava}.

Exemplo: Uma vez montei esse código para estruturar um pdf contendo gastos em obras públicas. Além de usar o {tabulizer}, usei os pacotes usuais do {tidyverse} e o {janitor} para arrumar os nomes das colunas.

library(tabulizer)

Vamos usar esse pdf de exemplo.

Se estiver no desktop, é possível ver o documento abaixo:

# No meu pc demorou 40 segundos.
tab <- extract_tables('caminho/para/pdf_compras.pdf')

Agora veja a magia do {tidyverse} e do pacote {janitor} posta em prática:

tab_tidy <- tab |>
  # transforma matrizes em tibbles
  map(as_tibble) |>
  # empilha
  bind_rows() |>
  # arruma nomes a partir da primeira linha
  janitor::row_to_names(1) |> 
  janitor::clean_names() |> 
  # tira espaços extras
  mutate(across(everything(), str_squish))

A tabela abaixo mostra as primeiras cinco linhas do resultado.

Pacote {tesseract} para PDFs digitalizados

O {tesseract} é uma biblioteca escrita em C e é uma das mais famosas ferramentas abertas para extração de textos a partir de imagens. O pacote em R de mesmo nome serve para usar essa biblioteca pelo R sem causar dores de cabeça.

Para nossa felicidade, hoje em dia os pacotes {pdftools} e {tesseract} estão integrados. Dessa forma, podemos utilizar a função pdftools::pdf_text_ocr() para extrair o texto de um PDF usando OCR.

Vamos usar esse pdf de exemplo.

Se estiver no computador, é possível ver o documento abaixo:

pdf <- 'caminho/para/pdf_digitalizado.pdf'
txt <- pdf_ocr_text(
  pdf = pdf,    # caminho do arquivo
  page = 1     # índice da página
)
## Converting page 1 to pdf_digitalizado_1.png... done!

Se o PDF tiver mais páginas, basta colocar os índices no texto.

# imprimindo só os 300 primeiros caracteres do resultado
cat(str_trunc(txt, 300))
## This is a sample document to test the PDF Image+Text OCR Engine.
## This is a sample document to test the PDF Image+Text OCR Engine.
## This is a sample document to test the PDF Image+Text OCR Engine.
## This is a sample document to test the PDF Image+Text OCR Engine.
## This is a sample document to test the...

Wrap-up

  • Se seu pdf for digital, use pdftools::pdf_text().
  • Se seu pdf for digitalizado, pdftools::pdf_text_ocr(), que usa o pacote {tesseract} por trás.
  • Existem alternativas úteis, como o Poppler em linha de comando e o pacote {tabulizer}.

É isso. Happy coding ;)

comments powered by Disqus