npm package: @soblend/scraper
Description: Librería robusta de scrapers para múltiples servicios y casos de uso
Author: Soblend
Latest version: 1.0.0

<div align="center">
  <img src="https://rogddqelmxyuvhpjvxbf.supabase.co/storage/v1/object/public/files/f5rv2r2hmce.png" alt="@soblend/scraper" width="600" />
  
  <h1>@soblend/scraper</h1>
  <p>Librería profesional de web scraping para Node.js con 13 scrapers especializados</p>
  
  [![npm version](https://img.shields.io/npm/v/@soblend/scraper.svg)](https://www.npmjs.com/package/@soblend/scraper)
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
</div>

---

## Descripción

**@soblend/scraper** es una colección completa de 13 scrapers robustos y verificados, diseñados para extraer datos de diferentes fuentes web. Cada scraper está optimizado para su caso de uso específico, incluye manejo robusto de errores, reintentos automáticos y una API consistente.

La librería está construida con arquitectura modular, permitiéndote importar únicamente los scrapers que necesitas, reduciendo el tamaño del bundle y mejorando el rendimiento de tu aplicación.

## Instalación

```bash
npm install @soblend/scraper
```

## Inicio Rápido

```javascript
import { MetaDataFinder, CryptoPriceWatcher } from '@soblend/scraper';

// Extraer metadatos de un sitio web
const metaFinder = new MetaDataFinder();
const metadata = await metaFinder.scrape('https://example.com');
console.log(metadata.metadata.basic.title);

// Obtener precios de criptomonedas
const crypto = new CryptoPriceWatcher();
const top10 = await crypto.getTopCryptos(10);
console.log(top10.cryptos);
```

---

## Scrapers Disponibles

### ImageCollector

Scraper especializado para descargar imágenes de páginas web con capacidad de filtrado por dimensiones y formato.

**Casos de uso:**
- Creación de datasets para entrenamiento de modelos de IA
- Descarga masiva de wallpapers e imágenes
- Archivado de galerías web

**Opciones de configuración:**
```javascript
{
  timeout: 10000,              // Tiempo máximo de espera por request (ms)
  maxImages: 100,              // Número máximo de imágenes a extraer
  minWidth: 0,                 // Ancho mínimo en píxeles
  minHeight: 0,                // Alto mínimo en píxeles
  formats: ['.jpg', '.png'],   // Formatos de imagen aceptados
  userAgent: 'custom-agent',   // User agent personalizado
  retries: 3,                  // Número de reintentos en caso de fallo
  downloadPath: './images'     // Ruta de descarga
}
```

**Métodos:**

- `scrape(url)` - Extrae información de todas las imágenes de la URL
  - Parámetros: `url` (string) - URL de la página a scrapear
  - Retorna: Objeto con `success`, `total`, `images[]`, `sourceUrl`

- `download(images, outputPath)` - Descarga las imágenes al sistema de archivos
  - Parámetros: 
    - `images` (array) - Array de objetos de imagen
    - `outputPath` (string, opcional) - Ruta de destino
  - Retorna: Array de resultados con estado de cada descarga

**Ejemplo:**
```javascript
import { ImageCollector } from '@soblend/scraper';

const collector = new ImageCollector({
  maxImages: 50,
  minWidth: 800,
  minHeight: 600,
  formats: ['.jpg', '.png', '.webp']
});

const result = await collector.scrape('https://example.com/gallery');

if (result.success) {
  console.log(`Encontradas ${result.total} imágenes`);
  const downloads = await collector.download(result.images, './my-images');
  console.log(`Descargadas: ${downloads.filter(d => d.success).length}`);
}
```

---

### MetaDataFinder

Extractor completo de metadatos web incluyendo SEO, Open Graph, Twitter Cards y JSON-LD.

**Casos de uso:**
- Herramientas de análisis SEO
- Generadores de previews sociales
- Auditorías de sitios web
- Extracción de datos estructurados

**Opciones de configuración:**
```javascript
{
  timeout: 10000,
  userAgent: 'Mozilla/5.0...',
  retries: 3
}
```

**Métodos:**

- `scrape(url)` - Extrae todos los metadatos de la URL
  - Retorna: Objeto con `success`, `url`, `metadata`, `scrapedAt`
  - `metadata` incluye: `basic`, `openGraph`, `twitter`, `jsonLd`, `additional`

**Ejemplo:**
```javascript
import { MetaDataFinder } from '@soblend/scraper';

const finder = new MetaDataFinder();
const result = await finder.scrape('https://example.com');

if (result.success) {
  console.log('Título:', result.metadata.basic.title);
  console.log('Descripción:', result.metadata.basic.description);
  console.log('Imagen OG:', result.metadata.openGraph.image);
  console.log('Twitter Card:', result.metadata.twitter.card);
  console.log('JSON-LD:', result.metadata.jsonLd);
}
```

**Estructura de datos retornada:**
- `basic`: title, description, keywords, canonical, favicon, etc.
- `openGraph`: title, type, url, image, description, site_name, etc.
- `twitter`: card, site, creator, title, description, image, etc.
- `jsonLd`: Array de objetos JSON-LD estructurados
- `additional`: lang, charset, viewport, robots, author, generator

---

### FontHunter

Scraper para descubrir y catalogar fuentes tipográficas de múltiples fuentes.

**Fuentes soportadas:**
- Google Fonts
- DaFont
- Análisis de CSS personalizado

**Métodos:**

- `scrapeGoogleFonts(query)` - Busca fuentes en Google Fonts
  - Parámetros: `query` (string, opcional) - Término de búsqueda
  - Retorna: Lista de fuentes con nombre, categoría, URLs

- `scrapeDaFont(category)` - Extrae fuentes de DaFont
  - Parámetros: `category` (string) - Categoría (ej: 'popular', 'new')
  - Retorna: Lista de fuentes con autor y enlaces

- `scrapeFromCSS(url)` - Analiza fuentes usadas en un sitio web
  - Parámetros: `url` (string) - URL del sitio a analizar
  - Retorna: Fuentes encontradas en CSS y @font-face

**Ejemplo:**
```javascript
import { FontHunter } from '@soblend/scraper';

const hunter = new FontHunter();

// Buscar en Google Fonts
const googleFonts = await hunter.scrapeGoogleFonts('Roboto');
console.log(`Encontradas ${googleFonts.total} fuentes`);

// Obtener fuentes populares de DaFont
const dafonts = await hunter.scrapeDaFont('popular');

// Analizar fuentes de un sitio web
const siteFonts = await hunter.scrapeFromCSS('https://example.com');
console.log('Fuentes usadas:', siteFonts.fonts);
```

---

### ProductWatcher

Monitor de precios para sitios de e-commerce con historial de precios integrado.

**Plataformas soportadas:**
- Amazon
- MercadoLibre
- AliExpress

**Métodos:**

- `scrapeAmazon(productUrl)` - Extrae datos de producto Amazon
  - Retorna: Título, precio, moneda, rating, reviews, disponibilidad, imagen

- `scrapeMercadoLibre(productUrl)` - Extrae datos de MercadoLibre
  - Retorna: Título, precio, condición, ventas, imagen

- `scrapeAliExpress(productUrl)` - Extrae datos de AliExpress
  - Retorna: Título, precio, rating, órdenes, imagen

- `getPriceHistory(url)` - Obtiene historial de precios de un producto
  - Retorna: Array con precio, fecha y plataforma

- `getPriceComparison(url)` - Análisis comparativo de precios
  - Retorna: Precio actual, más bajo, más alto, cambio porcentual

**Ejemplo:**
```javascript
import { ProductWatcher } from '@soblend/scraper';

const watcher = new ProductWatcher();

const product = await watcher.scrapeAmazon('https://amazon.com/dp/...');
console.log(`${product.product.title}: $${product.product.price}`);

// Consultar después de un tiempo
const comparison = watcher.getPriceComparison(productUrl);
console.log(`Cambio: ${comparison.changePercent}%`);
console.log(`Precio más bajo histórico: $${comparison.lowest}`);
```

---

### CryptoPriceWatcher

Scraper de cotizaciones de criptomonedas en tiempo real.

**Fuentes de datos:**
- CoinMarketCap
- Binance

**Métodos:**

- `scrapeCoinMarketCap(symbols)` - Obtiene datos de CoinMarketCap
  - Parámetros: `symbols` (array) - Símbolos de criptomonedas (ej: ['BTC', 'ETH'])
  - Retorna: Rank, símbolo, nombre, precio, cambio 24h, volumen, market cap

- `scrapeBinance(pairs)` - Obtiene datos de Binance
  - Parámetros: `pairs` (array) - Pares de trading (ej: ['BTCUSDT', 'ETHUSDT'])
  - Retorna: Símbolo, precio, cambio 24h, volumen

- `getCryptoBySymbol(symbol)` - Busca una criptomoneda específica
  - Parámetros: `symbol` (string) - Símbolo (ej: 'BTC')

- `getTopCryptos(limit)` - Obtiene las top N criptomonedas
  - Parámetros: `limit` (number) - Cantidad (default: 10)

**Ejemplo:**
```javascript
import { CryptoPriceWatcher } from '@soblend/scraper';

const crypto = new CryptoPriceWatcher({ currency: 'USD' });

// Top 10 criptomonedas
const top10 = await crypto.getTopCryptos(10);
top10.cryptos.forEach(coin => {
  console.log(`${coin.symbol}: $${coin.price} (${coin.change24h})`);
});

// Buscar Bitcoin específicamente
const btc = await crypto.getCryptoBySymbol('BTC');
console.log(`Bitcoin: $${btc.crypto.price}`);

// Obtener de Binance
const binance = await crypto.scrapeBinance(['BTCUSDT', 'ETHUSDT']);
```

---

### RedditPostGrabber

Extractor de posts, comentarios y datos de subreddits usando la API JSON pública de Reddit.

**IMPORTANTE:** Reddit puede bloquear requests sin autenticación OAuth. Para uso en producción, se recomienda usar la API oficial de Reddit con credenciales OAuth.

**Métodos:**

- `scrape(subreddit, options)` - Extrae posts de un subreddit
  - Parámetros:
    - `subreddit` (string) - Nombre del subreddit (sin 'r/')
    - `options` (object):
      - `sort`: 'hot', 'new', 'top', 'rising', 'controversial'
      - `limit`: Número de posts (default: 25)
      - `timeframe`: 'hour', 'day', 'week', 'month', 'year', 'all' (para top/controversial)
  - Retorna: Array de posts con título, autor, score, comentarios, URL, etc.

- `getComments(subreddit, postId)` - Obtiene comentarios de un post
  - Retorna: Post info + árbol de comentarios con replies anidados

- `searchSubreddit(subreddit, query, options)` - Busca posts en un subreddit
  - Parámetros: Similar a `scrape()` con parámetro `query` adicional

**Ejemplo:**
```javascript
import { RedditPostGrabber } from '@soblend/scraper';

const reddit = new RedditPostGrabber({ limit: 25 });

// Posts hot de r/javascript
const posts = await reddit.scrape('javascript', { 
  sort: 'hot', 
  limit: 25 
});

posts.posts.forEach(post => {
  console.log(`${post.title} (${post.score} upvotes)`);
  console.log(`${post.numComments} comentarios`);
});

// Obtener comentarios
const comments = await reddit.getComments('javascript', 'abc123');
console.log(`Total comentarios: ${comments.total}`);

// Buscar posts
const search = await reddit.searchSubreddit('javascript', 'react hooks');
```

---

### SocialStats

Extractor de estadísticas públicas de redes sociales.

**Plataformas soportadas:**
- Instagram (limitado sin autenticación)
- TikTok (limitado sin autenticación)
- Twitter/X (limitado)
- YouTube

**Métodos:**

- `scrapeInstagram(username)` - Estadísticas de perfil Instagram
  - Retorna: Seguidores, siguiendo, posts, bio, verificación

- `scrapeTikTok(username)` - Estadísticas de perfil TikTok
  - Retorna: Seguidores, siguiendo, likes, videos, bio

- `scrapeTwitter(username)` - Estadísticas de perfil Twitter/X
  - Retorna: Información básica (limitada sin API oficial)

- `scrapeYouTube(channelId)` - Estadísticas de canal YouTube
  - Retorna: Suscriptores, vistas, videos, nombre, descripción

**Ejemplo:**
```javascript
import { SocialStats } from '@soblend/scraper';

const social = new SocialStats();

const instagram = await social.scrapeInstagram('username');
if (instagram.success) {
  console.log(`Seguidores: ${instagram.stats.followers}`);
  console.log(`Posts: ${instagram.stats.posts}`);
}

const youtube = await social.scrapeYouTube('UCxxxxxxxx');
console.log(`Suscriptores: ${youtube.stats.subscribers}`);
```

**Limitaciones:** Muchas redes sociales requieren autenticación para acceder a estadísticas completas. Los datos disponibles sin autenticación son limitados.

---

### GitHubTrends

Scraper para descubrir proyectos trending de GitHub y buscar repositorios.

**Métodos:**

- `scrapeTrending(language, timeframe)` - Repositorios trending
  - Parámetros:
    - `language` (string) - Lenguaje de programación (ej: 'javascript', 'python', '' para todos)
    - `timeframe` (string) - 'daily', 'weekly', 'monthly'
  - Retorna: Lista de repos con owner, nombre, estrellas, descripción, lenguaje, stars de hoy

- `scrapeRepository(owner, repo)` - Detalles de un repositorio
  - Retorna: Descripción, estrellas, forks, watchers, topics, lenguaje, licencia

- `searchRepositories(query, options)` - Buscar repositorios
  - Parámetros:
    - `query` (string) - Término de búsqueda
    - `options` (object): `sort` ('stars', 'forks'), `order` ('desc', 'asc'), `language`

**Ejemplo:**
```javascript
import { GitHubTrends } from '@soblend/scraper';

const github = new GitHubTrends();

// Trending diario de JavaScript
const trending = await github.scrapeTrending('javascript', 'daily');
trending.repositories.forEach(repo => {
  console.log(`${repo.fullName}: ${repo.stars} ⭐ (+${repo.todayStars} hoy)`);
});

// Detalles de un repo específico
const repo = await github.scrapeRepository('facebook', 'react');
console.log(repo.repository.description);

// Buscar repos de machine learning
const search = await github.searchRepositories('machine learning', {
  sort: 'stars',
  order: 'desc'
});
```

---

### JobScraper

Agregador de ofertas de trabajo de múltiples plataformas.

**Plataformas soportadas:**
- Indeed
- LinkedIn (limitado)
- Computrabajo
- RemoteOK

**Métodos:**

- `scrapeIndeed(query, location, options)` - Buscar en Indeed
  - Parámetros:
    - `query` (string) - Término de búsqueda
    - `location` (string) - Ubicación
    - `options.limit` (number) - Máximo de resultados
  - Retorna: Título, empresa, ubicación, salario, resumen, fecha, URL

- `scrapeLinkedIn(query, location)` - Buscar en LinkedIn (limitado)

- `scrapeComputrabajo(query, location)` - Buscar en Computrabajo
  - `location` puede ser: 'mexico', 'argentina', 'colombia', etc.

- `scrapeRemoteOK(query)` - Buscar trabajos remotos en RemoteOK

**Ejemplo:**
```javascript
import { JobScraper } from '@soblend/scraper';

const jobs = new JobScraper();

// Buscar en Indeed
const indeed = await jobs.scrapeIndeed('javascript developer', 'New York', {
  limit: 20
});

indeed.jobs.forEach(job => {
  console.log(`${job.title} - ${job.company}`);
  console.log(`${job.location} | ${job.salary}`);
});

// Trabajos remotos
const remote = await jobs.scrapeRemoteOK('developer');
console.log(`${remote.total} trabajos remotos encontrados`);
```

---

### AnimeFinder

Scraper especializado para MyAnimeList con búsqueda y exploración de anime.

**Métodos:**

- `scrapeMyAnimeList(query)` - Buscar anime
  - Parámetros: `query` (string) - Término de búsqueda
  - Retorna: Título, tipo, episodios, score, sinopsis, imagen, URL

- `scrapeAnimeDetails(malId)` - Detalles completos de un anime
  - Parámetros: `malId` (number) - ID de MyAnimeList
  - Retorna: Info completa incluyendo géneros, estudios, estado, fechas

- `scrapeTopAnime(type, limit)` - Top anime
  - Parámetros:
    - `type` (string) - 'anime', 'airing', 'upcoming', 'tv', 'movie', etc.
    - `limit` (number) - Cantidad (default: 20)

- `scrapeSeasonal(year, season)` - Anime de la temporada
  - Parámetros:
    - `year` (number) - Año (ej: 2025)
    - `season` (string) - 'winter', 'spring', 'summer', 'fall'

**Ejemplo:**
```javascript
import { AnimeFinder } from '@soblend/scraper';

const anime = new AnimeFinder();

// Buscar anime
const search = await anime.scrapeMyAnimeList('one piece');
console.log(`Encontrados ${search.total} resultados`);

// Top anime
const top = await anime.scrapeTopAnime('anime', 20);
top.animes.forEach(a => {
  console.log(`${a.rank}. ${a.title} - Score: ${a.score}`);
});

// Anime de temporada actual
const seasonal = await anime.scrapeSeasonal(2025, 'winter');
console.log(`${seasonal.total} animes esta temporada`);
```

---

### EventScraper

Descubridor de eventos de múltiples plataformas.

**Plataformas soportadas:**
- Eventbrite
- Meetup (limitado)
- Ticketmaster

**Métodos:**

- `scrapeEventbrite(query, location)` - Buscar eventos en Eventbrite
  - Retorna: Título, fecha, ubicación, precio, imagen, URL

- `scrapeMeetup(topic, location)` - Buscar meetups (requiere autenticación para datos completos)

- `scrapeTicketmaster(query, location)` - Buscar eventos en Ticketmaster
  - Retorna: Título, venue, fecha, precio, imagen

- `scrapeLocalEvents(city, country)` - Agrega eventos de múltiples fuentes

**Ejemplo:**
```javascript
import { EventScraper } from '@soblend/scraper';

const events = new EventScraper();

// Eventos tech en San Francisco
const eventbrite = await events.scrapeEventbrite('tech', 'San Francisco');
eventbrite.events.forEach(event => {
  console.log(`${event.title} - ${event.date}`);
  console.log(`${event.location} | ${event.price}`);
});

// Todos los eventos locales
const local = await events.scrapeLocalEvents('Austin', 'US');
console.log(`${local.total} eventos encontrados`);
```

---

### WeatherScraper

Scraper de datos meteorológicos de múltiples fuentes.

**Fuentes soportadas:**
- Weather.com
- wttr.in (API JSON)
- Open-Meteo (API pública)

**Métodos:**

- `scrapeWeatherCom(location)` - Datos de Weather.com
  - Retorna: Temperatura, condición, sensación térmica, humedad, viento, pronóstico

- `scrapeWttr(location)` - Datos de wttr.in (recomendado)
  - Parámetros: `location` (string) - Ciudad o coordenadas
  - Retorna: Clima actual completo + pronóstico de 3 días + info de ubicación

- `scrapeOpenMeteo(latitude, longitude)` - Datos de Open-Meteo API
  - Parámetros: Coordenadas geográficas
  - Retorna: Datos meteorológicos precisos por coordenadas

- `getWeather(location, source)` - Wrapper genérico
  - Parámetros:
    - `location` (string) - Ubicación
    - `source` (string) - 'wttr' o 'weather.com'

**Ejemplo:**
```javascript
import { WeatherScraper } from '@soblend/scraper';

const weather = new WeatherScraper();

// Usar wttr.in (recomendado)
const wttr = await weather.scrapeWttr('London');
console.log(`${wttr.location.name}, ${wttr.location.country}`);
console.log(`Temperatura: ${wttr.current.temperature}`);
console.log(`Condición: ${wttr.current.condition}`);
console.log(`Humedad: ${wttr.current.humidity}`);

// Pronóstico
wttr.forecast.forEach(day => {
  console.log(`${day.date}: ${day.high} / ${day.low}`);
});

// Por coordenadas
const coords = await weather.scrapeOpenMeteo(51.5074, -0.1278);
console.log(coords.current);
```

---

### ProxyFinder

Scraper de listas de proxies gratuitos con validación opcional.

**Fuentes:**
- free-proxy-list.net
- proxyscrape.com
- proxynova.com

**Métodos:**

- `scrapeFreeProxyList()` - Scrape de free-proxy-list.net
  - Retorna: IP, puerto, país, anonimato, protocolo (HTTP/HTTPS)

- `scrapeProxyScrape()` - Scrape de proxyscrape.com
  - Retorna: Lista de proxies en formato IP:puerto

- `scrapeProxyNova()` - Scrape de proxynova.com

- `getAllProxies()` - Agrega proxies de todas las fuentes
  - Retorna: Lista unificada sin duplicados

- `validateProxy(proxy)` - Valida si un proxy funciona
  - Retorna: Proxy con campo `working` (true/false) y `responseTime`

- `validateProxies(proxies, concurrency)` - Valida múltiples proxies
  - Parámetros:
    - `proxies` (array) - Array de proxies a validar
    - `concurrency` (number) - Validaciones simultáneas (default: 5)
  - Retorna: Estadísticas + lista de proxies con estado

**Ejemplo:**
```javascript
import { ProxyFinder } from '@soblend/scraper';

const finder = new ProxyFinder({ 
  validateProxies: false,
  validationTimeout: 5000 
});

// Obtener todos los proxies
const all = await finder.getAllProxies();
console.log(`Encontrados ${all.total} proxies únicos`);

all.proxies.slice(0, 10).forEach(proxy => {
  console.log(`${proxy.ip}:${proxy.port} (${proxy.country}) - ${proxy.protocol}`);
});

// Validar proxies
const validated = await finder.validateProxies(all.proxies.slice(0, 20), 5);
console.log(`${validated.working} de ${validated.total} funcionando`);

validated.workingProxies.forEach(proxy => {
  console.log(`${proxy.ip}:${proxy.port} - ${proxy.responseTime}ms`);
});
```

---

## Configuración Global

Todos los scrapers aceptan un objeto de opciones en su constructor para personalizar el comportamiento:

```javascript
const scraper = new AnyScraper({
  timeout: 15000,              // Tiempo máximo de espera en milisegundos (default: 10000)
  userAgent: 'CustomAgent/1.0', // User agent personalizado
  retries: 3                   // Número de reintentos ante fallos (default: 3)
});
```

### Opciones Específicas por Scraper

Algunos scrapers tienen opciones adicionales específicas:

**ImageCollector:**
- `maxImages`: Límite de imágenes a extraer
- `minWidth`, `minHeight`: Dimensiones mínimas
- `formats`: Array de extensiones permitidas
- `downloadPath`: Ruta de descarga

**ProductWatcher:**
- `priceHistory`: Array para almacenar historial de precios

**CryptoPriceWatcher:**
- `currency`: Moneda de referencia (default: 'USD')

**RedditPostGrabber:**
- `limit`: Número de posts por request (default: 25)
- `sort`: Ordenamiento por defecto ('hot', 'new', 'top', etc.)

**ProxyFinder:**
- `validateProxies`: Validar automáticamente (default: false)
- `validationTimeout`: Timeout para validación (default: 5000ms)

---

## Manejo de Errores

Todos los scrapers retornan un objeto de respuesta estandarizado:

```javascript
{
  success: boolean,           // Indica si la operación fue exitosa
  data: object | array,       // Datos extraídos (varía por scraper)
  error: string,              // Mensaje de error si success es false
  scrapedAt: string          // Timestamp ISO 8601 de la extracción
}
```

**Ejemplo de manejo de errores:**

```javascript
const result = await scraper.scrape(url);

if (result.success) {
  // Procesar datos
  console.log(result.data);
} else {
  // Manejar error
  console.error('Error:', result.error);
}
```

---

## Características Técnicas

### Arquitectura

- **Modular**: Cada scraper es una clase independiente importable individualmente
- **Tree-shaking**: Optimizado para bundlers modernos (webpack, rollup, vite)
- **Zero-config**: Funciona out-of-the-box con configuración sensible por defecto
- **Extensible**: Fácil de extender mediante herencia de clases

### Dependencias

La librería tiene dependencias mínimas y ligeras:

- **axios** (^1.6.2): Cliente HTTP robusto con soporte de timeouts e interceptores
- **cheerio** (^1.0.0-rc.12): Parser HTML ultrarrápido con API tipo jQuery
- **puppeteer-core** (^21.6.1): Opcional, para scraping dinámico futuro
- **chromium** (^3.0.3): Opcional, binario de navegador para puppeteer

### Rendimiento

- Requests HTTP asíncronos con axios
- Parser HTML en memoria sin overhead de navegador
- Sistema de reintentos con backoff exponencial
- Timeouts configurables para prevenir bloqueos

---

## Limitaciones y Consideraciones

### Rate Limiting

La mayoría de sitios web implementan límites de peticiones. Recomendaciones:

- Implementa delays entre requests consecutivos
- Respeta los archivos `robots.txt`
- Usa proxies rotativos para volumen alto (ver ProxyFinder)
- Considera APIs oficiales para uso en producción

### Autenticación

Algunos scrapers tienen limitaciones sin autenticación:

| Servicio | Limitación | Solución |
|----------|------------|----------|
| Reddit | Bloqueo 403 sin OAuth | Usar API oficial con credenciales |
| Instagram | Datos limitados | Requiere login para estadísticas completas |
| TikTok | Acceso restringido | API oficial requerida |
| LinkedIn | Scraping bloqueado | Usar LinkedIn API oficial |
| Twitter/X | Muy limitado | API oficial con autenticación |

### Cambios en Sitios Web

Los scrapers basados en HTML pueden fallar si los sitios cambian su estructura. Monitoreamos y actualizamos regularmente, pero:

- Implementa manejo de errores robusto
- Ten planes de contingencia (APIs oficiales)
- Reporta problemas en el repositorio

### Consideraciones Legales

- Verifica los Términos de Servicio de cada sitio antes de scrapear
- Respeta la privacidad y datos personales (GDPR, CCPA)
- No sobrecargues servidores con requests excesivos
- Atribuye fuentes apropiadamente cuando uses los datos

---

## Casos de Uso Reales

### Dashboard de Criptomonedas

```javascript
import { CryptoPriceWatcher } from '@soblend/scraper';

const crypto = new CryptoPriceWatcher();
const updateDashboard = async () => {
  const top10 = await crypto.getTopCryptos(10);
  // Actualizar UI con top10.cryptos
};

setInterval(updateDashboard, 60000); // Actualizar cada minuto
```

### Agregador de Empleos

```javascript
import { JobScraper } from '@soblend/scraper';

const jobs = new JobScraper();
const allJobs = [];

const indeed = await jobs.scrapeIndeed('developer', 'Remote');
const remote = await jobs.scrapeRemoteOK('developer');

allJobs.push(...indeed.jobs, ...remote.jobs);
// Almacenar en base de datos
```

### Monitor de Precios con Alertas

```javascript
import { ProductWatcher } from '@soblend/scraper';

const watcher = new ProductWatcher();
const checkPrice = async (productUrl, targetPrice) => {
  const result = await watcher.scrapeAmazon(productUrl);
  
  if (result.success && result.product.price <= targetPrice) {
    // Enviar alerta (email, SMS, etc.)
    sendAlert(`Precio bajo: $${result.product.price}`);
  }
};

setInterval(() => checkPrice(url, 50), 3600000); // Cada hora
```

---

## Testing

La librería incluye ejemplos y tests de demostración:

```bash
# Ejecutar demo completo
npm start

# Ejecutar tests básicos
npm test
```

---

## Contribuciones

Las contribuciones son bienvenidas. Para contribuir:

1. Fork el repositorio
2. Crea una branch para tu feature (`git checkout -b feature/nuevo-scraper`)
3. Commit tus cambios (`git commit -m 'Add: NuevoScraper'`)
4. Push a la branch (`git push origin feature/nuevo-scraper`)
5. Abre un Pull Request

### Reportar Issues

Si encuentras un bug o tienes una sugerencia:
- Describe el problema claramente
- Incluye pasos para reproducirlo
- Indica versión de Node.js y sistema operativo
- Proporciona logs o screenshots si es posible

---

## Roadmap

Características planeadas para futuras versiones:

- [ ] Soporte TypeScript con tipos completos
- [ ] Scrapers adicionales (Spotify, Steam, Twitch)
- [ ] Sistema de caché configurable
- [ ] Rate limiting automático inteligente
- [ ] Middleware de transformación de datos
- [ ] Integración con proxies comerciales
- [ ] CLI para uso desde terminal
- [ ] Webhooks para notificaciones

---

## Licencia

MIT © Soblend

---

## Soporte

Para preguntas, problemas o sugerencias:

- Abre un issue en el repositorio
- Consulta la documentación completa
- Revisa los ejemplos en `/examples`

---

**Desarrollado para la comunidad de desarrolladores**