# PRD — Extrator e Parser de Artigos Multimotor (Foxcape + Trafilatura + Newspaper4k + Readability) > **Status:** Proposto / Planejamento > **Versão:** 1.0.0 > **Data:** 2026-08-20 > **Autor:** Antigravity AI / DunaMedia > **Localização:** `docs/prd_extrator_artigos_nlp.md` --- ## 1. Visão Geral e Contexto ### 1.1 Objetivo do Produto Construir um pipeline/script autônomo e resiliente em Python para **extração profunda e enriquecimento de conteúdo de artigos de notícias** a partir de listagens JSON previamente geradas (ex.: pelo extrator do Google News). O pipeline utiliza o **Foxcape** em modo *headless* para acessar furtivamente as URLs finais, aguardar a renderização completa da árvore DOM e coletar o HTML íntegro. Em seguida, processa esse HTML simultaneamente através de **três motores consagrados de extração de conteúdo (NLP / Web Content Extraction)**: 1. **Trafilatura** 2. **Newspaper4k** 3. **Readability (`readability-lxml`)** O resultado consolidado com o máximo de informações extraídas por cada motor é exportado em formato JSON estruturado na pasta `out/`. --- ## 2. Personas e Casos de Uso | Persona | Necessidade | Benefício | |---|---|---| | **Engenheiro de Dados / ETL** | Ingerir em lote o conteúdo completo de notícias a partir de arquivos JSON de busca. | Automação resiliente sem necessidade de criar parsers manuais para cada portal de notícias. | | **Cientista de Dados / NLP** | Comparar ou combinar diferentes abordagens de extração de texto, resumos e metadados. | Obter em um único JSON os outputs estruturados de 3 extratores líderes da indústria. | | **Operador de Terminal / Analista** | Executar o script via CLI informando o arquivo de entrada e acompanhando o progresso em tempo real. | Visibilidade clara via `stderr` sem poluir a saída JSON no `stdout`. | --- ## 3. Arquitetura e Fluxo do Sistema ```mermaid flowchart TD A[Arquivo JSON de Entrada\n out/river_plate.json] --> B[Script CLI / Use Case\n Carrega lista de notícias] B --> C[Iterador de Artigos] C --> D[Motor Foxcape Headless\n Navega até a URL final] D --> E[Aguarda DOM carregar\n domcontentloaded / load] E --> F[Coleta HTML Renderizado Completo] F --> G1[Motor 1: Trafilatura\n Texto limpo, autor, data, tags, meta] F --> G2[Motor 2: Newspaper4k\n Artigo, autores, resumo NLP, keywords, top image] F --> G3[Motor 3: Readability\n Miolo HTML limpo, texto limpo, título] G1 --> H[Agregador de Extrações] G2 --> H G3 --> H H --> I[JSON Consolidado de Saída\n out/extracted_articles_*.json] ``` --- ## 4. Requisitos Funcionais (FR) ### RF01 — Ingestão de Arquivo JSON de Notícias - O sistema deve aceitar como entrada um arquivo JSON localizado em `out/` (ou caminho customizado via flag CLI `--input` / `-i`). - Deve validar a presença da lista `items` e extrair as propriedades fundamentais de cada item (`url`, `titulo`, `subtitulo`, `quando_publicado`, `pagina`). - Deve suportar opções para limitar o processamento a N itens (ex.: `--limit 5` para testes rápidos). ### RF02 — Navegação e Coleta com Foxcape (Obrigatório) - O acesso às páginas **deve obrigatoriamente** ser realizado com o pacote `foxcape`, aproveitando seu motor anti-bot (Camoufox + evasão de fingerprinting). - Deve executar em modo `headless=True` por padrão (`FoxcapeConfig(headless=True, humanize=False)`). - Deve navegar até a URL de cada notícia, aguardar o evento de carregamento do DOM (`wait_until="domcontentloaded"` com fallback para timeout) e extrair a string HTML completa e íntegra da página. - Deve reaproveitar a mesma instância/sessão do navegador entre as requisições do lote para otimizar velocidade e consumo de memória. ### RF03 — Extração Máxima com Trafilatura - Para cada HTML coletado, executar o motor `trafilatura`. - Extrair todos os campos disponíveis: - Texto principal limpo (`raw_text` / `text`). - Título (`title`). - Autor(es) (`author`). - Data de publicação (`date`). - Descrição / Subtítulo (`description`). - Categorias e Tags (`categories`, `tags`). - URL canônica (`canonical_url`). - Comentários estruturados (se disponíveis). - Extração em formato JSON estruturado nativo do Trafilatura. ### RF04 — Extração Máxima com Newspaper4k - Para cada HTML coletado, instanciar `Article(url, input_html=html)`. - Executar `parse()` e os métodos de NLP: - Título do artigo (`title`). - Autores (`authors`). - Data de publicação (`publish_date`). - Texto completo limpo (`text`). - Resumo gerado por NLP (`summary`). - Palavras-chave extraídas por NLP (`keywords`). - Imagem de destaque (`top_image`) e lista de todas as imagens (`images`). - Metadados brutos OpenGraph e Schema.org (`meta_data`). ### RF05 — Extração com Readability (`readability-lxml`) - Para cada HTML coletado, processar via `Document(html)`. - Extrair: - Título limpo (`title()` e `short_title()`). - Conteúdo limpo em HTML sem boilerplates/anúncios (`summary()`). - Texto limpo puro derivado do conteúdo principal. ### RF06 — Consolidação e Exportação de Resultados - O sistema deve agregar o resultado dos 3 extratores em um único objeto por notícia. - Deve salvar o resultado final em formato JSON na pasta `out/` (ex.: `out/extracted_.json` ou caminho fornecido via `--output` / `-o`). - Deve incluir metadados de execução global: - `source_file`: caminho do JSON de entrada. - `processed_at`: timestamp ISO 8601 da execução. - `total_articles`: quantidade de artigos no arquivo de entrada. - `successful_articles`: quantidade processada com sucesso. - `failed_articles`: quantidade com falha. - `articles`: array com os objetos consolidados. ### RF07 — Interface de Linha de Comando (CLI) e Logs em Tempo Real - Disponibilizar interface CLI: ```bash python scripts/extract_article_contents.py -i out/river_plate.json -o out/river_plate_extracted.json ``` - Flags suportadas: - `-i, --input`: Caminho do arquivo JSON de entrada (obrigatório). - `-o, --output`: Caminho do arquivo JSON de saída (opcional, padrão: `out/extracted_.json`). - `-l, --limit`: Limitar quantidade de artigos processados (opcional). - `-t, --timeout`: Timeout em segundos por página no Foxcape (padrão: 30s). - `-s, --silent`: Suprime logs visuais no `stderr`. - Logs informativos enviados para `sys.stderr` com emojis e timestamps: - `[INFO] 🚀 Iniciando extração de N artigos a partir de ...` - `[INFO] 🌐 [1/10] Foxcape navegando: https://...` - `[INFO] ⚙️ [1/10] Processando extratores (Trafilatura, Newspaper4k, Readability)...` - `[INFO] ✅ [1/10] Concluído com sucesso!` - `[INFO] 💾 Salvo com sucesso em: out/...` ### RF08 — Tolerância a Falhas e Resiliência - Se o acesso a uma URL falhar (ex.: timeout, 404, bloqueio severo), registrar o erro no item individual (`status: "failed"`, `error_message: "..."`) e prosseguir imediatamente para a próxima notícia do lote. - Se um dos 3 extratores falhar em um HTML específico, os outros 2 extratores devem continuar operando normalmente, registrando o erro no campo correspondente ao motor que falhou. --- ## 5. Requisitos Não Funcionais (NFR) | ID | Requisito | Critério | |---|---|---| | **RNF01** | **Furtividade Anti-Bot** | Utilizar Foxcape/Camoufox para evitar bloqueios de TLS/JA3 e Cloudflare. | | **RNF02** | **Eficiência de Recursos** | Manter instância persistente do navegador aberta em lote em vez de instanciar/fechar o browser a cada URL. | | **RNF03** | **Segregação de Streams** | `sys.stderr` exclusivo para logs; `sys.stdout` reservado para output JSON caso não seja especificado arquivo. | | **RNF04** | **Modularidade** | Código estruturado em classes/módulos independentes para cada extrator (`TrafilaturaExtractor`, `NewspaperExtractor`, `ReadabilityExtractor`). | | **RNF05** | **Compatibilidade de Plataforma** | Totalmente funcional em Windows, Linux e macOS (Python 3.10+). | --- ## 6. Esquema de Dados (Contratos de Interface) ### 6.1 Esquema do JSON de Entrada (`Input`) ```json { "query": "River Plate", "language": "es", "locale": "AR", "total_paginas": 1, "total_itens": 2, "scraped_at": "2026-08-20T14:37:38.233557+00:00", "items": [ { "titulo": "Los puntajes de River vs. Independiente Santa Fe", "subtitulo": "River empató sem gols...", "quando_publicado": "Thu, 20 Aug 2026 03:27:26 GMT", "url": "https://www.tycsports.com/river-plate/los-puntajes-id755914.html", "pagina": 1 } ] } ``` ### 6.2 Esquema do JSON Consolidado de Saída (`Output`) ```json { "source_file": "out/river_plate.json", "processed_at": "2026-08-20T15:00:00.000000+00:00", "total_articles": 1, "successful_articles": 1, "failed_articles": 0, "articles": [ { "input_meta": { "titulo_original": "Los puntajes de River vs. Independiente Santa Fe", "subtitulo_original": "River empató sem gols...", "quando_publicado": "Thu, 20 Aug 2026 03:27:26 GMT", "url_original": "https://www.tycsports.com/river-plate/los-puntajes-id755914.html", "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...", "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/...", "text": "Franco Armani (6): Seguro en las pocas llegadas...", "raw_json": {} }, "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...", "summary": "Resumo gerado por NLP do newspaper...", "keywords": ["river", "santa fe", "puntajes", "armani"], "top_image": "https://media.tycsports.com/...", "images": ["https://media.tycsports.com/..."], "meta_data": {} }, "readability": { "title": "Los puntajes de River vs. Independiente Santa Fe", "short_title": "Los puntajes de River", "cleaned_html": "

