- 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
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):
- Trafilatura
- Newspaper4k
- 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
itemse 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 5para 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=Truepor 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.
- Texto principal limpo (
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).
- Título do artigo (
RF05 — Extração com Readability (readability-lxml)
- Para cada HTML coletado, processar via
Document(html). - Extrair:
- Título limpo (
title()eshort_title()). - Conteúdo limpo em HTML sem boilerplates/anúncios (
summary()). - Texto limpo puro derivado do conteúdo principal.
- Título limpo (
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>.jsonou 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 nostderr.
- Logs informativos enviados para
sys.stderrcom 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
- Execução de ponta a ponta: Executar o script apontando para
out/river_plate.jsone gerar com sucessoout/river_plate_extracted.json. - Uso Mandatório do Foxcape: Todas as páginas HTML devem ser renderizadas e baixadas via Foxcape headless.
- Completude das Extrações:
- O objeto
trafilaturadeve conter título, autor, data e texto limpo. - O objeto
newspaper4kdeve conter NLP summary, keywords, top_image e texto limpo. - O objeto
readabilitydeve conter HTML limpo e texto limpo.
- O objeto
- Resiliência a Falhas: Artigos com falha de conexão não devem quebrar o script e devem vir identificados como
extraction_status: "failed". - Logs Informativos: O operador visualiza no terminal o progresso item a item através do
stderr.