Files
TextNLPClassifierApp/docs/prd_extrator_artigos_nlp.md
andreferraro 6a45368cb0 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
2026-08-20 19:22:20 -03:00

12 KiB

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

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_<nome_do_arquivo_origem>.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:
    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_<basename>.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)

{
  "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)

{
  "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": "<div><p>Franco Armani (6)...</p></div>",
        "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:

[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.