feat(extractor): implement multi-engine article content extractor

- Added scripts/extract_article_contents.py for batch scraping with stealth Foxcape and triple extraction (Trafilatura, Newspaper4k, Readability)
- Created unit, integration, and E2E test suite in tests/test_extract_article_contents.py (90/90 passing)
- Updated specs/003-article-content-extractor and README.md with usage documentation and CLI contracts
- Passed ruff linting/formatting and mypy type checking cleanly
This commit is contained in:
2026-08-20 19:22:20 -03:00
parent 6e3d57619b
commit 6a45368cb0
85 changed files with 18345 additions and 3897 deletions
@@ -0,0 +1,94 @@
# Data Model: Article Content Multi-Engine Extractor
## 1. Entities & Value Objects
### 1.1 InputArticle (Value Object)
Representa uma notícia contida no arquivo JSON de entrada.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `titulo` | `str` | Sim | Título original capturado na busca. |
| `url` | `str` | Sim | URL final/resolvida da matéria. |
| `pagina` | `int` | Não (default: 1) | Página em que o artigo foi encontrado. |
| `subtitulo` | `str | None` | Não | Subtítulo ou resumo do feed RSS. |
| `quando_publicado` | `str | None` | Não | String de data/hora original da listagem. |
---
### 1.2 TrafilaturaData (Value Object)
Dados estruturados extraídos pelo motor Trafilatura.
| Campo | Tipo | Descrição |
|---|---|---|
| `title` | `str | None` | Título do artigo extraído pelo Trafilatura. |
| `author` | `str | None` | Autor(es) identificados. |
| `date` | `str | None` | Data de publicação (ISO YYYY-MM-DD se identificada). |
| `description` | `str | None` | Descrição editorial / lead. |
| `categories` | `list[str]` | Categorias editoriais extraídas. |
| `tags` | `list[str]` | Tags associadas ao artigo. |
| `canonical_url` | `str | None` | URL canônica declarada no HTML. |
| `text` | `str` | Texto principal limpo e higienizado. |
| `raw_json` | `dict[str, Any]` | Payload completo retornado pelo `trafilatura.extract(..., output_format='json')`. |
| `error` | `str | None` | Mensagem de erro caso o motor falhe. |
---
### 1.3 NewspaperData (Value Object)
Dados estruturados e enriquecidos com NLP pelo motor Newspaper4k.
| Campo | Tipo | Descrição |
|---|---|---|
| `title` | `str | None` | Título identificado pelo Newspaper. |
| `authors` | `list[str]` | Lista de autores extraídos. |
| `publish_date` | `str | None` | Data de publicação formatada em ISO string. |
| `text` | `str` | Texto integral limpo da matéria. |
| `summary` | `str | None` | Resumo automático gerado pelo módulo de NLP. |
| `keywords` | `list[str]` | Palavras-chave relevantes identificadas por NLP. |
| `top_image` | `str | None` | URL da imagem de destaque principal. |
| `images` | `list[str]` | Lista de URLs de imagens presentes no artigo. |
| `meta_data` | `dict[str, Any]` | Dicionário com metadados brutos OpenGraph e Schema. |
| `error` | `str | None` | Mensagem de erro caso o motor falhe. |
---
### 1.4 ReadabilityData (Value Object)
Dados higienizados pelo algoritmo Readability (`readability-lxml`).
| Campo | Tipo | Descrição |
|---|---|---|
| `title` | `str | None` | Título limpo da página. |
| `short_title` | `str | None` | Título curto/resumido. |
| `cleaned_html` | `str | None` | Bloco HTML do corpo do artigo higienizado sem anúncios/scripts. |
| `cleaned_text` | `str | None` | Texto puro derivado do corpo higienizado. |
| `error` | `str | None` | Mensagem de erro caso o motor falhe. |
---
### 1.5 ExtractedArticle (Entity)
Resultado consolidado da extração de uma notícia específica.
| Campo | Tipo | Descrição |
|---|---|---|
| `input_meta` | `InputArticle` | Metadados da notícia original. |
| `extraction_status` | `Literal["success", "failed"]` | Status global do processamento do artigo. |
| `error_message` | `str | None` | Mensagem de erro se o carregamento da página falhou. |
| `crawled_url` | `str` | URL efetivamente navegada no navegador. |
| `page_title` | `str | None` | Título retornado pelo DOM (`document.title`). |
| `http_status` | `int | None` | Código HTTP retornado pelo servidor (se disponível). |
| `trafilatura` | `TrafilaturaData | None` | Resultado do motor Trafilatura. |
| `newspaper4k` | `NewspaperData | None` | Resultado do motor Newspaper4k. |
| `readability` | `ReadabilityData | None` | Resultado do motor Readability. |
---
### 1.6 ExtractionBatchReport (Aggregate Root)
Relatório consolidado de saída do processamento de um lote.
| Campo | Tipo | Descrição |
|---|---|---|
| `source_file` | `str` | Caminho do arquivo JSON de entrada processado. |
| `processed_at` | `str` | Timestamp ISO 8601 UTC do momento da execução. |
| `total_articles` | `int` | Total de artigos processados do arquivo de entrada. |
| `successful_articles` | `int` | Quantidade de artigos extraídos com sucesso. |
| `failed_articles` | `int` | Quantidade de artigos que falharam na extração. |
| `articles` | `list[ExtractedArticle]` | Lista de artigos enriquecidos com suas extrações. |