Scrapear People Also Ask y Autocomplete de Google para Keyword Research (Guía Python)

Guía code-first para scrapear People Also Ask, Autocomplete y Related Searches de Google con Python, httpx y Playwright usando proxies residenciales geo-targeted. Incluye expansión de semillas, clustering por intención y exportación a CSV.

Scrape Google People Also Ask and Autocomplete for Keyword Research (Python Guide)
En este artículo

Si construyes herramientas de keyword research, Google te ofrece tres minas gratuitas que la mayoría de SEOs tocan manualmente y pocos automatizan a escala: Autocomplete, People Also Ask (PAA) y Related Searches. En esta guía vas a scrapear People Also Ask y Autocomplete de Google para investigación de palabras clave con Python, usando proxies residenciales para evitar rate limits y sesgos de localización.

El objetivo es convertir una semilla como "keyword research" en cientos de longtails, extraer preguntas reales de usuarios y exportar un CSV listo para planificar contenido y FAQs. Todo con código ejecutable, manejo de errores y patrones de producción.

Las tres minas de oro gratuitas para scrapear People Also Ask y Autocomplete

Google expone sugerencias en tres puntos distintos del journey de búsqueda, y cada uno mapea a una fase de intención:

  • Autocomplete (suggestqueries.google.com): aparece mientras el usuario escribe. Refleja intención exploratoria y consultas longtail emergentes. El endpoint complete/search?client=chrome&q= devuelve JSON puro con hasta 10 sugerencias por consulta.
  • People Also Ask: cajas de preguntas relacionadas que aparecen dentro del SERP. Cada acordeón, al expandirse, revela una nueva pregunta anidada — ideal para contenido de respuesta y FAQs.
  • Related Searches: lista al final del SERP. Representa consultas vecinas semánticamente, útiles para clustering temático.

Autocomplete captura intención antes del click, PAA captura intención durante la exploración del resultado, y Related Searches captura intención después. Combinados, cubren el embudo completo. Puedes leer más sobre cómo funciona el mecanismo de autocompletado en la entrada de Wikipedia sobre Autocomplete.

Por qué el scraping rápido choca con rate limits y sesgo local

El problema técnico central: Google aplica rate limits por IP a suggestqueries.google.com y al SERP. Si lanzas 200 peticiones por segundo desde un solo IP, en minutos recibes 429 Too Many Requests o respuestas vacías. Además, Autocomplete y PAA son sensibles a la geolocalización: la misma consulta devuelve sugerencias distintas en Alemania vs. México, porque Google personaliza por país, idioma e historial.

Esto significa dos cosas para tu pipeline:

  1. Necesitas rotación de IP para escalar sin bans.
  2. Necesitas geo-targeting preciso (país y ciudad) para capturar sugerencias localizadas reales, no las de un datacenter en Virginia.

Los proxies datacenter son rápidos pero sus rangos IP están catalogados como no residenciales y Google los degrada o bloquea con más frecuencia. Los proxies residenciales, al venir de ISPs reales, tienen huellas mucho más limpias. Para escala seria, Google ofrece la Custom Search JSON API oficial, pero está limitada a 100 consultas/día en el tier gratuito y no expone PAA ni Autocomplete — por eso el scraping sigue siendo la opción práctica para keyword research a volumen.

Configuración de ProxyHat: geo-targeting en el username

ProxyHat permite pasar flags de país, ciudad y sesión directamente en el username del proxy. El formato es:

http://user-country-DE-city-berlin:PASSWORD@gate.proxyhat.com:8080
socks5://user-country-DE-city-berlin:PASSWORD@gate.proxyhat.com:1080

Esto te da control granular: usa country-US para sugerencias estadounidenses, country-MX para mexicanas, o country-DE-city-berlin para ultra-localización. La flag session-abc123 mantiene la misma IP entre peticiones (útil para Playwright, donde necesitas coherencia durante una sesión de navegador). Consulta la documentación de ProxyHat para el listado completo de países y ciudades soportadas, y revisa el mapa de ubicaciones antes de planificar campañas multi-país.