Franco Armani (6)...

", "cleaned_text": "Franco Armani (6): Seguro en las pocas llegadas..." } } ] } ``` --- ## 7. Dependências do Projeto As seguintes bibliotecas Python são necessárias para viabilizar este PRD: ```toml [dependencies] foxcape = ">=0.1.1" trafilatura = ">=1.8.0" newspaper4k = ">=0.9.3" readability-lxml = ">=0.8.1" beautifulsoup4 = ">=4.12.0" lxml = ">=4.9.0" ``` --- ## 8. Critérios de Aceite 1. **Execução de ponta a ponta**: Executar o script apontando para `out/river_plate.json` e gerar com sucesso `out/river_plate_extracted.json`. 2. **Uso Mandatório do Foxcape**: Todas as páginas HTML devem ser renderizadas e baixadas via Foxcape headless. 3. **Completude das Extrações**: - O objeto `trafilatura` deve conter título, autor, data e texto limpo. - O objeto `newspaper4k` deve conter NLP summary, keywords, top_image e texto limpo. - O objeto `readability` deve conter HTML limpo e texto limpo. 4. **Resiliência a Falhas**: Artigos com falha de conexão não devem quebrar o script e devem vir identificados como `extraction_status: "failed"`. 5. **Logs Informativos**: O operador visualiza no terminal o progresso item a item através do `stderr`.