Files
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

4.1 KiB

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