A continuación, un wrapper que encapsula la configuración y genera URLs de proxy listas para usar, mostrando uso raw y estilo SDK lado a lado:

from dataclasses import dataclass
from typing import Optional, List

@dataclass
class ProxyHatConfig:
    username: str
    password: str
    country: str = "US"
    city: Optional[str] = None
    session: Optional[str] = None

    def _build_user(self) -> str:
        flags: List[str] = []
        if self.country:
            flags.append(f"country-{self.country}")
        if self.city:
            flags.append(f"city-{self.city.lower()}")
        if self.session:
            flags.append(f"session-{self.session}")
        if not flags:
            return self.username
        return f"{self.username}-" + "-".join(flags)

    def http_url(self) -> str:
        return f"http://{self._build_user()}:{self.password}@gate.proxyhat.com:8080"

    def socks5_url(self) -> str:
        return f"socks5://{self._build_user()}:{self.password}@gate.proxyhat.com:1080"

# Uso raw
cfg = ProxyHatConfig(
    username="user", password="pass",
    country="DE", city="berlin", session="kw-de-1"
)
print(cfg.http_url())
# http://user-country-DE-city-berlin-session-kw-de-1:pass@gate.proxyhat.com:8080

# Uso con httpx
import httpx
client = httpx.AsyncClient(proxy=cfg.http_url())

Este patrón te permite intercambiar país por consulta y mantener sesiones estables para Playwright. Para ver opciones de plan y límites de concurrencia, visita la página de precios de ProxyHat.

Paso 1: Scraper de Autocomplete con httpx y proxy

El endpoint de Autocomplete en formato Chrome devuelve JSON con la estructura [query, [suggestions], [types], ...]. Es ligero, rápido (~150-300 ms por consulta) y no requiere renderizar JavaScript, lo que lo hace ideal para concurrencia alta.

import httpx
import asyncio
from typing import List

AUTOCOMPLETE_URL = "https://suggestqueries.google.com/complete/search"

async def fetch_suggestions(client: httpx.AsyncClient, query: str) -> List[str]:
    params = {"client": "chrome", "q": query}
    try:
        r = await client.get(AUTOCOMPLETE_URL, params=params, timeout=10.0)
        r.raise_for_status()
        data = r.json()
        if isinstance(data, list) and len(data) > 1:
            return data[1]
        return []
    except (httpx.HTTPError, ValueError) as exc:
        print(f"Error para '{query}': {exc}")
        return []

async def main():
    proxy = "http://user-country-US:PASSWORD@gate.proxyhat.com:8080"
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
    async with httpx.AsyncClient(proxy=proxy, headers=headers) as client:
        suggestions = await fetch_suggestions(client, "keyword research")
        print(suggestions)

asyncio.run(main())

Cada llamada devuelve hasta 10 sugerencias. Si combinas esto con expansión de semillas, una keyword se convierte en cientos.

Paso 2: Expansor de semillas a longtail (a-z + modificadores)

La técnica clásica para multiplicar semillas: añade cada letra del alfabeto y modificadores de intención como prefijos/sufijos. Así, "keyword research" genera "keyword research a", "keyword research b", ..., "que es keyword research", "keyword research gratis", etc.

import string
from typing import List

def expand_seed(seed: str, modifiers: List[str] = None) -> List[str]:
    if modifiers is None:
        modifiers = [
            "que es", "como", "mejor", "gratis", "2025",
            "herramientas", "ejemplos", "vs", "tutorial", "definicion"
        ]
    queries = set()
    # Prefijos a-z (autocompletado letra a letra)
    for letter in string.ascii_lowercase:
        queries.add(f"{seed} {letter}")
    # Modificadores como prefijo y sufijo
    for mod in modifiers:
        queries.add(f"{mod} {seed}")
        queries.add(f"{seed} {mod}")
    return sorted(queries)

seeds = ["keyword research", "seo python", "scraping google"]
all_queries = []
for s in seeds:
    all_queries.extend(expand_seed(s))
