Skip to content

Reescrever camara/senado em cima das APIs oficiais (wrapper completo) #29

Description

@bdcdo

Contexto

Hoje camara.py e senado.py fazem HTML scraping das páginas de busca dos respectivos sites:

  • camara.py: HTML scraping de https://www.camara.leg.br/legislacao/busca — retorna 4 campos (link, titulo, descricao, ementa).
  • senado.py: HTML scraping de https://www6g.senado.leg.br/busca — retorna 5 campos.
  • Ambos aceitam apenas pesquisa, ano, tipo_materia.

Isso é frágil (quebra a cada mudança de layout) e expõe pouquíssimo do que as APIs oficiais oferecem. Esta issue substitui parte da #27 e propõe uma reescrita ambiciosa: wrapper completo das APIs oficiais, mantendo raspar(pesquisa=...) compatível.

Objetivo

Migrar camara.py e senado.py para consumir as APIs oficiais, com:

  • Cliente HTTP compartilhado com cache local, retry, rate limit e suporte async opcional.
  • Cobertura completa dos recursos das APIs (deputados, proposições, votações, eventos, frentes, órgãos, blocos, partidos, legislaturas, referências; senadores, matérias, votações, comissões, plenário, processo, orçamento, agência, reunião, lista, autoria).
  • Suporte a XML do Senado (a API do Senado é XML-default; o JSON tem chaves estranhas e precisa normalização).
  • Bulk download dos arquivos anuais da Câmara em CSV/XLSX/ODS/JSON/XML (cotas parlamentares, proposições, votações, eventos, etc.).
  • Joins helpers (ex.: `proposicao_completa(id)` que retorna autor + tramitação + votações + temas).
  • Compatibilidade: `raspar(pesquisa=..., ano=..., tipo_materia=...)` continua funcionando, agora alimentado pela API. Filtros novos (`dataInicio`/`dataFim`, `autor`, `situacao`, `sigla_uf`, `relator`, etc.) entram como acréscimo.
  • Testes unitários com fixtures (mocks da API) — não existem hoje.
  • Notebook de exemplo em `notebooks/dev/`.

APIs oficiais

Estrutura proposta

```
src/raspe/scrapers/
├── camara.py # mantém ScraperCamaraDeputados (compat raspar()) + delega para módulo
├── senado.py # mantém ScraperSenadoFederal (compat raspar()) + delega para módulo
├── camara_api/
│ ├── init.py # exporta classes-domínio
│ ├── client.py # cliente HTTP com cache, retry, rate limit
│ ├── deputados.py # Deputado, lista_deputados()
│ ├── proposicoes.py # Proposicao, lista_proposicoes()
│ ├── votacoes.py
│ ├── eventos.py, frentes.py, orgaos.py, blocos.py, partidos.py, legislaturas.py
│ ├── referencias.py
│ ├── bulk.py # download dos CSVs anuais
│ └── joins.py # proposicao_completa(), deputado_completo()
└── senado_api/
├── init.py
├── client.py # cliente com normalização XML→dict
├── senadores.py
├── materias.py
├── votacoes.py, comissoes.py, plenario.py, processo.py, orcamento.py, agencia.py, reuniao.py, lista.py, autoria.py
└── joins.py
```

Cliente HTTP compartilhado (`*_api/client.py`)

  • Sessão única (`requests.Session`) com adapter configurado.
  • Cache local com `requests-cache` (SQLite em diretório do usuário; TTL por endpoint — `referencias` cacheia mais tempo, dados ao vivo cacheiam menos).
  • Retry com backoff exponencial em 429/5xx respeitando `Retry-After` (refatorar a lógica de `base_scraper.py`, hoje orientada à paginação, para ser reusável aqui via `HTTPAdapter` + `urllib3.Retry`).
  • Rate limit configurável (default ~1 req/s) — falta no projeto hoje.
  • Async opcional com `httpx.AsyncClient` para varrer 513 deputados em paralelo.
  • Senado: normalização XML→dict (via `xmltodict`) ou parsing dos endpoints `.json` corrigindo as chaves estranhas (`@xml:lang`, listas-de-um-elemento que viram dict, etc.).

