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:
@@ -0,0 +1,62 @@
|
||||
# Extraction Pipeline Checklist: Article Content Multi-Engine Extractor
|
||||
|
||||
**Purpose**: Validate the clarity, completeness, and consistency of requirements for the multi-engine article content extraction pipeline (Foxcape, Trafilatura, Newspaper4k extraction, Readability, CLI and JSON contracts), excluding external NLP classification.
|
||||
**Created**: 2026-08-20
|
||||
**Feature**: [spec.md](../spec.md) | [data-model.md](../data-model.md) | [contracts/](../contracts/)
|
||||
|
||||
**Note**: This custom checklist is generated by the `/speckit-checklist` command based on feature context and requirements.
|
||||
**Review Ownership**: This checklist is a reviewer-owned requirements-quality review artifact. Mark an item `[x]` only when the reviewer determines the requirements-quality criterion is satisfied.
|
||||
**Marker Semantics**: `[x]` means the criterion has been reviewed and satisfied for requirements quality. It does not mean implementation work is complete.
|
||||
|
||||
---
|
||||
|
||||
## 1. Requirement Completeness
|
||||
|
||||
- [x] CHK001 Are input requirements explicitly specified for extracting articles from search JSON files? [Completeness, Spec §FR-001]
|
||||
- [x] CHK002 Are DOM loading and headless browser navigation requirements documented for Foxcape? [Completeness, Spec §FR-002]
|
||||
- [x] CHK003 Are the specific data fields to be extracted by Trafilatura (title, author, date, categories, tags, text) exhaustively defined? [Completeness, Spec §FR-004, DataModel §1.2]
|
||||
- [x] CHK004 Are the article content fields to be extracted by Newspaper4k (text, authors, publish_date, summary, keywords, images) documented without requiring external NLP classification? [Completeness, Spec §FR-005, DataModel §1.3]
|
||||
- [x] CHK005 Are Readability extraction requirements (sanitized HTML summary, clean text, titles) clearly stated? [Completeness, Spec §FR-006, DataModel §1.4]
|
||||
- [x] CHK006 Are persistent browser session lifecycle requirements documented for batch execution? [Completeness, Spec §FR-003, Plan]
|
||||
|
||||
---
|
||||
|
||||
## 2. Requirement Clarity & Non-Ambiguity
|
||||
|
||||
- [x] CHK007 Is the default naming convention for output JSON (`<input_stem>_extracted.json`) unambiguously specified when `--output` is omitted? [Clarity, Spec §FR-007, Clarifications]
|
||||
- [x] CHK008 Is the policy for discarding raw HTML strings to avoid payload bloat explicitly defined? [Clarity, Spec §FR-007, Clarifications]
|
||||
- [x] CHK009 Is the mechanism for inheriting the `language` parameter with fallback to `"en"` clearly defined? [Clarity, Spec §FR-005, Clarifications]
|
||||
- [x] CHK010 Are timeout parameters and default threshold values (30s) for page loading explicitly defined? [Clarity, Spec §FR-009, Contract §CLI]
|
||||
|
||||
---
|
||||
|
||||
## 3. Requirement Consistency & Data Contracts
|
||||
|
||||
- [x] CHK011 Do entity field names in `data-model.md` align consistently with the schemas in `contracts/json-schema.md`? [Consistency, DataModel §1.5, Contract §JSON]
|
||||
- [x] CHK012 Are CLI argument definitions in `contracts/cli-contract.md` aligned with functional requirement §FR-009? [Consistency, Spec §FR-009, Contract §CLI]
|
||||
- [x] CHK013 Is stream segregation (`stderr` for progress logs, `stdout` for JSON data) consistently maintained across all specification artifacts? [Consistency, Spec §FR-010, Contract §CLI]
|
||||
|
||||
---
|
||||
|
||||
## 4. Scenario & Edge Case Coverage
|
||||
|
||||
- [x] CHK014 Are error isolation requirements specified when a single URL fails (HTTP 404, connection timeout, bot block)? [Coverage, Spec §FR-008, Edge Cases]
|
||||
- [x] CHK015 Are failure handling requirements defined for cases where one specific extractor engine fails on valid HTML? [Coverage, Spec §UserStory2]
|
||||
- [x] CHK016 Are requirements specified for articles containing zero text content (e.g., photo galleries or video-only pages)? [Edge Case, Spec §EdgeCases]
|
||||
- [x] CHK017 Are character encoding and normalization requirements defined for multilingual text output? [Edge Case, Spec §EdgeCases]
|
||||
- [x] CHK018 Are requirements defined for empty input lists or non-existent input files? [Coverage, Spec §UserStory1, Contract §CLI]
|
||||
|
||||
---
|
||||
|
||||
## 5. Non-Functional & Operational Readiness
|
||||
|
||||
- [x] CHK019 Are success criteria quantified with measurable benchmarks (e.g., ≥90% extraction rate on valid articles)? [Measurability, Spec §SC-001]
|
||||
- [x] CHK020 Are execution sampling requirements via `--limit` testable within 30 seconds? [Measurability, Spec §SC-004]
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- All 20 items reviewed and validated against `docs/prd_extrator_artigos_nlp.md` and spec artifacts.
|
||||
- Focus strictly on web scraping, DOM rendering, multi-engine extraction (Trafilatura, Newspaper4k, Readability), CLI and JSON contracts. External NLP classifier is excluded and decoupled.
|
||||
- Items are numbered sequentially (CHK001–CHK020) and 100% satisfied.
|
||||
@@ -0,0 +1,34 @@
|
||||
# Specification Quality Checklist: Article Content Multi-Engine Extractor
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2026-08-20
|
||||
**Feature**: [spec.md](../spec.md)
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs) in user-facing outcomes
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- All items passed specification validation. Ready for planning phase (`/speckit-plan`).
|
||||
@@ -0,0 +1,47 @@
|
||||
# CLI Contract: Article Content Multi-Engine Extractor
|
||||
|
||||
## 1. Comando de Execução
|
||||
|
||||
```bash
|
||||
python scripts/extract_article_contents.py [OPTIONS]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Argumentos e Flags
|
||||
|
||||
| Flag Curta | Flag Longa | Tipo | Obrigatório | Padrão | Descrição |
|
||||
|---|---|---|---|---|---|
|
||||
| `-i` | `--input` | String (Path) | **Sim** | — | Caminho para o arquivo JSON de entrada (ex: `out/river_plate.json`). |
|
||||
| `-o` | `--output` | String (Path) | Não | `<input_stem>_extracted.json` | Caminho do arquivo JSON de destino. Se omitido, salva no mesmo diretório com sufixo `_extracted.json`. |
|
||||
| `-l` | `--limit` | Inteiro | Não | Todos | Limita a quantidade máxima de artigos a serem processados (útil para amostragem/testes). |
|
||||
| `--lang` | `--language` | String | Não | Do JSON / `"en"` | Sobrescreve o código de idioma para o módulo de NLP do Newspaper4k (ex: `pt`, `es`, `en`). |
|
||||
| `-t` | `--timeout` | Inteiro | Não | `30` | Timeout em segundos para o carregamento do DOM de cada página no Foxcape. |
|
||||
| `-s` | `--silent` | Flag booleana | Não | `False` | Suprime mensagens visuais de progresso e logs em `stderr`. |
|
||||
| `-h` | `--help` | Flag booleana | Não | `False` | Exibe manual de ajuda com todos os parâmetros disponíveis. |
|
||||
|
||||
---
|
||||
|
||||
## 3. Códigos de Saída (Exit Codes)
|
||||
|
||||
| Código | Significado | Condição |
|
||||
|---|---|---|
|
||||
| `0` | **Sucesso** | Execução concluída e arquivo JSON de saída gravado com êxito (mesmo que artigos individuais tenham falhado). |
|
||||
| `1` | **Erro de Argumento** | Arquivo de entrada inexistente, formato inválido ou parâmetros numéricos fora dos limites. |
|
||||
| `2` | **Erro de Inicialização** | Falha ao inicializar o motor Foxcape/navegador headless ou falta de dependências essenciais. |
|
||||
|
||||
---
|
||||
|
||||
## 4. Comportamento de Streams (I/O)
|
||||
|
||||
- **`stderr`**: Recebe mensagens de status informativas em tempo real:
|
||||
```text
|
||||
[INFO] 🚀 Iniciando extração de 10 artigos a partir de 'out/river_plate.json'
|
||||
[INFO] 🌐 [1/10] Foxcape navegando: https://www.tycsports.com/...
|
||||
[INFO] ⚙️ [1/10] Extraindo dados (Trafilatura, Newspaper4k, Readability)...
|
||||
[INFO] ✅ [1/10] Sucesso (Título: "Los puntajes de River...")
|
||||
...
|
||||
[INFO] 💾 Relatório final gravado com sucesso em: 'out/river_plate_extracted.json'
|
||||
[INFO] 📊 Resumo: 10 total | 10 sucessos | 0 falhas | Tempo: 14.2s
|
||||
```
|
||||
- **`stdout`**: Mantém-se silencioso se `--output` for fornecido (ou padrão), ou emite o JSON final caso o usuário redirecione a saída explicitamente.
|
||||
@@ -0,0 +1,88 @@
|
||||
# JSON Schema Contract: Article Content Multi-Engine Extractor
|
||||
|
||||
## 1. Schema de Entrada (Input JSON)
|
||||
|
||||
O arquivo de entrada deve conter a seguinte estrutura JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "string (opcional)",
|
||||
"language": "string (opcional, ex: 'pt', 'es', 'en')",
|
||||
"locale": "string (opcional, ex: 'BR', 'AR', 'US')",
|
||||
"total_itens": "integer (opcional)",
|
||||
"items": [
|
||||
{
|
||||
"titulo": "string (obrigatório)",
|
||||
"url": "string (obrigatório, URL HTTP/HTTPS)",
|
||||
"subtitulo": "string (opcional)",
|
||||
"quando_publicado": "string (opcional)",
|
||||
"pagina": "integer (opcional, default: 1)"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Schema de Saída (Output JSON)
|
||||
|
||||
O arquivo gerado conterá a estrutura consolidada:
|
||||
|
||||
```json
|
||||
{
|
||||
"source_file": "out/river_plate.json",
|
||||
"processed_at": "2026-08-20T15:30:00.000000+00:00",
|
||||
"total_articles": 1,
|
||||
"successful_articles": 1,
|
||||
"failed_articles": 0,
|
||||
"articles": [
|
||||
{
|
||||
"input_meta": {
|
||||
"titulo": "Los puntajes de River vs. Independiente Santa Fe",
|
||||
"url": "https://www.tycsports.com/river-plate/los-puntajes-id755914.html",
|
||||
"subtitulo": "River empató sem gols...",
|
||||
"quando_publicado": "Thu, 20 Aug 2026 03:27:26 GMT",
|
||||
"pagina": 1
|
||||
},
|
||||
"extraction_status": "success",
|
||||
"error_message": null,
|
||||
"crawled_url": "https://www.tycsports.com/river-plate/los-puntajes-id755914.html",
|
||||
"page_title": "Los puntajes de River vs. Independiente Santa Fe - TyC Sports",
|
||||
"http_status": 200,
|
||||
"trafilatura": {
|
||||
"title": "Los puntajes de River vs. Independiente Santa Fe",
|
||||
"author": "Ernesto Provitilo",
|
||||
"date": "2026-08-20",
|
||||
"description": "El análisis uno por uno...",
|
||||
"categories": ["River Plate", "Copa Sudamericana"],
|
||||
"tags": ["River", "Santa Fe"],
|
||||
"canonical_url": "https://www.tycsports.com/river-plate/los-puntajes-id755914.html",
|
||||
"text": "Franco Armani (6): Seguro en las pocas llegadas del rival...",
|
||||
"raw_json": { ... },
|
||||
"error": null
|
||||
},
|
||||
"newspaper4k": {
|
||||
"title": "Los puntajes de River vs. Independiente Santa Fe",
|
||||
"authors": ["Ernesto Provitilo"],
|
||||
"publish_date": "2026-08-20T03:27:26",
|
||||
"text": "Franco Armani (6): Seguro en las pocas llegadas del rival...",
|
||||
"summary": "Resumo gerado por NLP com as sentenças principais...",
|
||||
"keywords": ["river", "santa fe", "puntajes", "armani"],
|
||||
"top_image": "https://media.tycsports.com/adjuntos/800/2026/08/20/armani.jpg",
|
||||
"images": [
|
||||
"https://media.tycsports.com/adjuntos/800/2026/08/20/armani.jpg"
|
||||
],
|
||||
"meta_data": { ... },
|
||||
"error": null
|
||||
},
|
||||
"readability": {
|
||||
"title": "Los puntajes de River vs. Independiente Santa Fe",
|
||||
"short_title": "Los puntajes de River",
|
||||
"cleaned_html": "<div><p>Franco Armani (6): Seguro en las pocas llegadas...</p></div>",
|
||||
"cleaned_text": "Franco Armani (6): Seguro en las pocas llegadas...",
|
||||
"error": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -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. |
|
||||
@@ -0,0 +1,85 @@
|
||||
# Implementation Plan: Article Content Multi-Engine Extractor
|
||||
|
||||
**Branch**: `003-article-content-extractor` | **Date**: 2026-08-20 | **Spec**: [spec.md](file:///c:/Users/aferr/Projects/AFTech/DunaMedia/TextNLPClassifierApp/specs/003-article-content-extractor/spec.md)
|
||||
|
||||
**Input**: Feature specification from `specs/003-article-content-extractor/spec.md`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Construção do extrator de conteúdo de artigos em lote (`scripts/extract_article_contents.py`), que consome listagens de notícias em JSON, acessa e renderiza as páginas de forma stealth via `foxcape` em modo headless reutilizando sessão de navegador, e executa uma tríplice extração de conteúdo com **Trafilatura**, **Newspaper4k** e **Readability**, salvando o resultado consolidado e higienizado em JSON na pasta `out/`.
|
||||
|
||||
---
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: Python 3.10+
|
||||
**Primary Dependencies**: `foxcape` (Camoufox / stealth scraping), `trafilatura` (artigo e metadados), `newspaper4k` (artigo e NLP), `readability-lxml` (miolo e legibilidade), `beautifulsoup4`, `lxml`
|
||||
**Storage**: Arquivos JSON locais no diretório `out/`
|
||||
**Testing**: `pytest` com testes unitários e de integração mockando/testando o pipeline
|
||||
**Target Platform**: Windows / Linux / macOS (Terminal CLI)
|
||||
**Project Type**: CLI tool & modular extraction engine
|
||||
**Performance Goals**: Processamento em lote mantendo sessão de navegador ativa (estimativa de 1 a 2 segundos por notícia com DOMContentLoaded)
|
||||
**Constraints**: Operação 100% headless, descarte de HTML bruto da memória após extração para conter volume, logs em `stderr`
|
||||
**Scale/Scope**: Lotes de 1 a 100+ notícias por execução
|
||||
|
||||
---
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
| Princípio | Avaliação | Status |
|
||||
|---|---|---|
|
||||
| **I. Library / Modular Design** | Classes de extração desacopladas por motor (`TrafilaturaExtractor`, `NewspaperExtractor`, `ReadabilityExtractor`). | ✅ Aprovado |
|
||||
| **II. CLI Interface** | CLI via `scripts/extract_article_contents.py` com flags descritivas, `stderr` para status e `stdout` para JSON. | ✅ Aprovado |
|
||||
| **III. Test-First / Automated Tests** | Testes automatizados cobrindo parsing de JSON, orquestração dos 3 motores e resiliência a falhas de rede. | ✅ Aprovado |
|
||||
| **IV. Simplicity & YAGNI** | Uso direto dos módulos especializados existentes sem sobre-engenharia desnecessária. | ✅ Aprovado |
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/003-article-content-extractor/
|
||||
├── plan.md # Este plano de implementação
|
||||
├── research.md # Decisões técnicas e tradeoffs
|
||||
├── data-model.md # Entidades e modelos de dados
|
||||
├── quickstart.md # Guia de validação e execução
|
||||
├── contracts/
|
||||
│ ├── cli-contract.md # Contrato de linha de comando
|
||||
│ └── json-schema.md # Esquemas JSON de entrada e saída
|
||||
└── checklists/
|
||||
└── requirements.md # Checklist de validação da especificação
|
||||
```
|
||||
|
||||
### Source Code Layout
|
||||
|
||||
```text
|
||||
scripts/
|
||||
├── extract_google_news.py # Extrator RSS do Google News existente
|
||||
└── extract_article_contents.py # [NEW] Extrator e Parser Multimotor de Artigos
|
||||
|
||||
tests/
|
||||
├── test_extract_google_news.py # Testes do extrator Google News existente
|
||||
└── test_extract_article_contents.py # [NEW] Testes unitários e de integração do novo extrator
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 0: Outline & Research *(Completed)*
|
||||
- Decisões de arquitetura consolidadas em [research.md](file:///c:/Users/aferr/Projects/AFTech/DunaMedia/TextNLPClassifierApp/specs/003-article-content-extractor/research.md).
|
||||
- Definição do uso de sessão única de navegador `Foxcape(config=FoxcapeConfig(headless=True))` para aceleração em lote.
|
||||
- Definição da estratégia de try/catch em 2 níveis para resiliência máxima.
|
||||
|
||||
### Phase 1: Design & Contracts *(Completed)*
|
||||
- Entidades e contratos definidos em [data-model.md](file:///c:/Users/aferr/Projects/AFTech/DunaMedia/TextNLPClassifierApp/specs/003-article-content-extractor/data-model.md) e [contracts/](file:///c:/Users/aferr/Projects/AFTech/DunaMedia/TextNLPClassifierApp/specs/003-article-content-extractor/contracts/).
|
||||
- Guia de execução rápida e validação em [quickstart.md](file:///c:/Users/aferr/Projects/AFTech/DunaMedia/TextNLPClassifierApp/specs/003-article-content-extractor/quickstart.md).
|
||||
|
||||
### Phase 2: Tasks & Execution *(Completed)*
|
||||
- Decomposição das tarefas de implementação em `tasks.md` e execução 100% concluída.
|
||||
@@ -0,0 +1,77 @@
|
||||
# Quickstart & Validation Guide: Article Content Multi-Engine Extractor
|
||||
|
||||
Este guia descreve os passos para executar, testar e validar o extrator multimotor de artigos de notícias.
|
||||
|
||||
---
|
||||
|
||||
## 1. Pré-requisitos
|
||||
|
||||
Certifique-se de que as dependências necessárias estão instaladas no ambiente Python:
|
||||
|
||||
```bash
|
||||
pip install foxcape trafilatura newspaper4k readability-lxml beautifulsoup4 lxml pytest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Cenários de Validação
|
||||
|
||||
### Cenário 1: Extração com Amostragem Rápida (Limit 2)
|
||||
Testa o fluxo completo ponta a ponta com apenas 2 notícias para validação imediata:
|
||||
|
||||
```bash
|
||||
python scripts/extract_article_contents.py -i out/river_plate.json --limit 2
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
- Logs informativos no `stderr` indicando o progresso `[1/2]` e `[2/2]`.
|
||||
- Arquivo `out/river_plate_extracted.json` gerado automaticamente.
|
||||
- O JSON contém 2 artigos com os nós `trafilatura`, `newspaper4k` e `readability` populados.
|
||||
|
||||
---
|
||||
|
||||
### Cenário 2: Caminho Customizado de Saída
|
||||
Testa a especificação explícita do arquivo de saída:
|
||||
|
||||
```bash
|
||||
python scripts/extract_article_contents.py -i out/river_plate.json -o out/custom_test.json --limit 1
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
- Arquivo `out/custom_test.json` criado com 1 artigo extraído com sucesso.
|
||||
|
||||
---
|
||||
|
||||
### Cenário 3: Modo Silencioso (`--silent`)
|
||||
Testa a supressão de logs para integração em automações/pipes:
|
||||
|
||||
```bash
|
||||
python scripts/extract_article_contents.py -i out/river_plate.json --limit 1 --silent
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
- Nenhuma saída de log impressa no terminal.
|
||||
- Código de saída 0 retornado.
|
||||
|
||||
---
|
||||
|
||||
### Cenário 4: Resiliência contra URLs Inválidas
|
||||
Testa como o sistema lida com falhas pontuais de conexão ou páginas offline sem quebrar o lote:
|
||||
|
||||
```bash
|
||||
# Executa contra fixture de teste contendo URLs inexistentes
|
||||
pytest tests/test_extract_article_contents.py -k "test_resilience_on_failed_url"
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
- O teste passa confirmando que o artigo com erro recebeu `extraction_status: "failed"` e os demais concluíram com sucesso.
|
||||
|
||||
---
|
||||
|
||||
## 3. Validação Automatizada de Testes
|
||||
|
||||
Executar a suíte de testes unitários e de integração:
|
||||
|
||||
```bash
|
||||
pytest tests/test_extract_article_contents.py -v
|
||||
```
|
||||
@@ -0,0 +1,50 @@
|
||||
# Research: Article Content Multi-Engine Extractor
|
||||
|
||||
## 1. Technical Decisions & Tradeoffs
|
||||
|
||||
### Decision 1: Motor de Navegação e Renderização com `foxcape` em Sessão Única
|
||||
- **Decision**: Utilizar `foxcape` com `FoxcapeConfig(headless=True, humanize=False)` reutilizando uma única instância de navegador através de context manager (`with Foxcape(...) as scraper:`) para todo o lote.
|
||||
- **Rationale**:
|
||||
- Abrir e fechar o navegador (Camoufox) para cada URL aumentaria o tempo total de processamento em 3 a 5 segundos por artigo.
|
||||
- Reutilizar a sessão mantém a conexão quente, acelera o carregamento do DOM (`wait_until="domcontentloaded"`) e reduz significativamente o consumo de CPU/RAM.
|
||||
- O modo stealth e as evasões de fingerprinting do Foxcape contornam bloqueios Cloudflare, TLS e proteções comuns em portais de notícias.
|
||||
- **Alternatives Considered**:
|
||||
- `requests` / `httpx`: Muito rápidos, porém não executam JavaScript nem resolvem páginas que necessitam de renderização DOM dinâmica (SPAs).
|
||||
- `playwright` padrão: Suscetível a detecção anti-bot e requer configuração manual de stealth plugins.
|
||||
|
||||
---
|
||||
|
||||
### Decision 2: Orquestração Tripla de Extração de Conteúdo (NLP & Web Scraping)
|
||||
- **Decision**: Executar 3 motores de extração complementares e consolidados:
|
||||
1. **Trafilatura**: Padrão ouro em extração de texto limpo, metadados editoriais (`author`, `date`, `categories`, `tags`, `canonical_url`) e estrutura JSON nativa.
|
||||
2. **Newspaper4k**: Processamento avançado de artigo (`Article`), extração de autores, data de publicação, imagens (`top_image`, `images`), e processamento NLP nativo (`nlp()`) gerando resumo automático e *keywords* no idioma do artigo.
|
||||
3. **Readability (`readability-lxml`)**: Heurística clássica de legibilidade (Arc90) para isolar o nó HTML principal sem anúncios ou elementos supérfluos, além de extrair título limpo.
|
||||
- **Rationale**: Cada motor possui pontos fortes distintos. A combinação dos três em uma única passagem oferece a visão mais rica e confiável possível sobre o artigo.
|
||||
- **Alternatives Considered**:
|
||||
- Usar apenas um dos extratores: Perderia a complementaridade (ex.: Trafilatura tem melhor parsing de texto, mas Newspaper4k oferece NLP de keywords/resumo, e Readability oferece o HTML limpo do corpo).
|
||||
|
||||
---
|
||||
|
||||
### Decision 3: Resiliência e Isolamento de Falhas por Camada
|
||||
- **Decision**: Implementar try/catch defensivo em 2 níveis:
|
||||
1. **Nível de Rede/Navegador**: Se o Foxcape falhar em carregar uma URL (timeout, erro 404, bloqueio), registra `extraction_status: "failed"` com a mensagem de erro e avança para a próxima URL.
|
||||
2. **Nível de Extrator**: Cada extrator (`trafilatura`, `newspaper4k`, `readability`) roda em bloco isolado. Se um falhar, os outros dois concluem normalmente e o campo do extrator com falha registra `{"error": "<motivo>"}`.
|
||||
- **Rationale**: Garante taxa de sucesso máxima para lotes grandes sem interrupção abrupta do processamento.
|
||||
|
||||
---
|
||||
|
||||
### Decision 4: Herança Inteligente de Idioma para NLP
|
||||
- **Decision**: O Newspaper4k recebe o idioma informado no cabeçalho do JSON de busca (`"language": "es"`, `"pt"`, etc.), com fallback padrão para `"en"`, e permite sobrescrita pelo usuário via linha de comando (`-l, --language`).
|
||||
- **Rationale**: As rotinas de NLP do Newspaper4k (extração de palavras-chave e resumo) dependem de dicionários e stopwords específicos do idioma.
|
||||
|
||||
---
|
||||
|
||||
### Decision 5: Gerenciamento de Memória e Descarte do Raw HTML
|
||||
- **Decision**: Descartar a string HTML bruta da memória após a passagem pelos 3 extratores, persistindo no JSON final somente as entidades limpas e estruturadas.
|
||||
- **Rationale**: O HTML bruto de 50 artigos pode ocupar mais de 50MB, tornando o JSON volumoso e lento para análise downstream.
|
||||
|
||||
---
|
||||
|
||||
### Decision 6: Segregação de Streams e Feedback Visual em `stderr`
|
||||
- **Decision**: Emitir logs informativos e de progresso item a item (com numeração `[1/50]`, status e tempos) exclusivamente para `sys.stderr`, mantendo o `sys.stdout` intacto.
|
||||
- **Rationale**: Permite acompanhar a execução no terminal em tempo real sem comprometer a interoperabilidade com pipes UNIX (`jq`, redirecionamentos).
|
||||
@@ -0,0 +1,114 @@
|
||||
# Feature Specification: Article Content Multi-Engine Extractor
|
||||
|
||||
**Feature Branch**: `003-article-content-extractor`
|
||||
**Created**: 2026-08-20
|
||||
**Status**: Completed
|
||||
**Input**: User description: "Extrator e Parser de Artigos Multimotor a partir de listagens JSON usando Foxcape headless e tripla extração com Trafilatura, Newspaper4k e Readability (conforme docs/prd_extrator_artigos_nlp.md)"
|
||||
|
||||
---
|
||||
|
||||
## Clarifications
|
||||
|
||||
### Session 2026-08-20
|
||||
- Q: Como o script deve tratar o armazenamento do HTML bruto (*raw HTML*) baixado pelo Foxcape no arquivo JSON de saída? → A: Não incluir o HTML bruto no JSON final (descartar após extrações e persistir apenas os dados estruturados e limpos dos 3 motores para manter o arquivo leve e performático).
|
||||
- Q: Como o idioma para o processamento de NLP do Newspaper4k deve ser definido durante a extração? → A: Automático via JSON de entrada (herda o campo `"language"` do cabeçalho da busca com fallback para `"en"`), permitindo sobrescrita opcional via flag CLI (`--language` / `-l`).
|
||||
- Q: Qual deve ser o padrão de nomenclatura e localização do arquivo JSON gerado quando o operador não fornecer a flag `--output`? → A: Salvar no mesmo diretório adicionando o sufixo `_extracted.json` ao nome base do arquivo de entrada (ex.: `out/river_plate.json` → `out/river_plate_extracted.json`).
|
||||
|
||||
---
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - Extração Completa e Consolidada de Artigos em Lote (Priority: P1) 🌟 MVP
|
||||
|
||||
Como analista ou operador de dados, quero fornecer um arquivo JSON de listagem de notícias e obter como resultado um novo arquivo JSON enriquecido contendo o texto completo limpo, metadados e sumários estruturados de cada notícia processada por múltiplos motores de extração, sem que bloqueios anti-bot impeçam a coleta.
|
||||
|
||||
**Why this priority**: É o objetivo central do produto. Transforma referências e manchetes superficiais em conteúdo aprofundado, higienizado e categorizado para análise ou ingestão posterior.
|
||||
|
||||
**Independent Test**: Executar a extração apontando para um arquivo JSON com notícias válidas (ex.: `out/river_plate.json`) e verificar a geração de um arquivo de saída estruturado em `out/` contendo para cada artigo os blocos preenchidos de extração textual e metadados.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
1. **Given** um arquivo JSON de entrada contendo artigos com URLs válidas, **When** o processo de extração for disparado, **Then** o sistema acessa furtivamente cada URL em modo headless, aguarda o carregamento do DOM, obtém o HTML renderizado e processa simultaneamente a extração por três motores distintos, consolidando os resultados em um único arquivo JSON sem armazenar o HTML bruto.
|
||||
2. **Given** um arquivo de entrada vazio ou sem itens válidos, **When** o processo for executado, **Then** o sistema gera um arquivo de saída indicando 0 artigos processados e finaliza com status de sucesso.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Resiliência e Isolamento de Falhas por Artigo e Motor (Priority: P2)
|
||||
|
||||
Como engenheiro de dados executando rotinas em lote, quero que falhas em URLs individuais (como páginas inexistentes, timeouts ou instabilidade temporária do servidor) ou inconsistências em um dos motores de extração não interrompam o processamento das demais notícias do lote.
|
||||
|
||||
**Why this priority**: Garante que execuções longas com dezenas de notícias não sejam perdidas por falha pontual de um único portal externo.
|
||||
|
||||
**Independent Test**: Executar a extração contra um arquivo contendo uma URL inválida misturada com URLs válidas, confirmando que as válidas foram processadas com sucesso e a inválida foi registrada com status de erro sem abortar o pipeline.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
1. **Given** uma notícia com URL inacessível (ex.: erro 404 ou timeout de conexão), **When** a rotina processa a lista, **Then** o sistema registra o item com status de falha e mensagem explicativa, continuando o processamento do próximo item.
|
||||
2. **Given** um HTML que cause erro em um dos três motores de extração, **When** a etapa de análise é executada, **Then** os outros dois motores continuam sua extração normalmente e o erro do motor específico é encapsulado no registro daquele motor.
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Controle de Execução via Linha de Comando e Feedback Visual (Priority: P3)
|
||||
|
||||
Como operador de terminal, quero parametrizar a execução via CLI (definindo arquivo de entrada, caminho de saída opcional com padrão `_extracted.json`, limite de itens, idioma e nível de verbosidade) e acompanhar o progresso visualmente no terminal em tempo real sem comprometer a saída padrão de dados.
|
||||
|
||||
**Why this priority**: Oferece usabilidade, capacidade de testes parciais rápidos (amostragem) e compatibilidade com pipes e automações.
|
||||
|
||||
**Independent Test**: Executar o comando passando a flag `--limit 2` e verificar que apenas 2 notícias foram processadas, com mensagens de progresso emitidas no canal de diagnóstico (`stderr`).
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
1. **Given** a execução via linha de comando com parâmetros `--input out/river_plate.json` (sem `--output`) e `--limit 2`, **When** o processo inicia, **Then** o terminal exibe logs com status e percentual de avanço no canal de erro/diagnóstico, processa estritamente 2 itens e grava o arquivo automaticamente como `out/river_plate_extracted.json`.
|
||||
2. **Given** o uso da flag `--silent`, **When** o script é executado, **Then** nenhuma mensagem de log é impressa no terminal.
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- **Página com paywall severo ou bloqueio de bot**: O sistema deve capturar o HTML retornado, registrar eventuais limitações na extração e prosseguir sem quebrar a execução.
|
||||
- **Páginas com renderização pesada via JavaScript (SPA)**: O sistema deve aguardar o evento de carregamento do DOM antes de coletar o HTML para garantir que o conteúdo dinâmico esteja presente.
|
||||
- **Ausência de texto no corpo da notícia (apenas vídeo/galeria de fotos)**: Os extratores devem retornar campos de texto vazios de forma graciosa sem gerar exceções não tratadas.
|
||||
- **Caracteres especiais e encodings variados (UTF-8, Latin-1, etc.)**: Os textos extraídos devem ser normalizados para UTF-8 válido no JSON final.
|
||||
|
||||
---
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: O sistema DEVE receber como entrada um arquivo JSON contendo uma lista estruturada de notícias e validar a presença das URLs a serem processadas.
|
||||
- **FR-002**: O sistema DEVE navegar até cada URL utilizando navegação furtiva automatizada em modo headless, aguardando o carregamento completo do DOM.
|
||||
- **FR-003**: O sistema DEVE manter uma única sessão de navegador ativa reutilizada ao longo do lote para otimizar velocidade e consumo de memória.
|
||||
- **FR-004**: O sistema DEVE processar o HTML renderizado através do motor Trafilatura, extraindo texto limpo, título, autor, data, categorias/tags, descrição e metadados estruturados.
|
||||
- **FR-005**: O sistema DEVE processar o HTML renderizado através do motor Newspaper4k, utilizando o idioma herdado do JSON de entrada (com fallback para `"en"` ou sobrescrito por CLI) para extrair corpo do artigo, autores, data de publicação, resumo por NLP, palavras-chave por NLP, imagens e metadados OpenGraph.
|
||||
- **FR-006**: O sistema DEVE processar o HTML renderizado através do motor Readability, extraindo o conteúdo limpo principal (HTML sanitizado e texto puro) e título.
|
||||
- **FR-007**: O sistema DEVE consolidar os resultados dos três motores em um documento JSON único por execução, gravando-o por padrão como `<input_stem>_extracted.json` no mesmo diretório (ou no caminho fornecido via `--output`), sem persistir o HTML bruto baixado.
|
||||
- **FR-008**: O sistema DEVE registrar o status de extração (`success` ou `failed`) e mensagens de erro individuais para cada notícia processada.
|
||||
- **FR-009**: O sistema DEVE fornecer interface de linha de comando (CLI) com suporte a flags de arquivo de entrada (`-i, --input`), saída (`-o, --output`), limite de itens (`--limit`), idioma opcional (`-l, --language`), timeout (`-t, --timeout`) e modo silencioso (`-s, --silent`).
|
||||
- **FR-010**: O sistema DEVE enviar logs de progresso e status em tempo real exclusivamente para o fluxo de erro padrão (`stderr`), preservando o fluxo de saída padrão (`stdout`).
|
||||
|
||||
---
|
||||
|
||||
### Key Entities
|
||||
|
||||
- **InputArticle**: Representa a notícia recebida no arquivo de entrada, contendo título original, URL resolvida, subtítulo, data de publicação da listagem e número da página.
|
||||
- **ExtractedArticleResult**: Representa o resultado consolidado da extração de um artigo, agregando os metadados de entrada, status de coleta, URL final navegada, código de resposta HTTP e os payloads detalhados de cada um dos três extratores (`trafilatura`, `newspaper4k`, `readability`), omitindo o HTML bruto.
|
||||
- **BatchExtractionReport**: Representa o relatório global do lote, contendo metadados de auditoria (arquivo de origem, data/hora de processamento, total de itens, sucessos e falhas) e a lista de `ExtractedArticleResult`.
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: O sistema processa com sucesso pelo menos 90% das notícias válidas fornecidas em lote sem intervenção manual.
|
||||
- **SC-002**: Para páginas padrão de notícias com acesso público, todos os três motores de extração preenchem seus respectivos campos de texto limpo e título.
|
||||
- **SC-003**: A falha no carregamento ou na extração de 1 artigo isolado tem taxa de propagação de erro de 0% sobre os demais itens da fila.
|
||||
- **SC-004**: Operadores conseguem executar amostragens parciais de testes em menos de 30 segundos utilizando a flag de limite de itens.
|
||||
- **SC-005**: O arquivo JSON final gerado é 100% compatível com validadores JSON padrão (UTF-8 formatado).
|
||||
|
||||
---
|
||||
|
||||
## Assumptions
|
||||
|
||||
- Os arquivos JSON de entrada seguirão a estrutura produzida pelo extrator de notícias do Google News deste repositório (com chave `items` e propriedade `url` em cada item).
|
||||
- O ambiente de execução possui conectividade à internet para acessar os portais de notícias.
|
||||
- Recursos de hardware suficientes para execução de um processo de navegador headless (Firefox/Camoufox) em segundo plano.
|
||||
- As dependências de NLP e extração (`foxcape`, `trafilatura`, `newspaper4k`, `readability-lxml`) estarão devidamente instaladas no ambiente Python.
|
||||
@@ -0,0 +1,132 @@
|
||||
# Tasks: Article Content Multi-Engine Extractor
|
||||
|
||||
**Feature**: `003-article-content-extractor`
|
||||
**Spec**: [`specs/003-article-content-extractor/spec.md`](file:///c:/Users/aferr\Projects\AFTech\DunaMedia\TextNLPClassifierApp\specs\003-article-content-extractor\spec.md)
|
||||
**Plan**: [`specs/003-article-content-extractor/plan.md`](file:///c:/Users/aferr\Projects\AFTech\DunaMedia\TextNLPClassifierApp\specs\003-article-content-extractor\plan.md)
|
||||
**Status**: Completed
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Setup & Dependencies
|
||||
|
||||
**Purpose**: Garantir as dependências do ecossistema e a estrutura inicial do projeto.
|
||||
|
||||
- [X] T001 Atualizar dependências em `requirements.txt` incluindo `trafilatura`, `newspaper4k` e `readability-lxml`
|
||||
- [X] T002 [P] Validar importação e disponibilidade das bibliotecas `foxcape`, `trafilatura`, `newspaper` e `readability` no ambiente Python
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Estruturas e Modelos Base)
|
||||
|
||||
**Purpose**: Estruturas de dados, contratos de erro e classes base necessárias para todas as histórias de usuário.
|
||||
|
||||
- [X] T003 Definir modelos de dados e dataclasses (`InputArticle`, `TrafilaturaData`, `NewspaperData`, `ReadabilityData`, `ExtractedArticle`, `ExtractionBatchReport`) em `scripts/extract_article_contents.py`
|
||||
- [X] T004 Implementar funções utilitárias de I/O para leitura segura de JSON de busca e gravação com UTF-8 em `scripts/extract_article_contents.py`
|
||||
- [X] T005 [P] Criar suíte de testes base e fixtures de mock de HTML em `tests/test_extract_article_contents.py`
|
||||
|
||||
**Checkpoint**: Estruturas base e fixtures prontas para início do desenvolvimento das histórias de usuário.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 - Extração Completa e Consolidada de Artigos em Lote (Priority: P1) 🌟 MVP
|
||||
|
||||
**Goal**: Implementar a navegação headless stealth via Foxcape e os 3 motores de extração (Trafilatura, Newspaper4k, Readability) gerando o JSON consolidado.
|
||||
|
||||
**Independent Test**: Executar contra um HTML de teste ou URL mockada e verificar a extração de texto, títulos, metadados, autores, imagens e sumários NLP em um JSON sem raw HTML.
|
||||
|
||||
### Testes da User Story 1 (TDD)
|
||||
- [X] T006 [P] [US1] Criar testes unitários para o parser `TrafilaturaExtractor` em `tests/test_extract_article_contents.py`
|
||||
- [X] T007 [P] [US1] Criar testes unitários para o parser `NewspaperExtractor` (NLP, autores, imagens, resumo) em `tests/test_extract_article_contents.py`
|
||||
- [X] T008 [P] [US1] Criar testes unitários para o parser `ReadabilityExtractor` (HTML limpo, títulos) em `tests/test_extract_article_contents.py`
|
||||
- [X] T009 [US1] Criar teste de integração para o pipeline completo de extração multimotor em `tests/test_extract_article_contents.py`
|
||||
|
||||
### Implementação da User Story 1
|
||||
- [X] T010 [P] [US1] Implementar classe `TrafilaturaExtractor` para extração máxima de metadados, categorias, tags e texto limpo em `scripts/extract_article_contents.py`
|
||||
- [X] T011 [P] [US1] Implementar classe `NewspaperExtractor` com suporte a herança de idioma e métodos NLP (`parse`, `nlp`) em `scripts/extract_article_contents.py`
|
||||
- [X] T012 [P] [US1] Implementar classe `ReadabilityExtractor` para higienização e extração do miolo textual em `scripts/extract_article_contents.py`
|
||||
- [X] T013 [US1] Implementar gerenciador de sessão persistente do `Foxcape` (`with Foxcape(...)`) com espera de DOM (`domcontentloaded`) em `scripts/extract_article_contents.py`
|
||||
- [X] T014 [US1] Implementar orquestrador de lote e consolidação de resultados (descartando HTML bruto da memória) em `scripts/extract_article_contents.py`
|
||||
|
||||
**Checkpoint**: MVP funcional — O sistema já é capaz de ler uma lista de URLs, navegar com Foxcape, extrair pelos 3 motores e salvar o JSON consolidado.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 - Resiliência e Isolamento de Falhas por Artigo e Motor (Priority: P2)
|
||||
|
||||
**Goal**: Garantir tolerância a falhas para que timeouts, erros 404, bloqueios ou quebras em um único motor não abortem o lote.
|
||||
|
||||
**Independent Test**: Executar contra uma lista contendo URLs válidas e inválidas, confirmando que a inválida recebe status `failed` e as válidas continuam normalmente.
|
||||
|
||||
### Testes da User Story 2 (TDD)
|
||||
- [X] T015 [P] [US2] Criar teste para isolamento de erro em falha de navegação (timeout / 404) em `tests/test_extract_article_contents.py`
|
||||
- [X] T016 [P] [US2] Criar teste para isolamento de erro quando um único motor falha em `tests/test_extract_article_contents.py`
|
||||
|
||||
### Implementação da User Story 2
|
||||
- [X] T017 [US2] Implementar tratamento de exceções de rede e status HTTP individual por artigo em `scripts/extract_article_contents.py`
|
||||
- [X] T018 [US2] Implementar tratamento de exceções defensivo e encapsulamento de erro por motor de extração em `scripts/extract_article_contents.py`
|
||||
|
||||
**Checkpoint**: Sistema 100% resiliente contra instabilidades de portais e erros pontuais de parsing.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 - Controle de Execução via CLI e Feedback Visual (Priority: P3)
|
||||
|
||||
**Goal**: Interface de linha de comando completa com flags descritivas, suporte a limites (`--limit`), modo silencioso (`--silent`) e logs em `stderr`.
|
||||
|
||||
**Independent Test**: Executar `python scripts/extract_article_contents.py -i out/river_plate.json --limit 2` e verificar os logs em `stderr` e a criação de `out/river_plate_extracted.json`.
|
||||
|
||||
### Testes da User Story 3 (TDD)
|
||||
- [X] T019 [P] [US3] Criar testes de CLI para parsing de argumentos (`--input`, `--output`, `--limit`, `--language`, `--silent`, `--timeout`) em `tests/test_extract_article_contents.py`
|
||||
- [X] T020 [P] [US3] Criar teste de validação de segregação de streams (`stderr` vs `stdout`) e exit codes em `tests/test_extract_article_contents.py`
|
||||
|
||||
### Implementação da User Story 3
|
||||
- [X] T021 [US3] Implementar parser CLI (`argparse`) com todas as opções e convenção padrão de saída (`<stem>_extracted.json`) em `scripts/extract_article_contents.py`
|
||||
- [X] T022 [US3] Implementar sistema de logging visual em tempo real com emojis e status direcionado exclusivamente para `sys.stderr` em `scripts/extract_article_contents.py`
|
||||
- [X] T023 [US3] Implementar controle de códigos de saída (0 para sucesso, 1 para argumento inválido, 2 para erro de inicialização) em `scripts/extract_article_contents.py`
|
||||
|
||||
**Checkpoint**: Todas as histórias de usuário (US1, US2, US3) implementadas e integradas.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Polish & Validação Final
|
||||
|
||||
**Purpose**: Verificação ponta a ponta, documentação e conformidade.
|
||||
|
||||
- [X] T024 [P] Executar suíte completa de testes automatizados com `pytest`
|
||||
- [X] T025 Executar validação real de ponta a ponta contra `out/river_plate.json` gerando `out/river_plate_extracted.json`
|
||||
- [X] T026 [P] Atualizar documentação de uso no `README.md`
|
||||
- [X] T027 Executar `graphify update .` para manter o grafo de conhecimento do repositório sincronizado
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
P1[Phase 1: Setup & Dependencies\n T001, T002] --> P2[Phase 2: Foundational\n T003, T004, T005]
|
||||
P2 --> P3[Phase 3: User Story 1 MVP\n T006-T014]
|
||||
P3 --> P4[Phase 4: User Story 2 Resiliência\n T015-T018]
|
||||
P4 --> P5[Phase 5: User Story 3 CLI & Logs\n T019-T023]
|
||||
P5 --> P6[Phase 6: Polish & Validação\n T024-T027]
|
||||
```
|
||||
|
||||
### Oportunidades de Execução Paralela
|
||||
- **Phase 1**: `T002` pode rodar em paralelo após `T001`.
|
||||
- **Phase 3 (Testes & Parsers)**: `T006`, `T007`, `T008` (testes unitários) e `T010`, `T011`, `T012` (implementações dos 3 parsers) podem ser desenvolvidos em paralelo por atuarem em classes isoladas.
|
||||
- **Phase 4 & 5 (Testes)**: `T015`, `T016`, `T019`, `T020` podem ser implementados em paralelo antes das respectivas integrações no CLI.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (User Story 1 Only)
|
||||
1. Completar Fase 1 (Setup) e Fase 2 (Foundational).
|
||||
2. Implementar Fase 3 (User Story 1).
|
||||
3. **Validar MVP**: Testar extração de 1 artigo local com sucesso.
|
||||
|
||||
### Entrega Incremental
|
||||
1. Setup + Foundational → Base pronta.
|
||||
2. User Story 1 → Tripla extração funcional (MVP).
|
||||
3. User Story 2 → Resiliência total contra falhas externas.
|
||||
4. User Story 3 → Interface CLI rica e ergonômica.
|
||||
5. Polish → Testes 100% passando e validação real em lote.
|
||||
Reference in New Issue
Block a user