Files
TextNLPClassifierApp/README.md
T
andreferraro 6e3d57619b feat(extractor): add Google News headlines extractor with Foxcape headless and URL resolution
- Add standalone CLI script scripts/extract_google_news.py for Google News RSS scraping
- Integrate foxcape in headless mode as primary stealth anti-bot engine
- Implement parallel article URL resolution using googlenewsdecoder and ThreadPoolExecutor
- Support language and regional locale mapping (-l, --lang, --locale)
- Implement real-time progress logging in stderr and --silent flag
- Add unit, integration, and live E2E tests in tests/test_extract_google_news.py
- Add full SpecKit documentation (specs/002-google-news-extractor/)
- Create comprehensive README.md covering both NLP Classifier and Google News Extractor
2026-08-20 11:50:16 -03:00

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

⚙️ 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'

📁 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
├── 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-nlp-classifier/         # Especificações do classificador
│   └── 002-google-news-extractor/  # Especificações do extrator de notícias
├── tests/                          # Suíte de testes automatizados
│   ├── fixtures/                   # Amostras de ECP, Markdown e XML RSS
│   ├── test_classifier.py          # Testes unitários do classificador
│   └── test_extract_google_news.py # Testes unitários e testes E2E ao vivo
├── 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.