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
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
# 🧠 TextNLPClassifierApp
|
||||
|
||||
[](https://www.python.org/)
|
||||
[](LICENSE)
|
||||
[](https://github.com/astral-sh/ruff)
|
||||
[](https://mypy-lang.org/)
|
||||
[-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.
|
||||
Reference in New Issue
Block a user