- 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
312 lines
15 KiB
Markdown
312 lines
15 KiB
Markdown
# 🧠 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)
|
|
- [3. Extrator e Parser Multimotor de Artigos](#3--extrator-e-parser-multimotor-de-artigos)
|
|
- [Visão Geral e Tríplice Extração](#visão-geral-e-tríplice-extração)
|
|
- [Argumentos e Flags CLI](#argumentos-e-flags-cli)
|
|
- [Exemplos de Uso](#exemplos-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.
|
|
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
|
|
|
|
```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'
|
|
```
|
|
|
|
---
|
|
|
|
## 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
|
|
```bash
|
|
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)
|
|
```bash
|
|
python scripts/extract_article_contents.py -i out/river_plate.json --limit 2
|
|
```
|
|
|
|
#### 3. Destino Customizado e Timeout Ajustado
|
|
```bash
|
|
python scripts/extract_article_contents.py -i out/petrobras_result.json -o out/petrobras_full.json --timeout 45
|
|
```
|
|
|
|
---
|
|
|
|
## 📁 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
|
|
│ └── 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:
|
|
|
|
```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.
|