print(f"Generadas {len(all_queries)} consultas desde {len(seeds)} semillas")
# Generadas 243 consultas desde 3 semillas

Cada una de esas 243 consultas se pasa al scraper de Autocomplete, que devuelve ~10 sugerencias → potencialmente 2,000+ longtails. Para ejecutarlas sin saturar, usa concurrencia limitada con un semáforo:

import asyncio
import httpx

CONCURRENCY = 50  # 50 sesiones concurrentes
PROXY = "http://user-country-US:PASSWORD@gate.proxyhat.com:8080"

async def bounded(sem, client, q):
    async with sem:
        return q, await fetch_suggestions(client, q)

async def expand_concurrent(queries: List[str]) -> dict:
    sem = asyncio.Semaphore(CONCURRENCY)
    async with httpx.AsyncClient(proxy=PROXY) as client:
        tasks = [bounded(sem, client, q) for q in queries]
        results = await asyncio.gather(*tasks, return_exceptions=True)
    out = {}
    for r in results:
        if isinstance(r, tuple):
            out[r[0]] = r[1]
    return out

suggestions_map = asyncio.run(expand_concurrent(all_queries))
print(f"Recogidas sugerencias para {len(suggestions_map)} consultas")

Con 50 conexiones concurrentes y ~200 ms por petición, procesar 243 consultas toma ~1 segundo. Ajusta CONCURRENCY según tu plan y el comportamiento observado.

Paso 3: Extracción de People Also Ask con Playwright

El PAA requiere renderizar JavaScript y hacer click en los acordeones para revelar preguntas anidadas. Aquí entra Playwright con proxy. La flag session mantiene la misma IP durante toda la sesión del navegador, evitando que Google detecte saltos sospechosos.

from playwright.async_api import async_playwright
from typing import List
import asyncio

PROXY = {
    "server": "http://gate.proxyhat.com:8080",
    "username": "user-country-US-session-paa-1",
    "password": "PASSWORD",
}

PAA_QUESTION_SELECTOR = 'div.related-question-pair'

async def scrape_paa(query: str, max_clicks: int = 6) -> List[str]:
    questions = set()
    async with async_playwright() as p:
        browser = await p.chromium.launch(proxy=PROXY, headless=True)
        page = await browser.new_page()
        await page.goto(f"https://www.google.com/search?q={query}")
        await page.wait_for_selector(PAA_QUESTION_SELECTOR, timeout=10000)

        for _ in range(max_clicks):
            els = await page.query_selector_all(PAA_QUESTION_SELECTOR)
            for el in els:
                text = (await el.inner_text()).strip().split("\n")[0]
                if text.endswith("?"):
                    questions.add(text)
            # Click en el primer acordeón cerrado para revelar preguntas anidadas
            closed = await page.query_selector(f'{PAA_QUESTION_SELECTOR}:not([expanded])')
            if not closed:
                break
            try:
                await closed.click(timeout=2000)
                await page.wait_for_timeout(800)
            except Exception:
                break

        await browser.close()
    return sorted(questions)

paa_questions = asyncio.run(scrape_paa("keyword research"))
print(f"Extraídas {len(paa_questions)} preguntas PAA")

Cada click revela una nueva pregunta y, a veces, un sub-acordeón. Repitiendo 6 iteraciones puedes capturar 15-40 preguntas por consulta. Las preguntas del PAA suelen venir con una fuente citada (un dominio que Google extrae como respuesta) — útil para link-building y análisis competitivo.

Paso 4: Recursión para expansión profunda de PAA

Para escalar, convierte cada pregunta extraída en una nueva consulta de búsqueda, que a su vez genera su propio bloque PAA. Esto es recursión de profundidad controlada:

from typing import Set

async def recursive_paa(seed: str, depth: int = 2, max_per_level: int = 10) -> Set[str]:
    seen: Set[str] = set()
    frontier = [seed]
    for _ in range(depth):
        next_frontier = []
        for q in frontier:
            if q in seen:
                continue
            seen.add(q)
            questions = await scrape_paa(q, max_clicks=4)
            next_frontier.extend(questions[:max_per_level])
            await asyncio.sleep(2)  # throttle educado
        frontier = next_frontier
    return seen