Modos de uso (compatibilidade preservada)

```python
import raspe

Modo legado (continua funcionando)

camara = raspe.camara()
df = camara.raspar(pesquisa="educação", ano=2024)

Novo modo (sub-recursos)

deputados = raspe.camara.deputados.lista(legislatura=57, sigla_uf="SP")
prop = raspe.camara.proposicoes.get(2390524)
df = prop.tramitacoes(formato="pandas")

Bulk

raspe.camara.bulk.cotas(ano=2024).to_parquet("cotas_2024.parquet")

Joins

completa = raspe.camara.joins.proposicao_completa(2390524) # autor + tramitação + votações + temas
```

Filtros adicionais nos `raspar()` (acréscimo, sem quebra)

  • `camara.py`: `dataInicio`/`dataFim`, `situacao`, `autor`, `sigla_uf`, `relator`, `abrangencia`.
  • `senado.py`: `dataInicio`/`dataFim`, `situacao`, `autor`, `assunto`.

Sub-tasks

  • Criar `camara_api/client.py` e `senado_api/client.py` (cache + retry + rate limit + async opcional).
  • Implementar `camara_api/{deputados,proposicoes,votacoes,eventos,frentes,orgaos,blocos,partidos,legislaturas,referencias}.py`.
  • Implementar `senado_api/{senadores,materias,votacoes,comissoes,plenario,processo,orcamento,agencia,reuniao,lista,autoria}.py` com normalização XML.
  • Refatorar `camara.py` e `senado.py` para usar o novo cliente, mantendo `raspar()` compatível e adicionando filtros novos.
  • Implementar `camara_api/bulk.py` (download de CSVs anuais).
  • Implementar `*_api/joins.py` (helpers de composição).
  • Adicionar `tests/test_camara.py` e `tests/test_senado.py` com mocks (`responses` ou `vcrpy`). Cobrir: paginação, retry em 429, cache hit, normalização XML do Senado, modo legado `raspar(pesquisa=...)`, sub-recursos.
  • Adicionar notebook `notebooks/dev/camara_senado_master_blaster.ipynb` demonstrando: (1) modo legado preservado; (2) listar deputados por legislatura/UF; (3) despesas de cota parlamentar; (4) proposições por autor + filtro de data; (5) votação completa (orientações + votos); (6) bulk download de um ano de votações; (7) buscar matérias do Senado por assunto; (8) composição de uma comissão.
  • (Opcional) CLI mínimo: `raspe camara deputados --legislatura 57 -o out.csv`.

Bibliotecas pesquisadas (do que copiar)

  • DadosAbertosBrasil (Python, v2.0.1 ago/2025, ativo) — referência principal. Modelo de classes-domínio (`Deputado`, `Proposicao`, `Senador`, ...), parâmetro `formato="pandas"|"json"|"url"`, filtros `contendo`/`excluindo` por substring, validação Pydantic. Não tem cache, retry, rate limit, async, CLI nem joins — é exatamente onde podemos somar.
  • camaraPy (Python, API legada XML) — fallback para casos não cobertos pela API v2 (votações antigas).
  • congressbr (R) — cobertura mais completa de Senado existente. Usar a lista de funções `sen_`/`cham_` como roteiro de exaustividade ao implementar `senado_api/`. Paper: https://doi.org/10.25222/larr.447
  • rcongresso (R) — Câmara API v2, tibbles tidyverse, padrão `fetch_*`.
  • cepesp-python (TSE) — referência de cache local interno.
  • MCP Câmara Server — exemplo recente de uso da API v2.

Não faz parte desta issue

Supersedes #27.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions