Files
TextNLPClassifierApp/README.md
T
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

15 KiB

🧠 TextNLPClassifierApp

Python Version License Code Style Type Checked Tests

Plataforma modular em Python para Classificação Multilíngue de Inerência de Entidades (NLP/LLM/ECP) e Extração Inteligente de Manchetes de Notícias com Evasão Anti-Bot (Google News RSS & Foxcape).


📑 Tabela de Conteúdos


🌟 Visão Geral

O TextNLPClassifierApp reúne ferramentas de engenharia de dados e processamento de linguagem natural:

  1. classify.py: Motor de classificação semântica e contextual que determina o grau de aderência e inerência de um documento Markdown em relação a uma entidade alvo definida em um ECP Snapshot (Entity Context Profile).
  2. scripts/extract_google_news.py: Extrator de notícias por palavra-chave, idioma e região geográfica que utiliza o motor stealth Foxcape (em modo headless), decodificação paralela de URLs para os links reais dos portais de notícias e feedback em tempo real.
  3. scripts/extract_article_contents.py: Extrator e parser de artigos multimotor com navegação stealth Foxcape headless e extração combinada via Trafilatura, Newspaper4k (NLP) e Readability, consolidando texto higienizado, autores, datas, imagens e resumos em JSON estruturado.

⚙️ Instalação e Setup

1. Clonar o Repositório e Criar Ambiente Virtual

git clone <URL_DO_REPOSITORIO>
cd TextNLPClassifierApp

python -m venv .venv
# Windows (PowerShell)
.venv\Scripts\Activate.ps1
# Linux/macOS
source .venv/bin/activate

2. Instalar Dependências

pip install -r requirements.txt

3. Baixar Binários do Navegador Stealth (Camoufox)

O motor Foxcape utiliza o navegador customizado Camoufox para evasão avançada de fingerprinting:

python -m camoufox fetch

1. 🧠 Classificador de Conteúdo e Inerência (NLP / LLM / ECP)

O que é e Como Funciona

O classificador avalia se um texto em Markdown trata centralmente, contextualmente ou apenas de forma superficial de uma determinada entidade alvo (ex: empresa, figura pública, clube, conceito). A entidade é descrita através de um ECP Snapshot (Entity Context Profile) contendo nomes canônicos, apelidos (aliases), âncoras temáticas, âncoras negativas e entidades de contexto relacional.

O sistema suporta nativamente 6 idiomas: Português (pt), Inglês (en), Espanhol (es), Francês (fr), Alemão (de) e Italiano (it).

Arquitetura de Classificação em 3 Tiers

flowchart TD
    MD[Markdown Content] --> Pre[Pré-processamento & Detecção de Idioma]
    ECP[ECP Snapshot JSON] --> Pre
    Pre --> T1[Tier 1: Determinístico & Regras NLP]
    T1 -- Alta Confiança --> Decision[Decisão Final JSON]
    T1 -- Score Intermediário / Ambíguo --> T2{Tier 2 Habilitado?}
    T2 -- Sim --> Emb[Tier 2: Similaridade Semântica / Embeddings]
    T2 -- Não --> Decision
    Emb -- Inconclusivo --> T3{Tier 3 Habilitado?}
    T3 -- Sim --> LLM[Tier 3: LLM Inherence Adapter]
    T3 -- Não --> Decision
    LLM --> Decision
  • Tier 1 (Determinístico / NLP Leve): Análise de frequência de termos, detecção de âncoras temáticas no primeiro terço do documento, contagem de aliases e penalização por âncoras negativas.
  • Tier 2 (Vetorial / Embeddings) (Opcional: --enable-embeddings): Projeção vetorial e cálculo de cosseno entre o perfil da entidade e os parágrafos do documento.
  • Tier 3 (LLM Fallback) (Opcional: --enable-llm): Consulta a modelo de linguagem para desambiguação de casos limiares e sutilezas semânticas.

Categorias de Decisão

Categoria Descrição
DIRECT_INHERENT O conteúdo é centrado e focado diretamente na entidade alvo.
CONTEXTUAL_INHERENT A entidade é relevante no contexto da discussão, mesmo dividindo foco com outros temas.
TANGENTIAL A entidade é mencionada apenas de passagem ou em listas ilustrativas.
NOT_RELATED O conteúdo não possui relação substantiva com a entidade alvo.

Formato do ECP Snapshot e Markdown

Exemplo de ECP Snapshot (ecp_sample.json):

{
  "target_entity_id": "ent_river_plate",
  "target_name": "River Plate",
  "aliases": ["Club Atlético River Plate", "Millonario", "El Más Grande"],
  "domain": "sports/football",
  "anchors": ["Monumental", "Copa Libertadores", "Marcelo Gallardo", "Copa Sudamericana"],
  "negative_anchors": ["Boca Juniors vitória", "Flamengo campeão"],
  "graph_version": "1.0.0",
  "related_entities": [
    {
      "entity_id": "ent_gallardo",
      "name": "Marcelo Gallardo",
      "relation_type": "manager",
      "weight": 0.85,
      "aliases": ["Muñeco"]
    }
  ]
}

Exemplos de Uso CLI

# Classificação Determinística padrão (Tier 1)
python classify.py --ecp tests/fixtures/sample_ecp.json --content tests/fixtures/sample_article.md

# Salvar resultado em arquivo JSON formatado
python classify.py --ecp ecp.json --content artigo.md --output out/resultado_classificacao.json

# Habilitar camadas adicionais (Embeddings e LLM)
python classify.py --ecp ecp.json --content artigo.md --enable-embeddings --enable-llm

2. 📰 Extrator de Manchetes do Google News

O que é e Como Funciona

O script scripts/extract_google_news.py é um extrator CLI autônomo projetado para consultar o feed RSS do Google News com máxima velocidade, resiliência e integridade de dados.

Diferenciais Técnicos

  • 🛡️ Foxcape Headless Anti-Bot: Utiliza o motor foxcape com FoxcapeConfig(headless=True) e Camoufox para evitar bloqueios 429/403 e captchas em segundo plano sem abrir navegadores visuais.
  • 🔗 Resolução Automática de URLs Reais: Decodifica em paralelo (ThreadPoolExecutor) as URLs intermediárias do Google News (news.google.com/rss/articles/...) entregando diretamente o link final do portal de notícia (Olé, TyC Sports, ge, ESPN, BBC, etc.).
  • 🧹 Higienização de HTML: Sanitiza resumos e descrições, eliminando tags HTML residuais (<ol>, <li>, <a>, <span>).
  • 📊 Logging Informativo no Terminal: Emite o status passo a passo no canal stderr sem poluir a saída JSON em stdout (100% compatível com pipes e jq).
  • 🌎 Mapeamento de Idioma & Região: Mapeia automaticamente códigos regionais (pt → BR:pt-BR, es → AR:es-419, en → US:en-US) permitindo customização com --locale.

Argumentos e Flags de Linha de Comando

Flag Tipo Obrigatório Padrão Descrição
-q, --query, --keyword str Sim — Termo de pesquisa (ex: "River Plate").
-l, --lang, --language str Não "pt" Código do idioma (pt, es, en, de, fr, it).
--locale, --country str Não None País/região editorial (BR, AR, MX, US, GB, ES).
-p, --max-pages int Não 1 Quantidade de páginas (1 a 10; cada página traz 10 itens).
-o, --output str Não None Caminho para salvar o arquivo JSON.
--pretty flag Não False Formata o JSON no terminal com indentação de 2 espaços.
--no-resolve-urls flag Não False Desativa a resolução e mantém as URLs brutas do Google News.
-s, --silent, --quiet flag Não False Suprime os logs de progresso no stderr.

Exemplos Práticos de Uso

1. River Plate (Argentina / Espanhol / 2 Páginas / Salvar em Arquivo)

python scripts/extract_google_news.py -q "River Plate" -l es --locale AR -p 2 -o out/river_plate.json
  • Saída no Terminal:
[INFO] 🔍 Consultando Google News: 'River Plate' (idioma: es, locale: AR, max_pages: 2)...
[INFO] 📥 Feed RSS recebido (162117 bytes).
[INFO] 📰 20 artigos extraídos do feed XML.
[INFO] 🔗 Decodificando 20 URLs do Google News para os portais reais...
[INFO] ✅ 20/20 URLs resolvidas com sucesso para os domínios de origem.
[INFO] 💾 Arquivo salvo com sucesso: 'out/river_plate.json' (20 notícias).

2. Cruzeiro (Brasil / Português / Formatado no Terminal)

python scripts/extract_google_news.py --query "Cruzeiro" --lang pt --locale BR --pretty

3. Fórmula 1 (Inglaterra / Inglês)