all_questions = asyncio.run(recursive_paa("keyword research", depth=2))
print(f"Total preguntas únicas: {len(all_questions)}")

Con profundidad 2 y 10 preguntas por nivel, una semilla produce ~100 preguntas únicas. Subir a profundidad 3 multiplica exponencialmente, pero incrementa el riesgo de bloqueos — equilibra con throttling y rotación de sesión.

Paso 5: Deduplicación, clustering por intención y exportación a CSV

Tras recoger sugerencias de Autocomplete y preguntas de PAA, tendrás duplicados y ruido. Normaliza, deduplica y agrupa por intención para priorizar contenido:

import csv
import re
from collections import defaultdict
from typing import List, Dict

def normalize(q: str) -> str:
    return re.sub(r"[^\w\sáéíóúñ]", "", q.lower()).strip()

def cluster_by_intent(questions: List[str]) -> Dict[str, List[str]]:
    buckets = defaultdict(list)
    markers = {
        "informacional": ["que es", "que son", "definicion", "significado", "diferencia"],
        "transaccional": ["comprar", "precio", "barato", "mejor", "review", "top"],
        "howto": ["como", "tutorial", "pasos", "guia", "aprender"],
        "comparativa": ["vs", "alternativa", "comparativa"],
    }
    seen = set()
    for q in questions:
        n = normalize(q)
        if not n or n in seen:
            continue
        seen.add(n)
        intent = "otro"
        for label, kws in markers.items():
            if any(k in n for k in kws):
                intent = label
                break
        buckets[intent].append(q)
    return buckets

