124 lines
3.9 KiB
Markdown
124 lines
3.9 KiB
Markdown
# CLI Contract & Routing Specification: `scripts/extract_article_contents.py`
|
|
|
|
## 1. Interface de Linha de Comando (CLI)
|
|
|
|
O script `scripts/extract_article_contents.py` preserva 100% da interface CLI existente sem novos argumentos:
|
|
|
|
```bash
|
|
python scripts/extract_article_contents.py \
|
|
-i, --input PATH # (Obrigatório) JSON de busca de notícias de entrada
|
|
[-o, --output PATH] # (Opcional) Caminho do JSON de saída textual
|
|
[-l, --limit N] # (Opcional) Limite máximo de artigos a processar
|
|
[--lang, --language CODE] # (Opcional) Código do idioma para NLP (ex: pt, es, en)
|
|
[-t, --timeout SEC] # (Opcional) Timeout em segundos para navegação (default: 30)
|
|
[-s, --silent] # (Opcional) Suprime logs no stderr
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Regras de Resolução de Arquivos de Saída
|
|
|
|
### Caso A: Sem `-o/--output` (Padrão)
|
|
|
|
Para uma entrada: `out/river_plate.json`
|
|
|
|
- **Saída Textual**: `out/river_plate_extracted.json`
|
|
- **Saída de Mídia**: `out/river_plate_media.json` (gerado obrigatoriamente; contém `{"articles": []}` se nenhuma mídia for identificada)
|
|
|
|
### Caso B: Com `-o/--output` customizado
|
|
|
|
Para uma entrada: `out/noticias.json` com `-o out/processados/brasil_completo.json`
|
|
|
|
- **Saída Textual**: `out/processados/brasil_completo.json`
|
|
- **Saída de Mídia**: `out/processados/brasil_completo_media.json` (gerado obrigatoriamente; contém `{"articles": []}` se nenhuma mídia for identificada)
|
|
|
|
Regra:
|
|
- Diretório: mesmo diretório da saída textual informada.
|
|
- Nome base (stem): mesmo stem da saída textual informada.
|
|
- Sufixo: `_media`.
|
|
- Extensão: `.json`.
|
|
- Geração: O arquivo `*_media.json` MUST ser gerado em toda execução bem-sucedida do lote, sem omissão condicional.
|
|
|
|
---
|
|
|
|
## 3. Estrutura do Arquivo de Saída Textual (`*_extracted.json`)
|
|
|
|
```json
|
|
{
|
|
"source_file": "out/river_plate.json",
|
|
"processed_at": "2026-08-24T20:30:00.000000+00:00",
|
|
"total_articles": 8,
|
|
"successful_articles": 7,
|
|
"failed_articles": 1,
|
|
"articles": [
|
|
{
|
|
"input_meta": {
|
|
"titulo": "Notícia textual completa",
|
|
"url": "https://example.com/noticia-1",
|
|
"subtitulo": "Subtítulo",
|
|
"quando_publicado": "há 2 horas",
|
|
"pagina": 1
|
|
},
|
|
"extraction_status": "success",
|
|
"error_message": null,
|
|
"crawled_url": "https://example.com/noticia-1",
|
|
"page_title": "Título no DOM",
|
|
"http_status": 200,
|
|
"trafilatura": { "text": "...", "markdown": "...", "title": "..." },
|
|
"newspaper4k": { "text": "...", "summary": "...", "keywords": [] },
|
|
"readability": { "cleaned_text": "...", "cleaned_html": "..." }
|
|
},
|
|
{
|
|
"input_meta": {
|
|
"titulo": "Notícia onde todos provedores falharam",
|
|
"url": "https://example.com/falha"
|
|
},
|
|
"classification_status": "failed",
|
|
"error_message": "media classification providers unavailable",
|
|
"crawled_url": "https://example.com/falha",
|
|
"page_title": null,
|
|
"http_status": null,
|
|
"trafilatura": null,
|
|
"newspaper4k": null,
|
|
"readability": null
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Estrutura do Arquivo de Saída de Mídia (`*_media.json`)
|
|
|
|
```json
|
|
{
|
|
"articles": [
|
|
{
|
|
"input_meta": {
|
|
"titulo": "Vídeo dos melhores momentos do jogo",
|
|
"url": "https://example.com/video-gols",
|
|
"subtitulo": "Assista ao lance",
|
|
"quando_publicado": "há 1 hora",
|
|
"pagina": 1
|
|
},
|
|
"crawled_url": "https://example.com/video-gols",
|
|
"page_title": "Vídeo dos Melhores Momentos",
|
|
"http_status": 200,
|
|
"content_type": "media",
|
|
"media_type": "video"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Códigos de Saída (Exit Codes)
|
|
|
|
| Exit Code | Significado |
|
|
|:---|:---|
|
|
| `0` | Lote processado com sucesso (arquivos gravados) |
|
|
| `1` | Arquivo de entrada inexistente ou erro de argumentos CLI |
|
|
| `2` | Erro fatal não tratado |
|
|
| `130` | Interrupção pelo usuário (`SIGINT` / Ctrl+C) |
|