python scripts/extract_google_news.py --query "Formula 1" --lang en --locale GB --pretty

4. Filtragem com jq em Modo Silencioso

python scripts/extract_google_news.py -q "inteligência artificial" -s | jq '.items[].url'

3. 📰 Extrator e Parser Multimotor de Artigos

Visão Geral e Tríplice Extração

O script scripts/extract_article_contents.py lê os arquivos JSON gerados pelo extrator do Google News (ou qualquer lista contendo items com url), acessa cada página via Foxcape em modo stealth headless (reutilizando uma única sessão de navegador ativa com espera do evento domcontentloaded), e executa simultaneamente 3 motores especializados de extração:

  1. Trafilatura: Texto principal higienizado, autores, data de publicação, categorias, tags, URL canônica e payload estruturado nativo.
  2. Newspaper4k: Artigo completo, autores, imagens (top_image e galeria), resumo automático e palavras-chave (keywords) extraídas por NLP nativo.
  3. Readability (readability-lxml): Miolo limpo em HTML sem anúncios ou scripts supérfluos, títulos e texto puro formatado.

O JSON final consolidado é salvo em out/ com descarte de strings HTML brutas para manter o arquivo leve e veloz.

Argumentos e Flags CLI

Parâmetro Tipo Padrão Descrição
-i, --input Caminho (obrigatório) — Arquivo JSON de busca de notícias (ex: out/river_plate.json).
-o, --output Caminho (opcional) <input_stem>_extracted.json Arquivo JSON de destino consolidado.
-l, --limit Inteiro (opcional) Todos Limita a quantidade máxima de notícias processadas.
--lang, --language String (opcional) Do JSON / en Sobrescreve o código de idioma para o NLP do Newspaper4k (ex: pt, es, en).
-t, --timeout Inteiro (opcional) 30 Timeout em segundos por página no Foxcape.
-s, --silent Flag booleana False Suprime logs informativos de progresso no stderr.

Exemplos de Uso

1. Extração Completa Automática

python scripts/extract_article_contents.py -i out/river_plate.json
# Gera automaticamente out/river_plate_extracted.json

2. Amostragem Rápida (Limit 2 Notícias)

python scripts/extract_article_contents.py -i out/river_plate.json --limit 2

3. Destino Customizado e Timeout Ajustado

python scripts/extract_article_contents.py -i out/petrobras_result.json -o out/petrobras_full.json --timeout 45

📁 Estrutura do Projeto

TextNLPClassifierApp/
├── classify.py                     # CLI principal do Classificador de Inerência
├── scripts/
│   ├── __init__.py                 # Pacote utilitário de scripts
│   ├── extract_google_news.py      # CLI de Extração de Manchetes do Google News
│   └── extract_article_contents.py # CLI de Extração e Parsing Multimotor de Artigos
├── src/                            # Módulos centrais do classificador
│   ├── classifier.py               # Orquestrador de classificação (Tier 1, 2, 3)
│   ├── models.py                   # Modelos de dados e esquemas (ECPSnapshot, Decision)
│   ├── preprocessor.py             # Normalização de texto e detecção de idioma
│   └── adapters/                   # Adaptadores opcionais de Embeddings e LLM
├── specs/                          # Especificações e planos arquiteturais (Speckit)
│   ├── 001-multilingual-entity-classifier/
│   ├── 002-google-news-extractor/
│   └── 003-article-content-extractor/ # Specs da feature de extração multimotor
├── tests/                          # Suíte de testes automatizados
│   ├── test_classifier.py
│   ├── test_extract_google_news.py
│   └── test_extract_article_contents.py # Testes do extrator de conteúdo
├── requirements.txt                # Dependências do projeto
├── pyproject.toml                  # Configurações de ferramentas (pytest, ruff, mypy)
└── README.md                       # Documentação principal

🧪 Testes e Qualidade de Código

O repositório possui cobertura com testes unitários, testes de integração e testes End-to-End (E2E) com requisição de rede ao vivo:

# Executar todos os testes do projeto
pytest -v

# Executar especificamente os testes do Extrator de Notícias
pytest tests/test_extract_google_news.py -v

# Validação e correção automática de formatação com Ruff
ruff check --fix .
ruff format .

# Verificação estática de tipos com Mypy
mypy scripts/ src/

📄 Licença

Este projeto está licenciado sob os termos da licença MIT. Consulte o arquivo LICENSE para mais detalhes.