1

Pitch: Transformei a base aberta do CNPJ (73 milhões de estabelecimentos) em buscas de segundos, sem servidor e sem banco de dados

A Receita Federal publica todo mês a base completa do CNPJ: todas as empresas do Brasil, com endereço, telefone, e-mail, CNAE, porte, Simples/MEI e sócios. É uma mina de ouro para vendas B2B, mas é chata de usar: 37 arquivos zip, quase 8 GB compactados, uns 27 GB de CSV, e algumas armadilhas no caminho.

Quis responder perguntas como "restaurantes abertos nos últimos 90 dias em Campinas, com telefone" ou "de quem é o domínio empresa.com.br?" em segundos, sem pagar servidor nem banco. Deixo aqui como ficou e, principalmente, o que deu errado.

A arquitetura

zips da Receita → DuckDB (em disco) → Parquet → dataset privado no Hugging Face
                                                           │
                         actors na Apify (DuckDB/pyarrow) ◄─┘
  • DuckDB junta estabelecimentos, empresas, Simples e as tabelas auxiliares.
  • O resultado vira Parquet, organizado de quatro jeitos diferentes, um para cada tipo de busca: por UF ordenado por CNAE (leads), por prefixo do CNPJ (consulta em lote), por município (casar com Google Maps) e por domínio de e-mail.
  • Os arquivos ficam num dataset privado do Hugging Face: é de graça, não pede cartão e aceita leitura parcial por HTTP.
  • Cada busca roda como um "actor" na Apify e lê só os pedaços do Parquet que interessam. Como os arquivos estão ordenados, as estatísticas de mínimo e máximo de cada bloco dizem onde está o que você procura.

As armadilhas (a parte que vale ler)

1. O arquivo diz ISO-8859-1, mas não é. Tem bytes que só fazem sentido em Windows-1252, e o leitor latin-1 do DuckDB recusa o arquivo. Resolvi transcodificando para UTF-8 na extração: chunk.decode("cp1252", errors="replace").

2. O ignore_errors=true descartou 74% das empresas, calado. Nomes como BAR ""DO ZE"" LTDA usam aspas dobradas como escape. Na configuração padrão, o DuckDB achou essas linhas malformadas, e o ignore_errors simplesmente as pulou. Minha primeira versão tinha 8 milhões de estabelecimentos ativos em vez de 28 milhões. A correção é escape='"'. A lição de verdade: sempre compare a contagem de linhas lidas com a contagem de linhas do arquivo. O conversor agora se recusa a publicar se as duas não baterem.

3. 32 GB de RAM não bastaram. Juntar 28 milhões de estabelecimentos com 70 milhões de empresas e ordenar o resultado fez o sistema matar o processo. O que resolveu: banco DuckDB em disco, memory_limit='6GB', diretório temporário e um estado por vez. Para a base de consulta, com 73 milhões de linhas, ordenar tudo de uma vez não terminava nem em 20 minutos. Dividir por prefixo e ordenar cada pedaço (~700 mil linhas) levou 15 minutos no total.

4. Ler Parquet remoto com o DuckDB foi lento para busca pontual. Cada consulta refazia a checagem do arquivo e o redirecionamento do Hugging Face, uns 2 s por CNPJ. Troquei por pyarrow + HfFileSystem: leio o índice de cada arquivo uma vez, agrupo os CNPJs pelo bloco em que caem e baixo cada bloco uma vez só, 64 em paralelo. Também reduzi os blocos de 20 mil para 2 mil linhas. Resultado: 2.000 CNPJs foram de 209 s para 53 s, e o custo caiu para 1/6.

5. O e-mail cadastrado costuma ser do contador. Um único escritório aparece como e-mail de contato de 438 CNPJs. Para descobrir o CNPJ pelo domínio, o nome da empresa precisa "lembrar" o domínio (corsi.com.br → CORSI CONTABILIDADE). Nomes que só compartilham a cidade ("... CAMPINAS") não contam.

6. Casar Google Maps com a Receita. Telefone e CEP + número acham os candidatos; a semelhança do nome e a compatibilidade da atividade decidem. Um dentista não pode casar com a imobiliária do mesmo prédio. Num teste com 180 lugares reais de Campinas, 60% casaram, quase todos com confiança alta.

LGPD

Mesmo sendo dado público, alguns cuidados: MEIs ficam de fora por padrão nos leads (o contato costuma ser da pessoa física), sócios vêm sem CPF e o CPF que a Receita põe no fim da razão social de empresário individual é removido.

Atualização

Uma tarefa agendada no Windows verifica toda noite se saiu base nova. Quando sai, ela baixa, converte, compara as contagens com o mês anterior, sobe para o Hugging Face, confere se todos os arquivos chegaram (já tive upload que "terminou" sem uma pasta) e roda um teste em cada actor. Se algo falhar, aparece uma notificação.

Se quiser usar

Publiquei as quatro buscas na Apify, cobrando por resultado:

Se você já trabalhou com essa base e caiu em outra armadilha, conta aí nos comentários. Tenho certeza de que ainda tem mais.

Carregando publicação patrocinada...