En esta guía de automatización completa aprenderás:
- Cómo construir un sistema 100% automatizado de generación de leads B2B
- Arquitectura completa: scraping → cualificación → enriquecimiento → outreach → seguimiento
- Integración de maps scraper con CRM, email automation y herramientas complementarias
- Workflows automatizados que generan 500+ leads cualificados semanalmente sin tocar nada
- Casos reales de sistemas que generan €50k-200k/año en autopilot
La Visión: Tu Máquina de Leads Funcionando Mientras Duermes
4.8★ Rating - 2,000+ Empresas Usan MapiLeads
Join agencias, SaaS, consultorías que generan 10x más leads con MapiLeads.
Leer testimonios 🚀Imagina despertarte cada mañana con esto en tu bandeja de entrada:
Reporte Diario Automatizado - 23 Enero 2025
- ✅ 247 nuevos leads extraídos de Google Maps
- ✅ 64 clasificados como "Hot Leads" (score >80)
- ✅ 64 perfiles enriquecidos con email + LinkedIn
- ✅ 64 emails personalizados enviados
- ✅ 18 emails abiertos (28% open rate)
- ✅ 4 respuestas positivas
- ✅ 2 reuniones auto-agendadas en tu calendario
Todo esto mientras dormías.
Esto no es ciencia ficción. Es lo que un maps scraper completamente automatizado puede hacer por ti. Y en esta guía te enseño exactamente cómo construirlo.
Si eres nuevo en el scraping de Google Maps, te recomiendo empezar con nuestra guía completa de extracción de leads con Google Maps scraper antes de automatizar.
Anatomía del Sistema: Los 8 Componentes de Automatización Total
Un sistema de generación de leads verdaderamente automatizado tiene 8 componentes trabajando en sincronía:
┌──────────────────────────────────────────────────────────────┐ │ SISTEMA AUTOMATIZADO │ │ │ │ 1. SEARCH → Genera búsquedas automáticamente │ │ GENERATION (ICP → 500 búsquedas únicas) │ │ │ │ │ ▼ │ │ 2. SCRAPING → Maps scraper extrae 24/7 │ │ ENGINE (500 leads/día en autopilot) │ │ │ │ │ ▼ │ │ 3. DATA → Limpia, normaliza, deduplica │ │ PROCESSING (datos consistentes, zero duplicados) │ │ │ │ │ ▼ │ │ 4. SCORING & → Algoritmo clasifica Hot/Warm/Cold │ │ FILTERING (solo Hot Leads pasan a siguiente) │ │ │ │ │ ▼ │ │ 5. ENRICHMENT → Añade email, LinkedIn, tech stack │ │ LAYER (APIs externas automáticas) │ │ │ │ │ ▼ │ │ 6. PERSONALIZATION → Genera emails customizados por lead │ │ ENGINE (plantillas + variables dinámicas) │ │ │ │ │ ▼ │ │ 7. OUTREACH → Envía secuencias multicanal │ │ AUTOMATION (email + SMS + LinkedIn) │ │ │ │ │ ▼ │ │ 8. RESPONSE → Detecta respuestas, agenda reuniones │ │ HANDLING (Calendly auto-booking) │ │ │ └──────────────────────────────────────────────────────────────┘
Vamos a construir cada componente paso a paso.
Componente 1: Search Generation Automatizado
60 Leads Gratis para Empezar
Sin tarjeta. Sin compromiso. Prueba la calidad de datos MapiLeads ahora mismo.
Empezar gratis 🚀El primer componente genera automáticamente todas las búsquedas que tu maps scraper debe ejecutar.
De Manual a Automatizado
| Proceso Manual | Proceso Automatizado | 
|---|---|
| Piensas cada búsqueda manualmente | Algoritmo genera búsquedas desde ICP definido | 
| Escribes "restaurante italiano Madrid" | Sistema genera 500 variaciones automáticamente | 
| Te pierdes búsquedas obvias | Cobertura completa, zero gaps | 
| Tiempo: 1-2 horas/semana | Tiempo: 0 min (corre solo) | 
Implementación: Script de Generación de Búsquedas
Ejemplo en Python (adaptable a cualquier lenguaje):
import pandas as pd
import itertools
# Define tu ICP
categories = [
    "restaurante italiano",
    "pizzería",
    "trattoria italiana",
    "osteria"
]
cities = [
    "Madrid",
    "Barcelona",
    "Valencia",
    "Sevilla",
    "Bilbao"
]
neighborhoods = [
    "Centro",
    "Norte",
    "Sur",
    "Este",
    "Oeste"
]
qualifiers = [
    "",  # búsqueda base
    "nuevo",
    "inaugurado 2024",
    "mejor",
    "recomendado"
]
# Genera todas las combinaciones
searches = []
for cat, city, neighborhood, qualifier in itertools.product(
    categories, cities, neighborhoods, qualifiers
):
    if qualifier:
        search = f"{qualifier} {cat} {neighborhood} {city}"
    else:
        search = f"{cat} {neighborhood} {city}"
    searches.append({
        'search_query': search,
        'category': cat,
        'city': city,
        'neighborhood': neighborhood,
        'qualifier': qualifier,
        'status': 'pending'
    })
