import requests
import pandas as pd
import time
from datetime import date, timedelta
# ==========================================
# CONFIGURAÇÕES
# ==========================================
URL = "https://[Log in to view URL]"
# Período de publicação dos editais
hoje = date.today()
inicio = hoje - timedelta(days=1)
# UF consultada (filtro aplicado direto na API)
UF = "ES"
# Modalidades que serão consultadas
modalidades = {
6: "Pregão Eletrônico",
7: "Pregão Presencial",
8: "Dispensa",
}
# Quantidade de registros por página
TAMANHO_PAGINA = 40
# Lista que armazenará os editais encontrados
resultados = []
# ==========================================
# FUNÇÃO PARA CONSULTAR A API
# ==========================================
def consultar_pagina(params):
max_tentativas = 5
for tentativa in range(max_tentativas):
espera = 15 * (tentativa + 1)
# Melhoria 3: erros de rede não derrubam mais o script
try:
resposta = requests.get(
URL,
params=params,
timeout=60
)
except (requests.Timeout, requests.ConnectionError) as erro:
print(f"Erro de rede: {erro}")
print(f"Nova tentativa em {espera} segundos...")
time.sleep(espera)
continue
print("URL consultada:", resposta.url)
print("Status HTTP:", resposta.status_code)
# Melhoria 2: 204 = consulta sem resultados (corpo vazio)
if resposta.status_code == 204:
time.sleep(2)
return {"data": [], "totalPaginas": 0}
# Se o PNCP limitar as requisições
if resposta.status_code == 429:
print(
f"Limite de requisições atingido. "
f"Aguardando {espera} segundos..."
)
time.sleep(espera)
continue
# Se ocorrer outro erro HTTP
if resposta.status_code != 200:
print("Mensagem de erro do PNCP:")
print(resposta.text)
resposta.raise_for_status()
# Consulta realizada com sucesso
dados = resposta.json()
# Pausa para reduzir a frequência das consultas
time.sleep(2)
return dados
raise RuntimeError(
f"Não foi possível concluir a consulta "
f"após {max_tentativas} tentativas."
)
# ==========================================
# COLETA DOS EDITAIS
# ==========================================
for codigo, nome_modalidade in modalidades.items():
print("\n" + "=" * 50)
print(f"Consultando: {nome_modalidade}")
print("=" * 50)
pagina = 1
while True:
params = {
"dataInicial": inicio.strftime("%Y%m%d"),
"dataFinal": hoje.strftime("%Y%m%d"),
"codigoModalidadeContratacao": codigo,
"uf": UF, # Melhoria 1: filtro direto na API
"pagina": pagina,
"tamanhoPagina": TAMANHO_PAGINA,
}
# Consulta a página atual
dados = consultar_pagina(params)
registros = dados.get("data", [])
# Interrompe quando não houver mais registros
if not registros:
print("Nenhum registro nesta página.")
break
total_paginas = dados.get("totalPaginas", 1)
print(
f"Página {pagina} de {total_paginas} "
f"| Registros recebidos: {len(registros)}"
)
# Processa cada edital
for item in registros:
unidade = item.get("unidadeOrgao") or {}
orgao = item.get("orgaoEntidade") or {}
# Segurança: mantém somente órgãos da UF desejada
if unidade.get("ufSigla") != UF:
continue
resultados.append({
"Controle PNCP": item.get(
"numeroControlePNCP"
),
"Órgão": orgao.get("razaoSocial"),
"Município": unidade.get("municipioNome"),
"UF": unidade.get("ufSigla"),
"Modalidade": nome_modalidade,
"Número da compra": item.get("numeroCompra"),
"Processo": item.get("processo"),
"Objeto": item.get("objetoCompra"),
"Publicação": item.get("dataPublicacaoPncp"),
"Abertura": item.get("dataAberturaProposta"),
"Encerramento": item.get(
"dataEncerramentoProposta"
),
"Valor estimado": item.get(
"valorTotalEstimado"
),
"Situação": item.get("situacaoCompraNome"),
"Link de origem": item.get("linkSistemaOrigem"),
})
# Vai para a próxima página, se existir
if pagina >= total_paginas:
break
pagina += 1
# ==========================================
# TRATAMENTO DOS RESULTADOS
# ==========================================
print("\nFinalizando a coleta...")
df = pd.DataFrame(resultados)
if not df.empty:
# Remove editais duplicados
if "Controle PNCP" in df.columns:
df = df.drop_duplicates(
subset=["Controle PNCP"]
)
# Ordena pela data de publicação, quando disponível
if "Publicação" in df.columns:
df = df.sort_values(
by="Publicação",
ascending=False,
na_position="last"
)
# Exporta para Excel
nome_arquivo = "editais_es.xlsx"
df.to_excel(
nome_arquivo,
index=False
)
print("\n" + "=" * 50)
print("COLETA CONCLUÍDA!")
print("=" * 50)
print(f"Total de editais únicos encontrados: {len(df)}")
print(f"Arquivo gerado: {nome_arquivo}")
else:
print(
f"Nenhum edital de {UF} foi encontrado "
"no período consultado."
)
To embed this project on your website, copy the following code and paste it into your website's HTML: