Validar, gerar e formatar CNPJ em Python
Funções em Python 3 sem dependências para validar, gerar e formatar CNPJ numérico e alfanumérico, testes com pytest parametrize e uso da API.
Validar CNPJ em Python cabe em poucas linhas da biblioteca padrão, e a mesma função atende o CNPJ numérico e o alfanumérico. A diferença está na conversão de cada caractere: em vez de int(c), que quebra com letras, usa-se ord(c) - 48. Para dígitos o resultado é o mesmo; para letras, A vale 17 e Z vale 42. O módulo abaixo funciona do Python 3.8 em diante e não depende de pacote nenhum. Antes de mexer em produção, vale ler o guia CNPJ alfanumérico: o que muda no seu sistema.
Validar CNPJ em Python (numérico e alfanumérico)
import re
import secrets
_SEPARADORES = re.compile(r"[./\-\s]")
_PESOS_1 = (5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2)
_PESOS_2 = (6,) + _PESOS_1
def _calcular_digito(base: str, pesos: tuple) -> int:
soma = sum((ord(c) - 48) * p for c, p in zip(base, pesos))
resto = soma % 11
return 0 if resto < 2 else 11 - resto
def validar_cnpj(valor: str) -> bool:
cnpj = _SEPARADORES.sub("", (valor or "").upper())
if not re.fullmatch(r"[A-Z0-9]{12}[0-9]{2}", cnpj):
return False
if cnpj[:12] == cnpj[0] * 12:
return False
base = cnpj[:12]
dv1 = _calcular_digito(base, _PESOS_1)
dv2 = _calcular_digito(base + str(dv1), _PESOS_2)
return cnpj[12:] == f"{dv1}{dv2}"O que cada parte resolve:
.upper()antes da limpeza faz12.abc.345/01de-35valer o mesmo que a forma em maiúsculas.[A-Z0-9]{12}[0-9]{2}aceita letras na raiz e na ordem, mas exige dígitos nos verificadores. A classe explícita também recusa dígitos Unicode de outros alfabetos, que\daceitaria.cnpj[:12] == cnpj[0] * 12recusa bases repetidas como 00.000.000/0000-00, que fecham a conta, mas não são CNPJs válidos._PESOS_2 = (6,) + _PESOS_1monta a segunda sequência de pesos (6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2) sem repetir números.
Fazendo a conta com 11.222.333/0001-81: a primeira soma é 102, o resto é 3 e o dígito é 8; a segunda soma é 120, o resto é 10 e o dígito é 1. Com 12.ABC.345/01DE-35, as somas são 459 e 424, os restos 8 e 6, e os dígitos 3 e 5. A calculadora de dígitos do CNPJ mostra cada produto.
Gerar CNPJ válido em Python
_DIGITOS = "0123456789"
_ALFANUMERICOS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def gerar_cnpj(alfanumerico: bool = False, formatado: bool = False) -> str:
alfabeto = _ALFANUMERICOS if alfanumerico else _DIGITOS
while True:
base = "".join(secrets.choice(alfabeto) for _ in range(12))
if base != base[0] * 12:
break
dv1 = _calcular_digito(base, _PESOS_1)
dv2 = _calcular_digito(base + str(dv1), _PESOS_2)
cnpj = f"{base}{dv1}{dv2}"
return formatar_cnpj(cnpj) if formatado else cnpjsecrets.choice usa o gerador criptográfico do sistema operacional, sem semente previsível. O CNPJ gerado é válido, mas pode coincidir com um CNPJ real por acaso; use-o em fixtures e bancos de teste. Se o teste depende de matriz ou filial, fixe a ordem (0001 é a matriz) e sorteie só a raiz, como explica o guia da estrutura do CNPJ. Para centenas de números sem escrever código, use o gerador de CNPJ em lote.
Formatar e limpar CNPJ em Python
def limpar_cnpj(valor: str) -> str:
return re.sub(r"[^A-Z0-9]", "", (valor or "").upper())
def formatar_cnpj(valor: str) -> str:
c = limpar_cnpj(valor)
if len(c) != 14:
raise ValueError(f"CNPJ precisa de 14 caracteres, recebeu {len(c)}")
return f"{c[:2]}.{c[2:5]}.{c[5:8]}/{c[8:12]}-{c[12:]}"formatar_cnpj("12abc34501de35") devolve "12.ABC.345/01DE-35", e aplicar a função de novo no resultado não muda nada. limpar_cnpj remove tudo que não for letra ou dígito, o que serve para normalizar antes de gravar, mas não substitui a validação. A formatação recusa tamanhos diferentes de 14 em vez de completar com zeros: se um CNPJ antigo perdeu o zero à esquerda numa coluna numérica, corrija com zfill(14) na migração, onde a origem do dado é conhecida.
Testes
Com pytest, parametrize gera um teste por tupla, e a saída mostra qual caso falhou.
import pytest
from cnpj import formatar_cnpj, gerar_cnpj, limpar_cnpj, validar_cnpj
@pytest.mark.parametrize(
("cnpj", "esperado"),
[
("11.222.333/0001-81", True),
("12.ABC.345/01DE-35", True),
("11.222.333/0001-82", False),
("00.000.000/0000-00", False),
],
)
def test_validar_cnpj(cnpj, esperado):
assert validar_cnpj(cnpj) is esperado
@pytest.mark.parametrize("alfanumerico", [False, True])
def test_gerados_sao_validos(alfanumerico):
assert all(validar_cnpj(gerar_cnpj(alfanumerico)) for _ in range(1000))
def test_formatar_e_limpar():
assert formatar_cnpj("12abc34501de35") == "12.ABC.345/01DE-35"
assert formatar_cnpj("12.ABC.345/01DE-35") == "12.ABC.345/01DE-35"
assert limpar_cnpj("11.222.333/0001-81") == "11222333000181"Rode com pytest -v. Para ampliar a tabela com letras em outras posições, use os exemplos de CNPJ alfanumérico para teste.
Usar a API em vez de reimplementar
A API do cpf.dev.br valida e gera CNPJs por HTTP, sem chave, com limite de 60 requisições por minuto por IP. Envie o CNPJ sem máscara; se precisar mandar a barra, codifique-a como %2F.
import requests
r = requests.get("https://www.cpf.dev.br/api/v1/cnpj/validar/12ABC34501DE35", timeout=5)
r.raise_for_status()
print(r.json())
# {'valido': True, 'cnpj': '12ABC34501DE35', 'formatado': '12.ABC.345/01DE-35',
# 'formato': 'alfanumerico', 'raiz': '12ABC345', 'ordem': '01DE', 'matriz': False}
lote = requests.get(
"https://www.cpf.dev.br/api/v1/cnpj/gerar",
params={"quantidade": 5, "formato": "alfanumerico", "formatado": "true"},
timeout=5,
)
print(lote.json()["cnpjs"])Para validar formulários em Django ou FastAPI, prefira a função local, que não depende de rede. Para conferir um número à mão, use o validador de CNPJ.