# Output: 500 búsquedas (4 × 5 × 5 × 5)
df = pd.DataFrame(searches)
df.to_csv('searches_queue.csv', index=False)
print(f"Generated {len(searches)} unique searches")
# Output: Generated 500 unique searches
Automatización nivel 2: Búsquedas adaptativas
El script anterior es estático. Para automatización completa, añade lógica adaptativa:
import datetime
# Genera búsquedas basadas en fecha actual
today = datetime.date.today()
current_month = today.strftime("%B")  # "January", "February"...
seasonal_qualifiers = {
    "December": ["navidad", "fin de año", "cena navideña"],
    "January": ["nuevo", "inaugurado 2025"],
    "June": ["terraza verano", "con terraza"],
    # ... etc
}
# Añade qualifiers estacionales
if current_month in seasonal_qualifiers:
    qualifiers.extend(seasonal_qualifiers[current_month])
# Resultado: búsquedas automáticamente adaptadas al mes actual
Automatización nivel 3: Búsquedas basadas en rendimiento
# Analiza qué búsquedas dieron mejores leads históricamente
historical_data = pd.read_csv('historical_leads.csv')
# Identifica búsquedas con mejor conversion rate
best_searches = historical_data.groupby('source_search').agg({
    'lead_id': 'count',
    'converted': 'sum'
}).reset_index()
best_searches['conversion_rate'] = (
    best_searches['converted'] / best_searches['lead_id']
)
# Genera más variaciones de las búsquedas que mejor convirtieron
top_searches = best_searches.nlargest(20, 'conversion_rate')
for search in top_searches['source_search']:
    # Genera 10 variaciones de cada top search
    variations = generate_search_variations(search, count=10)
    searches.extend(variations)
# Resultado: sistema aprende qué búsquedas funcionan mejor y las replica
Para entender más sobre cómo funciona Google Maps scraper internamente, lee nuestra guía técnica.
Componente 2: Scraping Engine 24/7
Con búsquedas generadas, necesitas un scraper que las ejecute automáticamente sin supervisión.
Opción A: Automatización con MapiLeads (No-Code)
MapiLeads puede automatizarse usando su API o integraciones:
Método 1: Zapier/Make Integration
- Google Sheet con búsquedas pendientes
- Zapier detecta nuevas filas
- Trigger abre Google Maps con búsqueda
- MapiLeads auto-extrae (modo scheduled)
- Resultados se envían a Google Sheets/CRM
Setup time: 2 horas | Complejidad: 3/10
Método 2: Browser Automation con Selenium
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import pandas as pd
# Carga búsquedas pendientes
searches = pd.read_csv('searches_queue.csv')
pending = searches[searches['status'] == 'pending']
driver = webdriver.Chrome()
driver.get('https://www.google.com/maps')
time.sleep(3)
for idx, row in pending.iterrows():
    search_query = row['search_query']
    # Busca en Google Maps
    search_box = driver.find_element('id', 'searchboxinput')
    search_box.clear()
    search_box.send_keys(search_query)
    search_box.send_keys(Keys.RETURN)
    time.sleep(5)
    # MapiLeads auto-detecta resultados y extrae
    # (asumiendo MapiLeads configurado en "auto-extract mode")
    time.sleep(30)  # tiempo para extracción
    # Marca búsqueda como completada
    searches.loc[idx, 'status'] = 'completed'
    searches.to_csv('searches_queue.csv', index=False)
    # Rate limiting
    time.sleep(60 + random.randint(0, 60))