def export_csv(questions: List[str], path: str = "keywords.csv") -> None:
    clusters = cluster_by_intent(questions)
    with open(path, "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        w.writerow(["keyword", "intent", "source"])
        for intent, qs in clusters.items():
            for q in qs:
                w.writerow([q, intent, "paa"])
    print(f"Exportadas {sum(len(v) for v in clusters.values())} keywords a {path}")

# Combinar Autocomplete + PAA y exportar
combined = list(all_questions) + [s for subs in suggestions_map.values() for s in subs]
export_csv(combined, "keyword_research.csv")

El CSV resultante es directamente utilizable: filtra por intención howto para guías, informacional para definiciones, comparativa para páginas de alternativas. Es la base de un calendario editorial orientado a búsqueda.

Errores comunes y casos límite

  • No respetar el locale: si pides Autocomplete sin geo-targeting, obtienes sugerencias del país del IP del datacenter, no del mercado objetivo. Usa siempre country-XX en el username.
  • Sesiones inestables en Playwright: sin la flag session-, cada petición puede rotar IP y Google muestra un challenge de verificación. Mantén una sesión por navegador.
  • No manejar 429: añade backoff exponencial y reintentos. Un 429 aislado no es fatal; ignorarlo sí lo es.
  • Selectores frágiles de PAA: Google cambia el DOM con frecuencia. Valida selectores antes de cada campaña y ten fallbacks.
  • Olvidar deduplicar: la recursión de PAA genera mucho solapamiento. Normaliza y deduplica antes de clustering.

Para casos de uso más amplios, revisa las guías de web scraping y SERP tracking.

Ética y consideraciones legales

Las sugerencias de Autocomplete y las preguntas de PAA son datos públicos visibles en cualquier navegador. Aun así, el scraping a escala tiene consideraciones éticas:

  • Throttlea educadamente: 2-5 segundos entre clics de PAA, concurrencia razonable en Autocomplete. No es una carrera, es una cosecha.
  • Respeta robots.txt: revisa google.com/robots.txt antes de automatizar.
  • Prefer APIs oficiales a escala: si vas a procesar decenas de miles de consultas diarias, valora la Custom Search API o herramientas de keyword research con licencia, aunque no cubran PAA.
  • Cumple ToS y GDPR/CCPA: no almacenes datos personales; las sugerencias agregadas no lo son, pero conviene revisar las condiciones de uso de Google.

El scraping de datos públicos para investigación de palabras clave es una práctica establecida en SEO, pero la sostenibilidad requiere moderación: mejor 1,000 consultas limpias que 50,000 que acaban en ban.

Puntos clave

  • Autocomplete, PAA y Related Searches cubren tres fases de intención: exploración, exploración del resultado y consulta vecina.
  • El endpoint suggestqueries.google.com/complete/search?client=chrome&q= devuelve JSON ligero ideal para concurrencia.
  • Los proxies residenciales con geo-targeting (país/ciudad) son necesarios para sugerencias localizadas y evitar rate limits por IP.
  • La expansión a-z + modificadores convierte una semilla en cientos de longtails; la recursión de PAA multiplica las preguntas.
  • Normaliza, deduplica y clusteriza por intención antes de exportar a CSV para contenido accionable.
  • Throttlea, respeta robots.txt y considera APIs oficiales a gran escala.

Conclusión y siguiente paso

Con ~200 líneas de Python tienes un pipeline completo de keyword research que combina las tres minas gratuitas de Google. El siguiente paso es integrarlo con tu stack de contenido: alimenta el CSV a tu herramienta de planificación editorial o a un generador de FAQs. Si necesitas escalar a múltiples mercados, rota países en ProxyHat y compara volúmenes de sugerencias por región para priorizar expansiones internacionales.

Preguntas frecuentes

¿Qué es scrapear People Also Ask y Autocomplete de Google para keyword research?

Es la práctica de extraer automáticamente las sugerencias de autocompletado de Google (endpoint suggestqueries.google.com), las preguntas del bloque People Also Ask del SERP y las Related Searches, para alimentar investigación de palabras clave a escala. Con Python y proxies residenciales se convierte una semilla en cientos de longtails y preguntas reales de usuarios, listas para planificar contenido y FAQs.

¿Por qué importa scrapear People Also Ask para usuarios de proxies?

Porque Google aplica rate limits por IP y personaliza sugerencias por geolocalización. Sin proxies residenciales con geo-targeting de país y ciudad, las sugerencias llegan sesgadas al país del datacenter y las llamadas rápidas provocan bloqueos 429. Los proxies residenciales ofrecen IPs limpias de ISPs reales y control de locale, imprescindibles para keyword research localizado y a escala.

¿Qué tipo de proxy funciona mejor para scrapear People Also Ask y Autocomplete?

Los proxies residenciales son la mejor opción: provienen de ISPs reales, tienen huellas limpias y soportan geo-targeting por país y ciudad (por ejemplo user-country-DE-city-berlin en ProxyHat). Los datacenter son más rápidos pero sus rangos IP están catalogados como no residenciales y Google los bloquea con más frecuencia. Para sesiones de navegador con Playwright, usa la flag session- para mantener la misma IP durante toda la sesión.

¿Cómo evitas bloqueos al scrapear People Also Ask y Autocomplete de Google?

Combina rotación de IPs residenciales, concurrencia limitada (50 sesiones concurrentes como máximo razonable), backoff exponencial ante 429, throttling de 2-5 segundos entre clics de PAA, sesiones estables en Playwright y geo-targeting consistente. Además, valida los selectores del DOM con frecuencia porque Google los cambia, y deduplica antes de clustering para no desperdiciar peticiones.

¿Es legal scrapear datos públicos de sugerencias de Google?

Las sugerencias de Autocomplete y las preguntas de PAA son datos públicos visibles en cualquier navegador, y el scraping para investigación de palabras clave es una práctica establecida en SEO. Conviene respetar robots.txt, cumplir los términos de servicio de Google, no almacenar datos personales y throttlear de forma educada. A gran escala, valora las APIs oficiales como Custom Search JSON API, aunque no cubran PAA ni Autocomplete.

¿Listo para empezar?

Proxies residenciales, ISP y móviles en más de 148 países. Crea una cuenta gratis.

Crear cuenta gratis
← Volver al Blog