🌐 ES
Chrome Añadir a Chrome (es gratis)
🎯 Lead Generation 📅 22 Enero 2025 ⏱️ 18 min

Maps Scraper: Automatiza tu Generación de Leads B2B Completamente en 2025

En esta guía de automatización completa aprenderás:

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

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

  1. Google Sheet con búsquedas pendientes
  2. Zapier detecta nuevas filas
  3. Trigger abre Google Maps con búsqueda
  4. MapiLeads auto-extrae (modo scheduled)
  5. 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:

Workflow completo:

  1. Lunes 06:00: Script genera 500 búsquedas nuevas
  2. Lunes-Viernes: MapiLeads scraper corre 8h/día (semi-automatizado)
  3. Diario 18:00: Procesamiento + scoring + enrichment automático
  4. Martes-Sábado 09:00: Envío de 50 emails/día (Lemlist)
  5. 24/7: Calendly auto-agenda reuniones cuando responden

Números (promedio mensual):

Tiempo invertido manualmente:

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:

Workflow:

  1. Domingo tarde (1h): Scrapea manualmente 200 leads con MapiLeads
  2. Automático: Zapier mueve leads de CSV a Airtable
  3. Automático: Fórmulas en Airtable calculan score
  4. Automático: Zapier filtra Hot Leads, busca email en Hunter.io
  5. Lunes-Viernes 10am: Zapier envía 10 emails/día desde Gmail
  6. Automático: Boomerang programa follow-ups si no responden
  7. Automático: Calendly agenda reuniones

Setup cost:

Resultados (mensual promedio):

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)

Resultado: 50-100 leads cualificados/mes, 2-4 clientes, €2k-4k revenue/mes, 10h trabajo/semana

Nivel 2: Growth (€100-200/mes)

Resultado: 400-600 leads cualificados/mes, 10-15 clientes, €8k-12k revenue/mes, 6h trabajo/semana

Nivel 3: Scale (€300-600/mes)

Resultado: 2,000+ leads cualificados/mes, 40-60 clientes, €30k-50k revenue/mes, 4h trabajo/semana (solo reuniones)

Nivel 4: Enterprise (€1k-3k/mes)

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:

Error #4: Zero Personalización

Problema: Automatización = emails genéricos = 2% response rate.

Solución: Automatiza personalización, no elimines personalización:

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

10:00-11:30 (1.5h) - Primera Extracción

14:00-14:30 (30 min) - Enrichment

Domingo (1 hora total)

10:00-11:00 (1h) - Primera Campaña

Resultado esperado:

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:

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.

Comienza tu Automatización Ahora →

Comparte este artículo