Guia de Spiders (MercoDocs)
Objetivo
Section titled “Objetivo”Cada spider coleta documentos publicos do Mercosul e exporta itens em JSONL, com foco em:
- navegacao de listagens dinamicas (iframe + paginacao)
- coleta de links de arquivos (pdf/doc/xls/zip etc.)
- enriquecimento por pagina de detalhe
- execucao incremental (evitar retrabalho)
Estrutura atual
Section titled “Estrutura atual”Arquivos principais em scraper/spiders/:
base.py: utilitarios compartilhados entre spidersmercosur_actas_y_anexos.pymercosur_comunicados_presidenciales.pymercosur_declaraciones_presidenciales.pymercosur_normativa.py
Classe base (BaseMercosurSpider)
Section titled “Classe base (BaseMercosurSpider)”A classe base centraliza comportamentos repetidos:
load_existing_data(): carrega URLs de detalhe ja processadas nooutput_filelog_failed_item(): registra erros nofailed_items_fileem JSONL_append_arquivo(): adiciona arquivos sem duplicar por URL_append_detail(): adiciona detalhes e resolve conflito de valores
Cada spider define ao menos:
namestart_urlsoutput_filefailed_items_file
Fluxo padrao de uma spider
Section titled “Fluxo padrao de uma spider”start_requests()cria requests com Playwright quando necessario.parse_list()abre iframe/lista, coleta linhas e resolve paginacao.- Para cada linha:
- monta item base
- identifica links de arquivo diretos
- identifica URL de detalhe
parse_details()enriquece o item com mais metadados e arquivos.- Item final eh exportado em JSONL pelo proprio Scrapy.
Contrato minimo do item
Section titled “Contrato minimo do item”Campos esperados para manter consistencia:
source_list_url: URL da listagemdetail_url(quando existir)arquivos: lista de objetos comdescricao,url,texto_linkdetalhes_completos(quando houver enriquecimento)
Observacao: cada spider pode incluir campos extras da tabela (ex.: Año, Título, Numero).
Como rodar
Section titled “Como rodar”Da raiz do projeto:
uv sync./scripts/crawl_spider.sh mercosur_declaraciones_presidencialesOu direto com Scrapy:
uv run --project . --directory scripts scrapy crawl mercosur_declaraciones_presidencialesTroubleshooting rapido
Section titled “Troubleshooting rapido”Frame ... nao encontrado- Verifique se a pagina mudou estrutura (iframe, dominio, seletor).
Listagem vazia- Revalide seletores de tabela e filtros padrao da pagina.
- Paginacao estagnada
- Verifique botao
nexte condicao de troca de primeira linha.
- Verifique botao
- Saida sem arquivos
- Reavalie regras de deteccao de URL de documento.
Caso real: Ver Acta e Ver Anexo em Declaracoes e Actas y Anexos
Section titled “Caso real: Ver Acta e Ver Anexo em Declaracoes e Actas y Anexos”Contexto: nas spiders mercosur_declaraciones_presidenciales e mercosur_actas_y_anexos, o item da listagem captura a URL de Ver Acta (ex.: /public/reuniones/doc/...).
O objetivo e capturar tambem os links internos da pagina de acta, incluindo Ver Anexo e metadados detalhados da reuniao.
Estrutura de Saída em Actas y Anexos
Section titled “Estrutura de Saída em Actas y Anexos”Para a spider mercosur_actas_y_anexos, a estrutura foi organizada para refletir a interface web, focando na extração profunda de atas e anexos:
ver_detalles: Objeto principal de enriquecimento.ver_actas: Lista de objetos capturados ao navegar no link “Ver Acta” dentro da página de detalhes.dados_basicos: Metadados da ata. Suporta múltiplos campos por linha (ex: Versión e Fecha Versión).reuniao: Metadados da reunião (Modalidad, Cidade/País). Inclui a lista de Asistentes capturada integralmente (mesmo com múltiplas linhas e tags aninhadas).arquivos_ata: Links diretos para PDF/DOC da ata.anexos: Lista de anexos, onde cada item contém a extração do modal (clicando em “Ver anexo”).ver_anexo: Dados do modal, incluindo a tabela de documentos_relacionados com links estruturados (Tipo, Número, Título, URL).
Captura de Modalidad
Section titled “Captura de Modalidad”A Modalidad (Presencial/Virtual) é capturada de forma redundante:
- Na Listagem: Extração via atributo
titleouaria-labelde ícones. - Nos Detalhes/Ata: Sincronização automática para a raiz do item principal caso descoberta durante a navegação profunda.
Iteração por Órgãos (mercosur_actas_y_anexos)
Section titled “Iteração por Órgãos (mercosur_actas_y_anexos)”A spider de Atas e Anexos agora automatiza a troca de órgãos no portal:
- Identifica todas as opções do menu
#organo. - Seleciona cada órgão sequencialmente.
- Aguarda o recarregamento dinâmico da tabela.
- Processa todas as páginas de resultados daquele órgão antes de prosseguir.
O campo Órgano é incluído em cada item para identificar a origem (ex: CMC, GMC, CCM).
Coleta Incremental vs. Completa (full_crawl)
Section titled “Coleta Incremental vs. Completa (full_crawl)”Por padrão, as spiders operam em modo incremental (atualização), pulando URLs de detalhes que já existem no arquivo de saída (output_file).
- Modo Atualização (Padrão):
- Carrega
processed_urlsdo arquivo JSONL. - Se a URL de detalhe já existe, o item é ignorado.
- Carrega
- Modo Coleta Completa:
- Força a re-coleta de todos os itens, ignorando o histórico.
- Uso:
uv run scrapy crawl mercosur_actas_y_anexos -a full_crawl=True
Melhorias na Extração de Dados (Base Spider)
Section titled “Melhorias na Extração de Dados (Base Spider)”A classe base agora utiliza uma lógica de extração sequencial e exaustiva para o conteúdo dos boxes (_extract_box_kv):
- Campos em Linha: Detecta padrões
Chave: Valordentro do mesmo elemento HTML (como em datas ou versões). - Suporte a Listas: Títulos seguidos de listas (como Asistentes) são consolidados automaticamente, preservando o texto interno de tags como
<b>. - Hierarquia de Anexos: Modais de anexo agora identificam tabelas internas e capturam links de documentos vinculados (como Diretivas ou Informes) de forma estruturada, incluindo seus respectivos metadados.
Abordagem A (testada e falhando): request direto da URL de acta
Section titled “Abordagem A (testada e falhando): request direto da URL de acta”Implementacao testada:
- detectar links
/public/reuniones/doc/...noparse_details - abrir cada URL com novo
scrapy.Request(com e semplaywright_include_page) - parsear os
a[href]da resposta para buscar anexos/documentos
Sintoma observado:
- em varios casos, a pagina retornada e de desafio anti-bot (
Just a moment...), sem os links reais de documento/anexo - resultado: o crawler chega na URL de acta, mas nao encontra
Ver Anexonem novos arquivos
Como reconhecer no debug:
- titulo/HTML da pagina contem
Just a moment - baixa contagem de links uteis na pagina de acta
- smoke test nao adiciona URLs novas em relacao ao baseline
Abordagem B (recomendada): navegacao browser-driven no mesmo contexto
Section titled “Abordagem B (recomendada): navegacao browser-driven no mesmo contexto”Ideia:
- em vez de abrir a acta por request separada, navegar/clicar no link
Ver Actadentro da mesma sessao/contexto de browser da pagina de detalhe - esperar seletor de conteudo real da acta (nao apenas
networkidle) - extrair
Ver Anexoe documentos finais dessa tela
Vantagem:
- tende a reaproveitar cookies, estado JS e checks ja resolvidos no contexto corrente
- reduz chance de cair no desafio anti-bot ao trocar de pagina
Risco/Trade-off:
- implementacao fica mais acoplada ao Playwright (menos “HTTP puro”)
- exige mais cuidado com fechamento de paginas e controle de fluxo assincrono
Abordagem C (fallback operacional): sessao assistida + estado persistido
Section titled “Abordagem C (fallback operacional): sessao assistida + estado persistido”Quando usar:
- quando as abordagens A e B continuam caindo em challenge anti-bot
- quando voce precisa destravar rapidamente uma coleta critica
Ideia:
- abrir execucao em modo visual (headful)
- resolver manualmente challenge/CAPTCHA uma vez
- salvar cookies/storage state do contexto
- reutilizar esse estado em execucoes seguintes da spider
Fluxo sugerido:
- Rodar a spider em ambiente local com navegador visivel.
- Completar challenge manualmente na primeira pagina bloqueada.
- Exportar o estado de sessao (cookies/localStorage) para arquivo.
- Configurar a spider para iniciar Playwright com esse estado.
- Monitorar expiracao do estado (normalmente expira e precisa renovar).
Vantagens:
- aumenta chance de acessar paginas internas de acta/anexo
- reduz retrabalho em coletas com bloqueio intermitente
Riscos/Trade-offs:
- menos automatizavel em CI puro
- estado pode expirar ou invalidar por IP/ambiente
- exige cuidado de seguranca com o arquivo de estado
Boas praticas para o estado de sessao:
- nao commitar arquivo de estado no repositório
- adicionar caminho do estado no
.gitignore - tratar como credencial operacional
- manter runbook de renovacao para contribuidores
Mini runbook (comandos)
Section titled “Mini runbook (comandos)”- Capturar estado de sessao manualmente:
uv run python scripts/capture_playwright_state.py --output debug/.state/mercodocs_playwright_state.json- Rodar spider reutilizando o estado salvo:
PLAYWRIGHT_STORAGE_STATE_PATH=debug/.state/mercodocs_playwright_state.json \uv run --project . --directory scripts scrapy crawl mercosur_declaraciones_presidenciales -O test_declaraciones_stateful.jsonl- Se voltar a bloquear, renovar estado:
- repetir passo 1 para gerar novo arquivo de estado
- rodar novamente o passo 2
- Teste dirigido para confirmar
Ver Acta/Ver Anexo:
uv run python debug/debug_acta_links.py \ --input data/test_declaraciones_stateful.jsonl \ --output debug/debug_acta_report.json \ --limit 5 \ --storage-state debug/.state/mercodocs_playwright_state.jsonInterpretacao rapida do relatorio:
has_challenge: true=> pagina caiu em anti-bot, sem DOM util para extracaoanexo_count > 0=> pagina exibe links de anexo no DOMdoc_count > 0=> pagina exibe links de documento no DOM
Criterio de aceite para esse caso
Section titled “Criterio de aceite para esse caso”- para pelo menos 1 item com
Ver Acta, o JSONL final deve conter:- URL da acta (manter)
- ao menos 1 link interno da acta (quando existir)
- links de
Ver Anexo(quando existir)
- sem duplicacao de URL em
arquivos
Boas praticas para contribuicao
Section titled “Boas praticas para contribuicao”- Prefira metodos pequenos e nomes diretos.
- Sempre use
_append_arquivo()para evitar duplicacao. - Sempre use
log_failed_item()comstageclaro. - Evite hardcode desnecessario de timeout sem justificativa.
- Mantenha compatibilidade do formato de saida JSONL.
Checklist de PR para spiders
Section titled “Checklist de PR para spiders”- Rodou a spider localmente
- Validou se
arquivosnao contem duplicatas - Validou pelo menos 1 pagina de detalhe
- Confirmou que
failed_items.jsonnao explodiu sem motivo - Atualizou este guia se mudou padrao arquitetural
Exemplo de esqueleto para nova spider
Section titled “Exemplo de esqueleto para nova spider”import scrapy
from scraper.spiders.base import BaseMercosurSpider
class MinhaSpider(BaseMercosurSpider): name = "minha_spider" start_urls = ["https://exemplo"] output_file = "minha_spider.jsonl" failed_items_file = "failed_items.json"
def start_requests(self): for url in self.start_urls: yield scrapy.Request( url, meta={"playwright": True, "playwright_include_page": True}, callback=self.parse_list, )
async def parse_list(self, response): # montar itens base e enviar requests de detalhe ...
async def parse_details(self, response, item): # enriquecer item, usar _append_arquivo/_append_detail yield item