driver.quit()
Setup time: 4 horas | Complejidad: 5/10
Opción B: Scraping Completamente Custom (Full Control)
Para volumen alto (>10k leads/día), construye scraper custom con Puppeteer:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
const fs = require('fs');
const csv = require('csv-parser');
puppeteer.use(StealthPlugin());
async function scrapeGoogleMaps(searchQuery) {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();
  await page.goto('https://www.google.com/maps');
  await page.waitForSelector('#searchboxinput');
  // Busca
  await page.type('#searchboxinput', searchQuery);
  await page.keyboard.press('Enter');
  await page.waitForTimeout(5000);
  // Scroll para cargar todos los resultados
  const scrollContainer = await page.$('[role="feed"]');
  for (let i = 0; i < 5; i++) {
    await page.evaluate(el => {
      el.scrollTop = el.scrollHeight;
    }, scrollContainer);
    await page.waitForTimeout(2000);
  }
  // Extrae datos
  const results = await page.evaluate(() => {
    const items = document.querySelectorAll('[role="article"]');
    return Array.from(items).map(item => {
      const name = item.querySelector('.fontHeadlineSmall')?.textContent;
      const rating = item.querySelector('.MW4etd')?.textContent;
      const address = item.querySelector('[class*="W4Efsd"]:nth-of-type(2)')?.textContent;
      return { name, rating, address };
    });
  });
  await browser.close();
  return results;
}
// Worker principal que corre 24/7
async function mainWorker() {
  const searches = [];
  // Carga searches pendientes
  fs.createReadStream('searches_queue.csv')
    .pipe(csv())
    .on('data', (row) => {
      if (row.status === 'pending') searches.push(row);
    })
    .on('end', async () => {
      console.log(`${searches.length} searches pending`);
      for (const search of searches) {
        console.log(`Scraping: ${search.search_query}`);
        const leads = await scrapeGoogleMaps(search.search_query);
        // Guarda resultados
        fs.appendFileSync(
          'leads_raw.json',
          JSON.stringify({ search: search.search_query, leads, timestamp: Date.now() }) + '\n'
        );
        // Rate limiting
        const delay = 60000 + Math.random() * 60000; // 1-2 min
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    });
}
// Corre worker indefinidamente
while (true) {
  await mainWorker();
  console.log('Batch completed. Waiting 1 hour before next batch...');
  await new Promise(resolve => setTimeout(resolve, 3600000)); // 1 hora
}
Deploy en servidor:
# En servidor Ubuntu/Debian
npm install pm2 -g
pm2 start scraper.js --name "maps-scraper"
pm2 save
pm2 startup
# Scraper ahora corre 24/7, se reinicia automáticamente si crashea
Componente 3: Data Processing Pipeline
Extracción + Verificación + Enriquecimiento
Todo-en-uno: emails, teléfonos, redes sociales, reviews. CSV listo para outreach.
Ver características 🚀Los datos raw del scraper necesitan limpieza antes de usar:
Pipeline Automatizado de Procesamiento
import pandas as pd
import re
import phonenumbers
def normalize_phone(phone, country='ES'):
    """Normaliza teléfonos a formato internacional"""
    try:
        parsed = phonenumbers.parse(phone, country)
        return phonenumbers.format_number(
            parsed,
            phonenumbers.PhoneNumberFormat.E164
        )
    except:
        return None
def normalize_url(url):
    """Normaliza URLs"""
    if not url:
        return None
    url = url.strip()
    if not url.startswith('http'):
        url = 'https://' + url
    return url.rstrip('/')
def clean_name(name):
    """Limpia nombres de negocios"""
    # Remove emojis
    name = re.sub(r'[^\w\s\-&\']', '', name)
    # Capitalize properly
    return name.strip().title()
def process_raw_leads(input_file, output_file):
    """Procesa leads raw y los normaliza"""
    df = pd.read_json(input_file, lines=True)
    # Flatten nested structure
    leads = []
    for _, row in df.iterrows():
        for lead in row['leads']:
            lead['source_search'] = row['search']
            lead['scraped_at'] = row['timestamp']
            leads.append(lead)
    df_leads = pd.DataFrame(leads)
    # Normalizaciones
    df_leads['name'] = df_leads['name'].apply(clean_name)
    df_leads['phone'] = df_leads['phone'].apply(normalize_phone)
    df_leads['website'] = df_leads['website'].apply(normalize_url)
    # Convert rating to float
    df_leads['rating'] = pd.to_numeric(
        df_leads['rating'].str.replace(',', '.'),
        errors='coerce'
    )
    # Remove rows with no contact info
    df_leads = df_leads[
        df_leads['phone'].notna() | df_leads['website'].notna()
    ]
    # Deduplication (por teléfono)
    df_leads = df_leads.drop_duplicates(subset=['phone'], keep='first')
    # Deduplication (por nombre + ciudad si no hay phone)
    df_leads = df_leads.drop_duplicates(
        subset=['name', 'city'],
        keep='first'
    )
    df_leads.to_csv(output_file, index=False)
    return len(df_leads)
# Automatiza: corre cada hora
import schedule
def job():
    count = process_raw_leads('leads_raw.json', 'leads_processed.csv')
    print(f"Processed {count} leads")
schedule.every(1).hours.do(job)
while True:
    schedule.run_pending()
    time.sleep(60)
Componente 4: Scoring & Filtering Automatizado
Algoritmo de scoring que clasifica leads automáticamente:
def calculate_lead_score(lead):
    """Calcula score de 0-100 basado en criterios"""
    score = 0
    # Rating (max 20 pts)
    if lead['rating']:
        if 4.0 <= lead['rating'] <= 4.3:
            score += 20  # Sweet spot
        elif 3.8 <= lead['rating'] < 4.0:
            score += 15
        elif lead['rating'] >= 4.3:
            score += 10
    # Reviews count (max 20 pts)
    reviews = lead.get('reviews_count', 0)
    if 50 <= reviews <= 300:
        score += 20  # Perfect range
    elif 30 <= reviews < 50:
        score += 15
    elif reviews > 300:
        score += 10
    # Tiene website (15 pts)
    if lead.get('website'):
        score += 15
        # Bonus si website es profesional
        if not any(x in lead['website'] for x in ['wix.com', 'wordpress.com']):
            score += 5
    # Responde a reseñas (15 pts)
    if lead.get('responds_to_reviews', False):
        score += 15
    # Tiene fotos (10 pts)
    photos_count = lead.get('photos_count', 0)
    if photos_count > 50:
        score += 10
    elif photos_count > 20:
        score += 5
    # Negocio reciente (15 pts)
    if lead.get('opened_recently', False):
        score += 15
    return min(score, 100)
def classify_lead(score):
    """Clasifica en Hot/Warm/Cold"""
    if score >= 75:
        return 'hot'
    elif score >= 50:
        return 'warm'
    else:
        return 'cold'
# Aplica scoring a todos los leads
df = pd.read_csv('leads_processed.csv')
df['score'] = df.apply(calculate_lead_score, axis=1)
df['classification'] = df['score'].apply(classify_lead)
# Filtra solo Hot y Warm para siguientes etapas
df_qualified = df[df['classification'].isin(['hot', 'warm'])]
df_qualified.to_csv('leads_qualified.csv', index=False)
print(f"Qualified {len(df_qualified)} leads ({len(df_qualified)/len(df)*100:.1f}%)")
Una vez cualificados tus leads, aprende las estrategias completas de conversión y cierre para maximizar tus resultados.
Componente 5: Enrichment Automatizado
Hasta 80,000 Leads/Mes Verificados
Planes escalables desde €4.99/mes. Sin límites ocultos. Cancela cuando quieras.
Ver planes 🚀Enriquece leads con emails y datos adicionales automáticamente:
import requests
from time import sleep
def find_email_hunter(domain, api_key):
    """Encuentra email usando Hunter.io API"""
    url = f"https://api.hunter.io/v2/domain-search"
    params = {
        'domain': domain,
        'api_key': api_key
    }
    response = requests.get(url, params=params)
    if response.status_code == 200:
        data = response.json()
        emails = data.get('data', {}).get('emails', [])
        # Prioriza: owner > manager > generic
        for email in emails:
            if email['type'] in ['personal']:
                return email['value']
        # Si no hay personal, usa primer email
        if emails:
            return emails[0]['value']
    return None
def enrich_lead(lead, hunter_api_key):
    """Enriquece un lead con email"""
    if not lead.get('website'):
        return lead
    # Extrae domain de website
    domain = lead['website'].replace('https://', '').replace('http://', '').split('/')[0]
    # Busca email
    email = find_email_hunter(domain, hunter_api_key)
    lead['email'] = email
    lead['enriched'] = True
    sleep(1)  # Rate limiting Hunter.io
    return lead
# Proceso automatizado de enrichment
HUNTER_API_KEY = 'tu_api_key'
df = pd.read_csv('leads_qualified.csv')
# Solo enriquece leads Hot (para ahorrar créditos API)
df_hot = df[df['classification'] == 'hot']
enriched = []
for idx, lead in df_hot.iterrows():
    enriched_lead = enrich_lead(lead.to_dict(), HUNTER_API_KEY)
    enriched.append(enriched_lead)
    if idx % 10 == 0:
        print(f"Enriched {idx}/{len(df_hot)} leads")
df_enriched = pd.DataFrame(enriched)
# Filtra solo leads con email encontrado
df_ready = df_enriched[df_enriched['email'].notna()]
df_ready.to_csv('leads_ready_for_outreach.csv', index=False)
print(f"{len(df_ready)} leads ready for outreach")
Componente 6: Personalización Automatizada
Genera emails personalizados automáticamente usando datos del lead:
def generate_personalized_email(lead):
    """Genera email personalizado basado en datos del lead"""
    # Variables base
    name = lead['name']
    city = lead.get('city', 'tu zona')
    rating = lead.get('rating', 0)
    reviews = lead.get('reviews_count', 0)
    # Selecciona template basado en perfil
    if lead.get('opened_recently'):
        template = "new_business"
    elif 3.8 <= rating < 4.2:
        template = "improvement_opportunity"
    elif rating >= 4.5 and reviews > 200:
        template = "high_performer"
    else:
        template = "generic"
    # Templates
    templates = {
        "new_business": f"""
Asunto: {name} - Oportunidad {city}
Hola,
Soy [Tu Nombre] de [Tu Empresa]. Vi que {name} inauguró recientemente en {city} - ¡enhorabuena!
Los primeros meses son cruciales para construir base de clientes. Trabajo con negocios nuevos como el tuyo ayudándoles a [tu propuesta de valor].
Por ejemplo, [Cliente Similar] consiguió [resultado específico] en sus primeros 3 meses con nosotros.
¿Tendría sentido una llamada rápida esta semana? Puedo mostrarte exactamente cómo lo hacemos.
[Tu Nombre]
[Teléfono]
        """,
        "improvement_opportunity": f"""
Asunto: {name} - De {rating}★ a 4.5+★
Hola,
Investigando {city}, encontré {name}. Con {rating}★ y {reviews} reseñas, claramente estáis haciendo bien muchas cosas.
Vi que [insight específico de reseñas]. Trabajo con negocios similares ayudándoles a [solución].
[Cliente Similar en misma ciudad] subió de {rating}★ a 4.6★ en 4 meses trabajando con nosotros.
¿10 minutos esta semana para ver si tiene sentido para vosotros?
[Tu Nombre]
        """,
        "high_performer": f"""
Asunto: {name} - Potencial de expansión
Hola,
{name} es claramente un referente en {city} ({rating}★, {reviews} reseñas - impresionante).
Justo por eso os escribo. Trabajo con negocios top como el vuestro que están listos para [siguiente nivel: segunda ubicación / franquicia / etc].
¿Estáis en modo crecimiento? Si es así, me encantaría hablar 15 min.
[Tu Nombre]
        """
    }
    # Añade datos de tracking
    email_data = {
        'to': lead['email'],
        'subject': templates[template].split('\n')[1].replace('Asunto: ', ''),
        'body': templates[template],
        'lead_id': lead.get('id'),
        'template_used': template,
        'variables': {
            'name': name,
            'rating': rating,
            'reviews': reviews,
            'city': city
        }
    }
    return email_data
# Genera emails para todos los leads
df = pd.read_csv('leads_ready_for_outreach.csv')
emails_to_send = []
for idx, lead in df.iterrows():
    email = generate_personalized_email(lead.to_dict())
    emails_to_send.append(email)
# Guarda queue de emails
pd.DataFrame(emails_to_send).to_json('emails_queue.json', orient='records')
print(f"Generated {len(emails_to_send)} personalized emails")
Componente 7: Outreach Automation
88% Email Finding Rate (vs. 45% Apollo)
MapiLeads especializado en negocios locales. Mejor data quality que herramientas genéricas.
Comparar herramientas 🚀Envía emails automáticamente con seguimiento:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import json
from datetime import datetime, timedelta
class EmailAutomation:
    def __init__(self, smtp_server, smtp_port, username, password):
        self.smtp_server = smtp_server
        self.smtp_port = smtp_port
        self.username = username
        self.password = password
    def send_email(self, to, subject, body):
        """Envía un email"""
        msg = MIMEMultipart()
        msg['From'] = self.username
        msg['To'] = to
        msg['Subject'] = subject
        msg.attach(MIMEText(body, 'plain'))
        try:
            server = smtplib.SMTP(self.smtp_server, self.smtp_port)
            server.starttls()
            server.login(self.username, self.password)
            server.send_message(msg)
            server.quit()
            return True
        except Exception as e:
            print(f"Error sending to {to}: {e}")
            return False
    def process_email_queue(self, queue_file, max_per_day=50):
        """Procesa queue de emails con rate limiting"""
        with open(queue_file, 'r') as f:
            emails = json.load(f)
        sent_today = 0
        for email in emails:
            if email.get('status') == 'sent':
                continue
            if sent_today >= max_per_day:
                print(f"Daily limit reached ({max_per_day})")
                break
            # Envía
            success = self.send_email(
                email['to'],
                email['subject'],
                email['body']
            )
            if success:
                email['status'] = 'sent'
                email['sent_at'] = datetime.now().isoformat()
                sent_today += 1
                # Schedule follow-up
                self.schedule_followup(email)
            # Rate limiting: 1 email cada 2 min
            import time
            time.sleep(120)
        # Guarda estado actualizado
        with open(queue_file, 'w') as f:
            json.dump(emails, f, indent=2)
        return sent_today
    def schedule_followup(self, email):
        """Programa follow-up automático"""
        followup_date = datetime.now() + timedelta(days=3)
        followup = {
            'to': email['to'],
            'subject': f"Re: {email['subject']}",
            'body': f"""
Hola de nuevo,
Te escribí hace unos días sobre {email['variables']['name']}.
¿Tuviste chance de ver mi email?
Adjunto este recurso que creo que te interesará: [link a case study].
¿Hablamos?
[Tu Nombre]
            """,
            'lead_id': email['lead_id'],
            'type': 'followup',
            'scheduled_for': followup_date.isoformat(),
            'status': 'scheduled'
        }
        # Añade a queue de follow-ups
        with open('followups_queue.json', 'a') as f:
            f.write(json.dumps(followup) + '\n')
# Setup automation
automation = EmailAutomation(
    smtp_server='smtp.gmail.com',
    smtp_port=587,
    username='tu@email.com',
    password='tu_password'  # Usa App Password si es Gmail
)
# Corre diariamente
import schedule
def daily_email_job():
    sent = automation.process_email_queue('emails_queue.json', max_per_day=50)
    print(f"Sent {sent} emails today")
schedule.every().day.at("09:00").do(daily_email_job)
while True:
    schedule.run_pending()
    time.sleep(60)
Componente 8: Response Handling & Meeting Booking
Último componente: detectar respuestas y agendar reuniones automáticamente:
import imaplib
import email
from email.header import decode_header
import re
class ResponseHandler:
    def __init__(self, imap_server, username, password, calendly_link):
        self.imap_server = imap_server
        self.username = username
        self.password = password
        self.calendly_link = calendly_link
    def check_responses(self):
        """Revisa inbox y detecta respuestas"""
        mail = imaplib.IMAP4_SSL(self.imap_server)
        mail.login(self.username, self.password)
        mail.select('inbox')
        # Busca emails sin leer de últimas 24h
        status, messages = mail.search(None, 'UNSEEN')
        email_ids = messages[0].split()
        responses = []
        for email_id in email_ids:
            status, msg_data = mail.fetch(email_id, '(RFC822)')
            for response_part in msg_data:
                if isinstance(response_part, tuple):
                    msg = email.message_from_bytes(response_part[1])
                    subject = decode_header(msg["Subject"])[0][0]
                    from_email = msg.get("From")
                    body = self.get_email_body(msg)
                    # Clasifica respuesta
                    sentiment = self.classify_response(body)
                    responses.append({
                        'from': from_email,
                        'subject': subject,
                        'body': body,
                        'sentiment': sentiment,
                        'email_id': email_id
                    })
        mail.close()
        mail.logout()
        return responses
    def classify_response(self, body):
        """Clasifica respuesta como positive/negative/neutral"""
        positive_keywords = [
            'interesa', 'hablamos', 'llámame', 'reunión',
            'cuéntame más', 'información', 'sí', 'adelante'
        ]
        negative_keywords = [
            'no interesa', 'no gracias', 'dar de baja',
            'no contactar', 'spam', 'no me interesa'
        ]
        body_lower = body.lower()
        positive_count = sum(1 for kw in positive_keywords if kw in body_lower)
        negative_count = sum(1 for kw in negative_keywords if kw in body_lower)
        if positive_count > negative_count:
            return 'positive'
        elif negative_count > positive_count:
            return 'negative'
        else:
            return 'neutral'
    def auto_respond_positive(self, response):
        """Responde automáticamente a leads positivos con Calendly"""
        auto_reply = f"""
Hola!
Genial que te interese. Para no dar vueltas con horarios, aquí tienes mi calendario:
{self.calendly_link}
Elige el día/hora que mejor te venga (son solo 15 minutos).
Nos vemos pronto!
[Tu Nombre]
        """
        # Envía auto-respuesta
        # (usa el EmailAutomation del componente anterior)
        return auto_reply
    def get_email_body(self, msg):
        """Extrae body del email"""
        if msg.is_multipart():
            for part in msg.walk():
                if part.get_content_type() == "text/plain":
                    return part.get_payload(decode=True).decode()
        else:
            return msg.get_payload(decode=True).decode()
# Automatiza checking de respuestas cada hora
handler = ResponseHandler(
    imap_server='imap.gmail.com',
    username='tu@email.com',
    password='tu_password',
    calendly_link='https://calendly.com/tu-link'
)
def check_and_respond():
    responses = handler.check_responses()
    positive_responses = [r for r in responses if r['sentiment'] == 'positive']
    print(f"Found {len(positive_responses)} positive responses")
    for response in positive_responses:
        handler.auto_respond_positive(response)
        print(f"Auto-responded to {response['from']}")
schedule.every(1).hours.do(check_and_respond)
Integrando Todo: El Sistema Completo
Ahorra 30 Horas/Mes con Automatización
Extracción automática 24/7. Exporta CSV listo para CRM en 1 clic.
Probar gratis 🚀Ahora conectamos los 8 componentes en un orquestador maestro:
#!/usr/bin/env python3
"""
Master Orchestrator - Automated Lead Generation System
Runs all 8 components in sequence
"""
import schedule
import time
from datetime import datetime
# Import all components
from search_generator import generate_searches
from scraper import scrape_batch
from data_processor import process_leads
from scorer import score_and_filter
from enricher import enrich_leads
from personalizer import generate_emails
from email_sender import send_batch
from response_handler import check_responses
def daily_pipeline():
    """Pipeline completo que corre diariamente"""
    print(f"\n{'='*60}")
    print(f"Starting daily pipeline: {datetime.now()}")
    print(f"{'='*60}\n")
    # Component 1: Generate searches (si es lunes)
    if datetime.now().weekday() == 0:  # Monday
        print("[1/8] Generating new searches...")
        generate_searches()
    # Component 2: Scrape (runs 24/7 in background, just verify)
    print("[2/8] Scraping in progress (background worker)...")
    # Component 3: Process raw data
    print("[3/8] Processing raw leads...")
    processed_count = process_leads()
    print(f"  ✓ Processed {processed_count} leads")
    # Component 4: Score and filter
    print("[4/8] Scoring and filtering...")
    qualified_count = score_and_filter()
    print(f"  ✓ Qualified {qualified_count} hot leads")
    # Component 5: Enrich
    print("[5/8] Enriching hot leads...")
    enriched_count = enrich_leads()
    print(f"  ✓ Enriched {enriched_count} leads with emails")
    # Component 6: Personalize
    print("[6/8] Generating personalized emails...")
    emails_count = generate_emails()
    print(f"  ✓ Generated {emails_count} personalized emails")
    # Component 7: Send (respects daily limit)
    print("[7/8] Sending emails...")
    sent_count = send_batch(max_emails=50)
    print(f"  ✓ Sent {sent_count} emails")
    # Component 8: Check responses
    print("[8/8] Checking responses...")
    response_count = check_responses()
    print(f"  ✓ Found {response_count} new responses")
    print(f"\n{'='*60}")
    print(f"Pipeline completed: {datetime.now()}")
    print(f"{'='*60}\n")
def hourly_response_check():
    """Revisa respuestas cada hora"""
    print(f"[{datetime.now()}] Checking for new responses...")
    response_count = check_responses()
    print(f"  ✓ {response_count} new responses")
# Schedule jobs
schedule.every().day.at("06:00").do(daily_pipeline)
schedule.every(1).hours.do(hourly_response_check)
print("Orchestrator started. Running 24/7...")
print("Daily pipeline: 06:00")
print("Response checks: Every hour")
while True:
    schedule.run_pending()
    time.sleep(60)
Deploy en servidor para 24/7:
# En servidor (DigitalOcean, AWS, etc.)
sudo apt-get update
sudo apt-get install python3-pip
pip3 install schedule pandas requests phonenumbers
# Copia todos los scripts
scp *.py usuario@servidor:/home/usuario/lead-automation/
# En el servidor
cd /home/usuario/lead-automation
pip3 install pm2
pm2 start orchestrator.py --name lead-system --interpreter python3
pm2 save
pm2 startup
# Sistema ahora corre 24/7 automáticamente
Casos de Estudio: Sistemas Automatizados Reales
Caso 1: Agencia SaaS → €120k ARR con Sistema 95% Automatizado
Perfil: Agencia que vende software de gestión de reseñas a negocios locales.
Sistema implementado:
- MapiLeads para scraping (2,000 leads/semana)
- Scoring automático prioriza negocios con 100-500 reseñas
- Hunter.io enrichment automático
- Lemlist para outreach multicanal
- Calendly para auto-booking
Workflow completo:
- Lunes 06:00: Script genera 500 búsquedas nuevas
- Lunes-Viernes: MapiLeads scraper corre 8h/día (semi-automatizado)
- Diario 18:00: Procesamiento + scoring + enrichment automático
- Martes-Sábado 09:00: Envío de 50 emails/día (Lemlist)
- 24/7: Calendly auto-agenda reuniones cuando responden
Números (promedio mensual):
- 8,000 leads scraped
- 1,800 cualificados (score >70)
- 900 enriquecidos con email
- 900 emails enviados
- 180 respuestas (20% response rate)
- 45 reuniones agendadas automáticamente
- 12 clientes cerrados (27% close rate)
- MRR añadido: €10,000 (ticket €850/mes)
Tiempo invertido manualmente:
- 2h/semana supervisando sistema
- 6h/semana en reuniones (las únicas no automatizables)
- Total: 8h/semana para €10k MRR nuevo = €1,250/hora
Clave del éxito: "El sistema funciona solo 95% del tiempo. Solo intervengo si detecto anomalía o para las reuniones. Es mi empleado más productivo."
Para mejorar las conversiones de estos leads, implementaron las estrategias B2B avanzadas de Google Maps.
Caso 2: Consultor Freelance → €8k/mes Pasivos con Zero Tiempo Activo
Perfil: Consultor de marketing digital que quería ingresos pasivos.
Sistema ultra-minimalista:
- No scraper custom, solo MapiLeads manualmente 1h/semana
- Airtable como "CRM" (scoring con fórmulas)
- Zapier conecta todo (sin código)
- Gmail + Boomerang para follow-ups
Workflow:
- Domingo tarde (1h): Scrapea manualmente 200 leads con MapiLeads
- Automático: Zapier mueve leads de CSV a Airtable
- Automático: Fórmulas en Airtable calculan score
- Automático: Zapier filtra Hot Leads, busca email en Hunter.io
- Lunes-Viernes 10am: Zapier envía 10 emails/día desde Gmail
- Automático: Boomerang programa follow-ups si no responden
- Automático: Calendly agenda reuniones
Setup cost:
- MapiLeads: €39/mes
- Zapier: €20/mes (plan básico)
- Hunter.io: €49/mes (500 emails)
- Airtable: €0 (plan gratis)
- Calendly: €0 (plan gratis)
- Total: €108/mes
Resultados (mensual promedio):
- 800 leads scraped/mes (200/semana × 1h cada domingo)
- 200 hot leads
- 120 con email (60% hit rate Hunter.io)
- 120 emails enviados
- 18 respuestas (15% response)
- 8 reuniones agendadas
- 2-3 clientes cerrados (€2,500 one-time consultoría)
- Revenue: €5k-7.5k/mes con 1h/semana de trabajo
Lección clave: "No necesitas sistema perfecto. Zapier + herramientas no-code me dan 90% de los resultados con 10% de la complejidad."
Stack de Herramientas por Nivel de Automatización
ROI 1,000%+ en Lead Generation
Reduce costos de €2/lead a €0.10/lead. Casos reales: €141 inversión → €233k facturación año 1.
Ver casos de éxito 🚀Nivel 1: Starter (0-€50/mes)
- Scraping: MapiLeads Free (100 leads/mes)
- CRM: Google Sheets (gratis)
- Email: Gmail + Boomerang (gratis + €5/mes)
- Scheduling: Calendly Free
- Automation: Manual con checklists
Resultado: 50-100 leads cualificados/mes, 2-4 clientes, €2k-4k revenue/mes, 10h trabajo/semana
Nivel 2: Growth (€100-200/mes)
- Scraping: MapiLeads Hobby (€39) - 2,000 leads/mes
- Enrichment: Hunter.io (€49) - 500 emails
- CRM: Airtable Pro (€20) o Pipedrive (€15)
- Email Automation: Mailshake (€29) o Lemlist (€59)
- Integration: Zapier (€20)
Resultado: 400-600 leads cualificados/mes, 10-15 clientes, €8k-12k revenue/mes, 6h trabajo/semana
Nivel 3: Scale (€300-600/mes)
- Scraping: MapiLeads Business (€99) + custom Puppeteer workers
- Infrastructure: 3-5 VPS (€50-100)
- Enrichment: Hunter.io + Clearbit (€150)
- Email: Reply.io multicanal (€70)
- CRM: HubSpot o Pipedrive Pro (€50)
- Automation: Custom scripts + cron jobs
Resultado: 2,000+ leads cualificados/mes, 40-60 clientes, €30k-50k revenue/mes, 4h trabajo/semana (solo reuniones)
Nivel 4: Enterprise (€1k-3k/mes)
- Scraping: Custom distributed system (10-50 workers)
- Infrastructure: AWS/GCP auto-scaling (€500-1k)
- Proxies: Residential pool 1,000+ IPs (€500)
- Data: PostgreSQL + Elasticsearch (€200)
- Enrichment: Multiple APIs con fallbacks (€300)
- Outreach: Outreach.io o Salesloft (€300)
- Team: VA para QA + meeting setting (€500)
Resultado: 10,000+ leads/mes, 100+ clientes, €100k-300k revenue/mes, sistema completamente delegado
Errores Fatales de Automatización (y Cómo Evitarlos)
Error #1: Automatizar Antes de Validar
Problema: Construyes sistema automatizado antes de probar que tu ICP/mensaje funciona.
Solución: Primeros 50 leads hazlos manualmente. Solo automatiza lo que ya funciona.
Error #2: No Monitorizar el Sistema
Problema: Sistema corre en background, crashea/falla, no te enteras hasta días después.
Solución: Implementa alertas automáticas:
# Alerta si el scraper no procesa nada en 24h
if datetime.now() - last_scrape_time > timedelta(hours=24):
    send_alert_email("SCRAPER STOPPED - Check immediately")
# Alerta si email bounce rate >10%
if bounce_rate > 0.1:
    send_alert_email(f"High bounce rate: {bounce_rate*100}%")
Error #3: Spam Yourself Into Blacklists
Problema: Automatizas emails pero sin warm-up, tu dominio se marca como spam.
Solución: Email warm-up gradual:
- Semana 1: 10 emails/día
- Semana 2: 20 emails/día
- Semana 3: 35 emails/día
- Semana 4+: 50 emails/día (máximo recomendado)
Error #4: Zero Personalización
Problema: Automatización = emails genéricos = 2% response rate.
Solución: Automatiza personalización, no elimines personalización:
- ✅ "Vi que [Negocio] inauguró hace [X meses]" ← datos del scraper
- ✅ "Varias reseñas mencionan [pain point]" ← análisis automático
- ❌ "Hola, te ofrezco [servicio genérico]" ← spam
El Sistema Mínimo Viable: Empieza en 4 Horas
Extrae 10,000 Leads/Mes desde €4.99
MapiLeads encuentra emails + teléfonos en 88% de negocios locales. Setup 2 minutos.
Empezar ahora 🚀No necesitas implementar todo hoy. Aquí está el MVP para empezar este fin de semana:
Sábado (3 horas total)
09:00-10:00 (1h) - Setup Herramientas
- Instala MapiLeads (2 min)
- Crea Google Sheet como CRM (10 min)
- Setup Gmail filters y labels (15 min)
- Crea cuenta Calendly (5 min)
- Crea cuenta Hunter.io (5 min)
- Escribe 2 templates de email (20 min)
10:00-11:30 (1.5h) - Primera Extracción
- Define ICP (15 min)
- Scrapea 100 leads con MapiLeads (30 min)
- Limpia datos en Google Sheet (30 min)
- Score manualmente, identifica top 20 (15 min)
14:00-14:30 (30 min) - Enrichment
- Busca emails de top 20 con Hunter.io (20 min)
- Completa Google Sheet con emails (10 min)
Domingo (1 hora total)
10:00-11:00 (1h) - Primera Campaña
- Personaliza emails para 20 leads (30 min)
- Envía emails manualmente (15 min)
- Setup Boomerang para follow-ups en 3 días (15 min)
Resultado esperado:
- 100 leads en tu "database"
- 20 emails enviados a hot leads
- 3-5 respuestas en próximos 7 días
- 1-2 reuniones agendadas
- Sistema básico funcionando, listo para automatizar progresivamente
Conclusión: Del Trabajo Manual al Sistema que Trabaja por Ti
La automatización de leads B2B con maps scraper no es futuro lejano. Es ahora.
Los números no mienten:
- Proceso manual: 2-3 leads/hora, €0 herramientas, 40h/semana trabajo = 80-120 leads/semana
- Sistema automatizado: 500 leads/semana, €100-200/mes herramientas, 4h/semana trabajo = 500 leads/semana
Ratio de eficiencia: 6-8x más leads con 90% menos tiempo.
La pregunta no es si automatizar. Es cuándo empezar.
Empieza este fin de semana con el MVP de 4 horas. En 30 días tendrás un sistema que genera leads mientras duermes.