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

262 lines
12 KiB
Markdown

# 🧠 TextNLPClassifierApp
[![Python Version](https://img.shields.io/badge/Python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue.svg)](https://www.python.org/)
[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Code Style](https://img.shields.io/badge/Code%20Style-Ruff-black.svg)](https://github.com/astral-sh/ruff)
[![Type Checked](https://img.shields.io/badge/Type%20Check-Mypy-blue.svg)](https://mypy-lang.org/)
[![Tests](https://img.shields.io/badge/Tests-Pytest%20(100%25%20Passing)-brightgreen.svg)](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](#-visão-geral)
- [Instalação e Setup](#-instalação-e-setup)
- [1. Classificador de Conteúdo e Inerência (NLP / LLM / ECP)](#1--classificador-de-conteúdo-e-inerência-nlp--llm--ecp)
- [O que é e Como Funciona](#o-que-é-e-como-funciona)
- [Arquitetura de Classificação em 3 Tiers](#arquitetura-de-classificação-em-3-tiers)
- [Categorias de Decisão](#categorias-de-decisão)
- [Formato do ECP Snapshot e Markdown](#formato-do-ecp-snapshot-e-markdown)
- [Exemplos de Uso CLI](#exemplos-de-uso-cli)
- [2. Extrator de Manchetes do Google News](#2--extrator-de-manchetes-do-google-news)
- [O que é e Como Funciona](#o-que-é-e-como-funciona-1)
- [Diferenciais Técnicos](#diferenciais-técnicos)
- [Argumentos e Flags de Linha de Comando](#argumentos-e-flags-de-linha-de-comando)
- [Exemplos Práticos de Uso](#exemplos-práticos-de-uso)
- [Estrutura do Projeto](#-estrutura-do-projeto)
- [Testes e Qualidade de Código](#-testes-e-qualidade-de-código)
- [Licença](#-licença)
---
## 🌟 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
```bash
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
```bash
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:
```bash
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
```mermaid
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`):
```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
```bash
# 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`](file:///c:/Users/aferr/Projects/AFTech/DunaMedia/TextNLPClassifierApp/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)
```bash
python scripts/extract_google_news.py -q "River Plate" -l es --locale AR -p 2 -o out/river_plate.json
```
* **Saída no Terminal**:
```text
[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)
```bash
python scripts/extract_google_news.py --query "Cruzeiro" --lang pt --locale BR --pretty
```
#### 3. Fórmula 1 (Inglaterra / Inglês)
```bash
python scripts/extract_google_news.py --query "Formula 1" --lang en --locale GB --pretty
```
#### 4. Filtragem com `jq` em Modo Silencioso
```bash
python scripts/extract_google_news.py -q "inteligência artificial" -s | jq '.items[].url'
```
---
## 📁 Estrutura do Projeto
```text
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:
```bash
# 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.